Make sure you have Xcode installed from the Mac App Store as well.
To run the quiz for this assignment, cd into the folder and run the following command:
python3 op q 1
To do the coding questions, open up the coding.playground file in the folder. You need Xcode for this!
Replace the underlines with an expression that the question asks for. When you’re done, hover your mouse over the line numbers on the left and you should see a play button. On the line where it says Test.defaultTestSuite.run, hit the play button. That will run the code up to that point and you can check whether your answer is correct in the terminal.
An example is shown below:
When you’re done with the entire assignment, Wechat either Michael or I the screenshots of your part 1 & 2. The uploading feature still isn’t working since my site got hacked–rip.
]]>Make sure you have Xcode installed from the Mac App Store as well.
In this camp, the homework is split into two parts: a quiz and a coding question.
To run the quiz for this assignment, cd into the folder and run the following command:
python3 op q 1
Looks familiar, isn’t it? We’re going to continue using the Op system for the WWDC camp this year. Once you execute that command, you should be able to see questions pop up and you can answer them just like how you answered WWPD questions last year.
To do the coding questions, open up the coding.playground file in the folder. You need Xcode for this!
Replace the underlines with an expression that the question asks for. When you’re done, hover your mouse over the line numbers on the left and you should see a play button. On the line where it says Test.defaultTestSuite.run, hit the play button. That will run the code up to that point and you can check whether your answer is correct in the terminal.
An example is shown below:
When you’re done with the entire assignment, Wechat either Michael or I the screenshots of your part 1 & 2. The uploading feature still isn’t working since my site got hacked–rip.
]]>def reverse(lst): if len(lst) <= 1: return lst return reverse(lst[1:]) + [lst[0]] lst = [1, 2] rev = reverse(lst)
You’re done! Upload your screenshot here!
Screenshot for completion
Upload files






If you encounter any issues, please contact Leo, Eugene, or John through Wechat. Thanks!
]]>As in Hog Phase 1, we will be using the op
autograder again (and for all future assignments)! You will be able to get realtime feedback and work through your mistakes this way.
Lists are Python data structures that can store multiple values. Each value can be any type and can even be another list! A list is written as a commma separated list of expressions within square brackets:
>>> list_of_nums = [1, 2, 3, 4] >>> list_of_bools = [True, True, False, False] >>> nested_lists = [1, [2, 3], [4, [5]]]
Each element in a list is assigned an index. Lists are zeroindexed, meaning their indices start at 0
and increase in sequential order. To retrieve an element from a list, use list indexing:
>>> lst = [6, 5, 4, 3, 2, 1] >>> lst[0] 6 >>> lst[3] 3
Often times we need to know how long a list is when we’re working with it. To find the length of a list, call the function len
on it:
>>> len([]) 0 >>> len([2, 4, 6, 8, 10]) 5
Tip: Recall that empty lists, []
, are falsey values. Therefore, you can use an if statement like the following if you only want to do operations on nonempty lists:
if lst: # Do stuff with the elements of list
This is equivalent to:
if len(lst) > 0: # Do stuff
You can also create a copy of some portion of the list using list slicing. To slice a list, use this syntax: lst[<start index>:<end index>]
. This expression evaluates to a new list containing the elements of lst
starting at and including the element at <start index>
up to but not including the element at end index
.
>>> lst = [True, False, True, True, False] >>> lst[1:4] [False, True, True] >>> lst[:3] # Start index defaults to 0 [True, False, True] >>> lst[3:] # End index defaults to len(lst) + 1 [True, False] >>> lst[:] # Creates a copy of the whole list [True, False, True, True, False]
List comprehensions are a compact and powerful way of creating new lists out of sequences. The general syntax for a list comprehension is the following:
[<expression> for <element> in <sequence> if <conditional>]
The syntax is designed to read like English: “Compute the expression for each element in the sequence if the conditional is true for that element.”
Let’s see it in action:
>>> [i**2 for i in [1, 2, 3, 4] if i % 2 == 0] [4, 16]
Here, for each element i
in [1, 2, 3, 4]
that satisfies i % 2 == 0
, we evaluate the expression i**2
and insert the resulting values into a new list. In other words, this list comprehension will create a new list that contains the square of each of the even elements of the original list.
If we were to write this using a for statement, it would look like this:
>>> lst = [] >>> for i in [1, 2, 3, 4]: ... if i % 2 == 0: ... lst += [i**2] >>> lst [4, 16]
Note: The if
clause in a list comprehension is optional. For example, you can just say:
>>> [i**2 for i in [1, 2, 3, 4]] [1, 4, 9, 16]
Do the actual coding in the lab05.py
file with Atom! I’m only describing what the problems are below.
What Would Python Display? (WWPD) problems:
As mentioned previously, we will be using the op
autograder again. For these problems, type in what Python would display after “?” .
If a question will result in an error, put in the string “Error”.
If a question will result in None, put in None.
If a question will result in no output, put in “Nothing”.
If a question will result in a function, put in the string “Function”.
To distinguish between printed logs and outputted strings, add single quotes around your string anwers:
>>> def returns1(): ... return '1' ... >>> print(1) 1 >>> returns1() '1'
Use Op to test your knowledge with the following “What Would Python Display?” questions:
python3 op q 1 u
For all WWPD questions, type Function
if you believe the answer is <function...>
, Error
if it errors, and Nothing
if nothing is displayed.
>>> lst, hi = [1, 2, 3, 4], 'hello' >>> lst[1] ______ >>> hi[2] ______ >>> lst[1:3] # Put a space after each comma ______ >>> lst[0:4:1] # Put a space after each comma ______ >>> lst[0:4:2] # Put a space after each comma ______ >>> 2 in lst ______ >>> 5 in lst ______ >>> >>> 'hi' in hi ______ >>> 'hell' in hi ______ >>> hi[0:4] + str(lst[0]) ______ >>> for num in lst: ... print(num) ______ ______ ______ ______ >>> for letter in hi: ... print(letter) ______ ______ ______ ______ ______ >>> for i in range(1, 5): ... print(i) ______ ______ ______ ______ >>> for i in range(1,10,2): ... print(i) ______ ______ ______ ______ ______ >>> [0 for i in [1, 2, 3] if i > 2 ] ______ >>> [[e, e+1] for e in [1, 2, 3]] ______ >>> lst = [1, 3, 3] >>> pika = lst.append(7) >>> lst ______ >>> print(pika) ______ >>> pika = lst.insert(0, 0) >>> lst ______ >>> print(pika) ______ >>> pika = lst.remove(3) >>> lst ______ >>> print(pika) ______ >>> pika = lst.pop(2) >>> pika ______ >>> lst ______ >>> lst2 = [0,1,7] >>> lst2 == lst ______ >>> lst2 is lst ______ >>> lst3 = lst >>> lst3 == lst ______ >>> lst3 is lst ______ >>> d = {'a': 1, 'b': 2, 'c': 3} >>> d['a'] ______ >>> d[1] ______ >>> d['hi'] = 4 >>> d['hi'] ______ >>> d['hi'] = 'hi' >>> d['hi'] ______ >>> d[1] = 'hi' >>> d[1] ______ >>> d[1337] = [1, 2, 3] >>> d[1337].pop(0) ______
Free Response Questions (FRQs)
Q1. If this not that
def if_this_not_that(): """Define a function which takes a list of integers `i_list` and an integer `this`. For each element in `i_list`, print the element if it is larger than `this`; otherwise, print the word "that". >>> original_list = [1, 2, 3, 4, 5] >>> if_this_not_that(original_list, 3) that that that 4 5 """ "*** YOUR CODE HERE ***" # Your code goes here
def factors_list(): """Return a list containing all the numbers that divide `n` evenly, except for the number itself. Make sure the list is in ascending order. >>> factors_list(6) [1, 2, 3] >>> factors_list(8) [1, 2, 4] >>> factors_list(28) [1, 2, 4, 7, 14] """ all_factors = [] "*** YOUR CODE HERE ***"
You’re done! Upload your screenshot here!
Screenshot for completion
Upload files






If you encounter any issues, please contact Leo, Eugene, or John through Wechat. Thanks!
]]>
Upload files






Upload files






As in Hog Phase 1, we will be using the op
autograder again (and for all future assignments)! You will be able to get realtime feedback and work through your mistakes this way.
In Lecture 04, we learned about higherorder functions (HOF)! These are useful for many things such as generalization and recursion.
All functions’ parent frame is the one it was defined in.
def
statementReview: in Python, functions can be defined using the def
statement as following:
def foo(paramater): #Function signature: "def" keyword, function name, parameters <function body> #Function body: code you want to execute return <expression> #Return statement: returns value
In the lecture, we learned about a new type of function: lambda expressions!
Just like a def
function, a lambda function takes in parameters and returns some value.
Unlike a def
function, a lambda function is much more limited in that they can only return a single expression (no other statements). Also, lambda functions do not have names bound to them, unlike a def
function.
This means that lambda functions are generally much more limited in what they can do. Typically, lambda functions are used as inputs to HOFs (will elaborate on this later), when they are only needed for a short period of time, or when the function can be defined as a single expression.
Here are some examples:
>>> square = lambda x: x * x #x is the parameter, x * x is returned >>> square(3) 9 #3 * 3 = 9 >>> add_xy = lambda x, y: x + y #x, y are the parameters, x + y is returned >>> add_xy(2, 4) 2 #2 + (4) = 2
The following terms can be used to describe any function:
Here is an example using a function defined with def
:
def square(x): #Takes in any real numbers (domain) return x * x #Outputs the square, which is nonnegative and real (range)
Here is an example using a lambda function:
double = lambda x: x * 2 #Takes in any real numbers (domain) #Outputs the double of the input, which is also real (range)
Functions are firstclass, which means they can be manipulated as values.
This leads to the definition of higherorder function: a function that takes in a function as an input, or outputs a function.
Example 1: An example from Hog Phase 1!
#Example 1: An example from Hog Phase 1! def make_fair_dice(sides): #The higherorder function """Return a die that returns 1 to SIDES with equal chance.""" assert type(sides) == int and sides >= 1, 'Illegal value for sides' def dice(): #Note that the parent frame of dice() is make_fair_dice() return randint(1,sides) return dice #The higherorder function make_fair_dice() returns dice() four_sided = make_fair_dice(4) #four_sided() is also a function because make_fair_dice() returned a function
In this example, make_fair_dice()
is the HOF because it returns a function in line 7. Thus, four_sided = make_fair_dice(4)
(line 9) is an assignment statement that evaluates the call expression make_fair_dice(4)
, which returns a function, then binds it tofour_sided
. Now that four_sided
is function, it can be called with the call expression four_sided()
!
Environment diagram for make_fair_dice()
We can use higherorder functions to convert a function that takes multiple arguments into a chain of functions that each take a single argument. More specifically, given a function
https://composingprograms.com/pages/16higherorderfunctions.html#curryingf(x, y)
, we can define a functiong
such thatg(x)(y)
is equivalent tof(x, y)
. Here,g
is a higherorder function that takes in a single argumentx
and returns another function that takes in a single argumenty
. This transformation is called currying.
Example 2: curried adder (an example using lambdas)
#Example 2: curried adder (an example using lambdas) hof_adder = lambda x: lambda y: x + y #hof_adder() is a HOF because it returns a function adder_2 = hof_adder(2) #hof_adder() outputs a function value, so adder_2 gets binded to the lambda function that adds 2 to whatever is inputted into it adder_2(3) #Will output 3 + 2 = 1 adder_2(4) #Will output 4 + 2 = 6
In this example, hof_adder
is the HOF because it returns a lambda function. Thus, adder_2
gets binded to the adder function returned by hof_adder(2)
. Because adder_2()
is a function, it can be called on as shown in lines 4 and 5.
In this example, currying allows us to create adder functions without needing to create a specific function for each addition we wish to compute.
Environment diagram for hofadder
Example 3: curried pow (an example using def
)
def curried_pow(x): def h(y): return pow(x, y) #Abstraction! (assume pow() works as intended) return h curried_pow(2)(3) #Will output 2 ** 3 = 8
In this example, curried_pow()
is the HOF because it returns a function (h
). Thus, the call expression curried_pow(2)
returns a function that can be called on, which is why curried_pow(2)(3)
will output 2³ = 8.
In this example, currying allows us to create pow functions without needing to create a specific function for each power we wish to compute.
Environment diagram for curry_pow()
Example 4: double currying and uncurrying (advanced)
def curry2(f): """Return a curried version of the given twoargument function.""" def g(x): def h(y): return f(x, y) return h return g def uncurry2(g): """Return a twoargument version of the given curried function.""" def f(x, y): return g(x)(y) return f
The
https://composingprograms.com/pages/16higherorderfunctions.html#curryingcurry2
function takes in a twoargument functionf
and returns a singleargument functionf
. Wheng
is applied to an argumentx
, it returns a singleargument functionh
. Whenh
is applied toy
, it callsf(x, y)
. Thus,curry2(f)(x)(y)
is equivalent tof(x, y)
. Theuncurry2
function reverses the currying transformation, so thatuncurry2(curry2(f))
is equivalent tof
.
Generalization is a useful technique using HOF where you find common structures to allow for shared interpretation.
Example from the slide:
identity = lambda k: k cube = lambda k: pow(k, 3) def summation(n, term): """ Sum the first n terms of a sequence from 1 >>> summation(5, identity) 15 >>> summation(5, cube) 225 """ total, k = 0, 1 while k <= n: total, k = total + term(k), k + 1 return total
summation()
is a HOF because it takes in a function—term
.
This example demonstrations generalization because it can compute the summation of any sequence. This is achieved by having the parameter term
. term
determines what the nᵗʰ is before adding it to the total.
Function composition is the action of taking multiple simple functions and combining them into a more complex one.
It’s just like in math where we can take f(x) and g(x) to form f(g(x)) or g(f(x))!
But, in programming, something like f(g(x)) would just be a nested call expression. In order to save the composite function to create h(x), it would require making a third function.
Simple example first:
>>> def f(x): ... return 2*x ... >>> def g(x): ... return x**2 ... >>> f(g(2)) # Nested call expression 8 >>> g(f(2)) 16 >>> def compose(func1, func2): ... def h(x): # h(x) isn't executed yet! ... return func1(func2(x)) ... return h ... >>> h = compose(f,g) # compose(f, g) evaluates to the composite function >>> h(2) # same as f(g(2)) 8 >>> h = compose(g, f) # returns g(f(x)) >>> h(2) # same as g(f(2)) 16
Do the actual coding in the lab04.py
file with Atom! I’m only describing what the problems are below.
What Would Python Display? (WWPD) problems:
As mentioned previously, we will be using the op
autograder again. For these problems, type in what Python would display after “?” .
If a question will result in an error, put in the string “Error”.
If a question will result in None, put in None.
If a question will result in no output, put in “Nothing”.
If a question will result in a function, put in the string “Function”.
To distinguish between printed logs and outputted strings, add single quotes around your string anwers:
>>> def returns1(): ... return '1' ... >>> print(1) 1 >>> returns1() '1'
Use Op to test your knowledge with the following “What Would Python Display?” questions:
python3 op q 1 u
For all WWPD questions, type Function
if you believe the answer is <function...>
, Error
if it errors, and Nothing
if nothing is displayed.
>>> lambda x: x # A lambda expression with one parameter x ______ >>> a = lambda x: x # Assigning the lambda function to the name a >>> a(5) ______ >>> (lambda: 3)() # Using a lambda expression as an operator in a call exp. ______ >>> b = lambda x: lambda: x # Lambdas can return other lambdas! >>> c = b(88) >>> c ______ >>> c() ______ >>> d = lambda f: f(4) # They can have functions as arguments as well. >>> def square(x): ... return x * x >>> d(square) ______
>>> z = 3 >>> e = lambda x: lambda y: lambda: x + y + z >>> e(0)(1)() ______ >>> f = lambda z: x + z >>> f(3) ______
>>> higher_order_lambda = lambda f: lambda x: f(x) >>> g = lambda x: x * x >>> higher_order_lambda(2)(g) # Which argument belongs to which function call? ______ >>> higher_order_lambda(g)(2) ______ >>> call_thrice = lambda f: lambda x: f(f(f(x))) >>> call_thrice(lambda y: y + 1)(0) ______ >>> print_lambda = lambda z: print(z) # When is the return expression of a lambda expression executed? >>> print_lambda ______ >>> one_thousand = print_lambda(1000) ______ >>> one_thousand ______
Use Op to test your knowledge with the following “What Would Python Display?” questions:
python3 op q 2 u
For all WWPD questions, type Function
if you believe the answer is <function...>
, Error
if it errors, and Nothing
if nothing is displayed.
>>> def even(f): ... def odd(x): ... if x < 0: ... return f(x) ... return f(x) ... return odd >>> steven = lambda x: x >>> stewart = even(steven) >>> stewart ______ >>> stewart(61) ______ >>> stewart(4) ______
>>> def cake(): ... print('beets') ... def pie(): ... print('sweets') ... return 'cake' ... return pie >>> chocolate = cake() ______ >>> chocolate ______ >>> chocolate() ______ >>> more_chocolate, more_cake = chocolate(), cake ______ >>> more_chocolate ______ >>> def snake(x, y): ... if cake == more_cake: ... return lambda: x + y ... else: ... return x + y >>> snake(10, 20) ______ >>> snake(10, 20)() ______ >>> cake = 'cake' >>> snake(10, 20) ______
Free Response Questions (FRQs)
Q1 HoF Adder
Drawing the environmental diagram
Once you’re done with coding, draw an enviornmental diagram for when your def statement is executed and hof_adder(1)(2)
is executed.
Once you’re done drawing, put your def statement and hof_adder(1)(2)
into Python Tutor, visualize execution, and mark down what you got wrong. Submit a photo of that. Example of what you want to put into Python Tutor:
def _(): """ Write a function named hof_adder which takes in one integer argument x and returns a function named adder that takes in one integer argument y. The returned function adder returns x+y. For this question, you'll have to draw an environmental diagram as well. Read the post for instructions on how to do it. >>> adder_2 = hof_adder(2) >>> adder_2(3) 1 >>> adder_2(4) 6 >>> adder_3 = hof_adder(3) >>> adder_3(4) 7 """ # Your code goes here
Drawing the environmental diagram
For this question, basically do the same thing as the question above. Draw an environmental diagram of your def statement being executed and curried_pow(1)(2)
being executed.
Once you’re done, compare your result to what Python Tutor shows, and mark down anything you got wrong. Submit an image of that too.
def _(): """ Write a function named curried_pow which takes in one intege argument x and returns a lambda function that takes in one integer argument y. The returned lambda function returns x to the power of y. The autograder will check if you've used lambda or not! Don't use a def statement. For this question, you'll also draw an environmental diagram to submit. Look in the post for more instructions. >>> curried_pow(2)(3) 8 >>> curried_pow(2)(4) 16 >>> curried_pow(1)(100) 1 >>> curried_pow(0)(100) 0 >>> curried_pow(1234)(0) 1 """
You’re done! Upload your screenshot here!
Screenshot for coding & WWPD part’s completion
Upload files






Screenshot for FRQ1 Env Diagram Drawing:
Screenshot for FRQ2 Env Diagram Drawing:
If you encounter any issues, please contact Leo, Eugene, or John through Wechat. Thanks!
]]>Note: We recommend starting and finishing Phase 1 as soon as possible to give yourself adequate time to complete Phase 2, which is harder and more timeconsuming. You do not have to wait until after the checkpoint date to start Phase 2.
This project will require everything you have learned so far: functions, boolean expressions, conditional statements, while/for loops, and HOFs (HOFs will be taught in week 6)!
In Hog, two players alternate turns trying to be the first to end a turn with at least 100 total points. On each turn, the current player chooses some number of dice to roll, up to 10. That player’s score for the turn is the sum of the dice outcomes.
To spice up the game, we will play with some special rules:
To get started, download all of the project code as a zip archive. You only have to make changes to hog.py
.
hog.py
: A starter implementation of Hogdice.py
: Functions for rolling dicehog_gui.py
: A graphical user interface for Hogop_resources.py
: A directory of tests used by op
op
: CSS autograderhog_log.txt
: Resources used by the op
autograderucb.py
: Utility functions for running hog_gui.py
images
: A directory of images used by hog_gui.py
You will have until Sunday, October 13 to finish the entire project. It is divided into two phases:
For the functions that we ask you to complete, there may be some initial code that we provide. If you would rather not use that code, feel free to delete it and start from scratch. You may also add new function definitions as you see fit.
However, please do not modify any other functions. Doing so may result in your code failing our autograder tests. Also, please do not change any function signatures (names, argument order, or number of arguments).
Throughout this project, you should be testing the correctness of your code. It is good practice to test often, so that it is easy to isolate any problems. However, you should not be testing too often, to allow yourself time to think through problems.
op
We have provided an autograder called op
to help you with testing your code and tracking your progress.
The primary purpose of op
is to test your implementations, but there are two things you should be aware of.
Before coding, check your understanding of the questions with WWPD. To run WWPD, run the following command from your terminal:
python3 op q [Question Number] u
This command will start an interactive prompt that looks like:
running wwpd
Type the answer in at the end of each "?"
>>> <Question here>
?
At the ?
, you can type what you expect the output to be. If you are correct, then this test case will be available the next time you run the autograder.
The idea is to understand conceptually what your program should do first, before you start writing any code.
Once you have finished WWPD and coding, you can check the correctness of your program using the tests:
python3 op q [Question #]
Most of the time, you will want to focus on a particular question. Use the q
option as directed in the problems below.
We recommend that you keep a backup after you finish each problem.
Finally, there is one last command implemented in the op
autograder. You can run the following code at the end of each Phase to check for completion:
python3 op p [Phase #]
A graphical user interface (GUI, for short) is provided for you. At the moment, it doesn’t work because you haven’t implemented the game logic. Once you complete the play
function, you will be able to play a fully interactive version of Hog!
In order to render the graphics, make sure you have Tkinter, Python’s main graphics library, installed on your computer. Once you’ve done that, you can run the GUI from your terminal:
python3 hog_gui.py
Important submission note: For full credit:
All Phase 1 WWPD must be correct in order to progress forward.
In the first phase, you will develop a simulator for the game of Hog.
The dice.py
file represents dice using nonpure zeroargument functions. These functions are nonpure because they may have different return values each time they are called. The documentation of dice.py
describes the two different types of dice used in the project:
six_sided
.make_test_dice
function.Before we start writing any code, read over the dice.py
file and check your understanding by running the following WWPD:
python3 op q 0 u
This should display a prompt that looks like this:
running wwpd
>>> from hog import *
>>> test_dice = make_test_dice(4, 1, 2)
>>> test_dice()
?
You should type in what you expect the output to be. To do so, you need to first figure out what test_dice
will do, based on the description above.
You can exit the WWPD by typing ctrl+z
. NOT Command+z!
Implement the roll_dice
function in hog.py
. It takes two arguments: a positive integer called num_rolls
giving the number of dice to roll and a dice
function. It returns the number of points scored by rolling the dice that number of times in a turn: either the sum of the outcomes or 1 (Pig Out).
To obtain a single outcome of a dice roll, call dice()
. You should call dice()
exactly num_rolls
times in the body of roll_dice
. Remember to call dice()
exactly num_rolls
times even if Pig Out happens in the middle of rolling. In this way, we correctly simulate rolling all the dice together.
Understand the problem:
Before writing any code, use the WWPD to verify your understanding of the question.
python3 op q 1 u
Write code and check your work:
Once you are done with WWPD, begin implementing your solution. You can check your correctness with:
python3 op q 1
Debugging your code interactively:
If the tests don’t pass, it’s time to debug. You can observe the behavior of your function using Python directly. First, start the Python interpreter and load the hog.py
file.
python3 i hog.py
Then, you can call your roll_dice
function on any number of dice you want. The roll_dice
function has a default argument value for dice
that is a random sixsided dice function. Therefore, the following call to roll_dice
simulates rolling four fair sixsided dice.
>>> roll_dice(4)
You will find that the previous expression may have a different result each time you call it, since it is simulating random dice rolls. You can also use test dice that fix the outcomes of the dice in advance. For example, rolling twice when you know that the dice will come up 3 and 4 should give a total outcome of 7.
>>> fixed_dice = make_test_dice(3, 4)
>>> roll_dice(2, dice=fixed_dice)
7
On most systems, you can evaluate the same expression again by pressing the up arrow or ControlP, then pressing enter or return.
If you find a problem, you need to change your
hog.py
file, save it, quit Python, start it again, and then start evaluating expressions. Pressing the up arrow should give you access to your previous expressions, even after restarting Python.
Continue debugging your code and running the ok
tests until they all pass. You should follow this same procedure of understanding the problem, implementing a solution, testing, and debugging for all the problems on this project.
Implement the free_bacon
helper function that returns the number of points scored by rolling 0 dice, based on the opponent’s current score
. You can assume that score
is less than 100. For a score less than 10, assume that the first of the two digits is 0.
Before writing any code, use the WWPD to verify your understanding of the question.
python3 op q 2 u
Once you are done with WWPD, begin implementing your solution. You can check your correctness with:
python3 op q 2
As noted above, you can also test free_bacon
interactively by entering python3 i hog.py
in the terminal and then calling free_bacon
with various inputs.
Implement the take_turn
function, which returns the number of points scored for a turn by rolling the given dice
num_rolls
times.
You will need to implement the Free Bacon rule based on opponent_score
, which you can assume is less than 100.
Your implementation of take_turn
should call both roll_dice
and free_bacon
when possible.
Before writing any code, use the WWPD to verify your understanding of the question.
python3 op q 3 u
Once you are done with WWPD, begin implementing your solution. You can check your correctness with:
python3 op q 3
Implement is_swap
, which returns whether or not the scores should be swapped because the ones digit of the current player’s score is the same as the tens digit of the opponent’s score.
The is_swap
function takes two arguments: the players’ scores. It returns a boolean value to indicate whether the Swine Swap condition is met.
Before writing any code, use the WWPD to verify your understanding of the question.
python3 op q 4 u
Once you are done with WWPD, begin implementing your solution. You can check your correctness with:
python3 op q 4
Implement the play
function, which simulates a full game of Hog. Players alternate turns rolling dice until one of the players reaches the goal
score.
To determine how much dice are rolled each turn, each player uses their respective strategy (Player 0 uses strategy0
and Player 1 uses strategy1
). A strategy is a function that, given a player’s score and their opponent’s score, returns the number of dice that the current player wants to roll in the turn. Each strategy function should be called only once per turn. Don’t worry about the details of implementing strategies yet. You will develop them in Phase 2.
When the game ends, play
returns the final total scores of both players, with Player 0’s score first, and Player 1’s score second.
Here are some hints:
take_turn
with all three arguments.take_turn
once per turn.other
.say
argument to the play
function for now. You will use it in Phase 2 of the project.Before writing any code, use the WWPD to verify your understanding of the question.
python3 op q 5 u
Once you are done with WWPD, begin implementing your solution. You can check your correctness with:
python3 op q 5
The last test for Question 5 is a fuzz test, which checks that your
play
function works for a large number of different inputs. Failing this test means something is wrong, but you should look at other tests to see where the problem might be.
Once you are finished, activate the following code segment right below #END PROBLEM 5:
say = say(score0,score1)
After you have done this, you will be able to play a graphical version of the game. We have provided a file called hog_gui.py
that you can run from the terminal:
python3 hog_gui.py
If you don’t already have Tkinter (Python’s graphics library) installed, you’ll need to install it first before you can run the GUI.
The GUI relies on your implementation, so if you have any bugs in your code, they will be reflected in the GUI. This means you can also use the GUI as a debugging tool; however, it’s better to run the tests first.
Congratulations! You have finished Phase 1 of this project! You may use the following command in the terminal to verify that Phase 1 is complete:
python3 op p 1
Upload your Phase 1 screenshot here!
Upload files






In the second phase, you will experiment with ways to improve upon the basic strategy of always rolling a fixed number of dice. First, you need to develop some tools to evaluate strategies.
Implement the make_averaged
function, which is a higherorder function that takes a function fn
as an argument. It returns another function that takes the same number of arguments as fn
(the function originally passed into make_averaged
). This returned function differs from the input function in that it returns the average value of repeatedly calling fn
on the same arguments. This function should call fn
a total of num_samples
times and return the average of the results.
To implement this function, you need a new piece of Python syntax! You must write a function that accepts an arbitrary number of arguments, then calls another function using exactly those arguments. Here’s how it works.
Instead of listing formal parameters for a function, we write
*args
. To call another function using exactly those arguments, we call it again with*args
. For example,
>>> def printed(fn):
... def print_and_return(*args):
... result = fn(*args)
... print('Result:', result)
... return result
... return print_and_return
>>> printed_pow = printed(pow)
>>> printed_pow(2, 8) Result: 256 256
>>. printed_abs = printed(abs) >>> printed_abs(10) Result: 10 10
Read the docstring for make_averaged
carefully to understand how it is meant to work.
Before writing any code, use the WWPD to verify your understanding of the question.
python3 op q 6 u
Once you are done with WWPD, begin implementing your solution. You can check your correctness with:
python3 op q 6
Implement the max_scoring_num_rolls
function, which runs an experiment to determine the number of rolls (from 1 to 10) that gives the maximum average score for a turn. Your implementation should use make_averaged
and roll_dice
.
If two numbers of rolls are tied for the maximum average score, return the lower number. For example, if both 3 and 6 achieve a maximum average score, return 3.
Before writing any code, use the WWPD to verify your understanding of the question.
python3 op q 7 u
Once you are done with WWPD, begin implementing your solution. You can check your correctness with:
python3 op q 7
To run this experiment on randomized dice, call run_experiments
using the r
option:
python3 hog.py r
Running experiments For the remainder of this project, you can change the implementation of run_experiments
as you wish. By calling average_win_rate
, you can evaluate various Hog strategies. For example, change the first if False:
to if True:
in order to evaluate always_roll(8)
against the baseline strategy of always_roll(4)
. You should find that it wins slightly more often than it loses, giving a win rate around 0.5.
Some of the experiments may take up to a minute to run. You can always reduce the number of samples in make_averaged
to speed up experiments.
A strategy can take advantage of the Free Bacon rule by rolling 0 when it is most beneficial to do so. Implement bacon_strategy
, which returns 0 whenever rolling 0 would give at least margin
points and returns num_rolls
otherwise.
Before writing any code, use the WWPD to verify your understanding of the question.
python3 op q 8 u
Once you are done with WWPD, begin implementing your solution. You can check your correctness with:
python3 op q 8
Once you have implemented this strategy, change run_experiments
to evaluate your new strategy against the baseline. You should find that it wins more than half of the time.
A strategy can also take advantage of the Swine Swap rule. The swap_strategy
rolls 0 if it would cause a beneficial swap. It also returns 0 if rolling 0 would give at least margin
points, even if this would cause a nonbeneficial swap. Otherwise, the strategy rolls num_rolls
.
Before writing any code, use the WWPD to verify your understanding of the question.
python3 op q 9 u
Once you are done with WWPD, begin implementing your solution. You can check your correctness with:
python3 op q 9
Once you have implemented this strategy, update run_experiments
to evaluate your new strategy against the baseline. You should find that it gives a significant edge over always_roll(4)
Finally, you can use the following code segment to verify that Phase 2 is complete:
python3 op p 2
Congratulations, you have reached the end of your first project! If you haven’t already, relax and enjoy a few games of Hog with a friend.
Upload your screenshot for Phase 2 here!
Upload files






If you encounter any issues, please contact Leo, Eugene, or John through Wechat. Thanks!
]]>New lab format!
We’ve improved the WWPD part of the lab. Now, you can get real time feedback.
To run the WWPD part of the lab, use the following command in the terminal:
python3 wwpd.py
Then, write what Python would display, and hit enter to check your answer:
If it’s incorrect, the terminal shows Incorrect
. Keep trying until you get it right!
But, to check your FRQ questions, please still use:
python3 lab03.py
If the FRQ questions all pass the tests and you’ve completed all your WWPD questions, the terminal will output that you’ve completed everything. Screenshot that and upload it.
Review + Some extras
In Lecture 03, we learned a new datatype: None!
1. None, pure, and nonpure functions
Remember that None just means that nothing is returned; it is not displayed by the interpreter either.
Here are some examples:
>>> None >>> print(None) None >>> not None True >>> if None: ... print(1) ... else: ... print(2) ... 2
Next, we learned about pure functions and nonpure functions.
The difference between print and return is that printing is a sideeffect of the print() function; it actually returns None. return, on the other hand, allows values to be passed into and output out of functions.
Here are some examples:
>>> def square(x): ... return x**2 #Pure function because it returns a value ... >>> square(2) 4 #The return value >>> x = print(2) #print() is a nonpure function 2 #print(2) first gets evaluated so 2 gets displayed as output. This is NOT the return value; it is just a side effect of running print() >>> print(x) None #Now we can see that print(2) evaluated to None, then binded to x because it was a nonpure function
2. “Falsey” values, “Truthy” values, boolean expressions, and conditional statements
In Python, there are values that evaluate to False—”Falsey” values—and values that evaluate to Truth—”Truthy” values.
There will be more to come, but for now, just know the following:
Here are some examples:
>>> bool(0) #bool() gives the boolean context of the input False >>> bool("") False >>> bool(1) True >>> bool(True) True >>> bool("CSS @ SAS") True
There are also many boolean expressions:
Also, remember shortcircuiting—when an expression returns the value and stops evaluating latter expressions.
Here are some examples:
>>> True and True and True True >>> True and True and False False >>> True and False and 1 / 0 False #This actually doesn't error because it stops being evaluated at the first False value! >>> False or False or False False >>> "Hello" or False or 1 / 0 True #This also doesn't error because it stops being evaluated at the first True value—"Hello" in this case! >>> not(0) True #0 holds a "Falsey" value >>> not(1) False #1 holds a "Truthy" value
Also remember these can be used in combination with conditional statements if, elif, and else:
""" if <conditional expression>: <suite of statements> elif <conditional expression>: <suite of statements> else: <suite of statements> Always starts with if clause Zero or more elif clauses Zero or one else clause, always at the end """ >>> if 0: #False ... print(1) ... elif not(True): #False ... print(2) ... elif "Yo": #True so stops here ... print(3) ... else: #Never gets evaluated because it stops in the preceding elif ... print(1 / 0) ... 3
3. Iteration
We use iteration when we want to repeat a process.
Here are some examples:
>>> i = 0 >>> while i < 5: #The stop condition ... print(i) ... i = i + 1 #Increments i towards 5, the stop condition ... 0 1 2 3 4 >>> for i in range(0, 5): #Iterates from 0 (inclusive) to 5 (exclusive) ... print(i) ... 0 1 2 3 4
Do the actual coding in the lab03.py
file with Atom! I’m only describing what the problems are below.
What Would Python Display? (WWPD) problems:
Remember that all the variables defined in the fill in the blank sections below are in the same frame! So, for instance, Q2 might use a variable defined in Q1.
If a question will result in an error, put in the string “Error”.
If a question will result in None, put in None.
If a question will result in no output, put in “Nothing”.
— Section 1 —
#Q1. #print(print(1)) #Q2a. #True and 1 / 0 and False #Q2b. #True or 1 / 0 or False #Q2c. #True and 0 #Q2d. #False or 1 #Q2e. #1 and 3 and 6 and 10 and 15 #Q2f. #0 or False or 2 or 1 / 0 #Q3 def how_big(x): if x > 10: print('huge') elif x > 5: return 'big' elif x > 0: print('small') else: print("nothin'") #Q3a. how_big(7) #Q3b. how_big(12) #Q3c. how_big(1) #Q3d. how_big(1)
Free Response Questions (FRQs)
#Q1 """Using the following definitions of if_function() and with_if_statement(), complete the YOUR CODE HERE segments to satisfy the doctests in if_function() and with_if_statement(). """ def if_function(condition, true_result, false_result): """Return true_result if condition is a true value, and false_result otherwise. >>> if_function(True, 2, 3) 2 >>> if_function(False, 2, 3) 3 >>> if_function(3==2, 3+2, 32) 1 >>> if_function(3>2, 3+2, 32) 5 """ if condition: return true_result else: return false_result def with_if_statement(): """ >>> result = with_if_statement() 2 >>> print(result) None """ if c(): return t() else: return f() def with_if_function(): """ >>> result = with_if_function() 1 2 >>> print(result) None """ return if_function(c(), t(), f()) def c(): "YOUR CODE HERE" def t(): "YOUR CODE HERE" def f(): "YOUR CODE HERE" #Q2 def a_plus_abs_b(a, b): """Return a+abs(b), but without calling abs. >>> a_plus_abs_b(2, 3) 5 >>> a_plus_abs_b(2, 3) 5 """ "YOUR CODE HERE" #Q3 def largest_factor(n): """Return the largest factor of n that is smaller than n. >>> largest_factor(15) # factors are 1, 3, 5 5 >>> largest_factor(80) # factors are 1, 2, 4, 5, 8, 10, 16, 20, 40 40 >>> largest_factor(13) # factor is 1 since 13 is prime 1 """ "YOUR CODE HERE"
You’re done! Upload your screenshot here!
Upload files






If you encounter any issues, please contact Leo, Eugene, or John through Wechat. Thanks!
]]>The Problem
def ________(): """Write a function named transform_name that modifies a person’s name (a string). For names that are less than seven characters long (you can input a string into the in len() function to evaluate the number of characters it contains) , add characters using the following rules until there are seven characters in the string: 1. If there is currently an odd number of characters, add “@“ to the beginning 2. If there is currently an even number of characters, add “%” to the end Note that once a character is added to a string with an odd number length, it will have an even length, and vice versa. If the length is still less than seven it should continue as if it were even or odd respectively. When the characters have been added, or if the name was already seven or more characters long, replace all instances ‘E’ and ‘e’ in the string with ‘3’ (remember that ‘E’ and ‘e’ are distinct characters). Then return the string. >>> transform_name('CSS') '@@CSS%%' >>> transform_name('Eugene Lee') '3ug3n3 L33' """ ## Your code goes here ##
Write a function named transform_name that modifies a person’s name (a string). For names that are less than seven characters long (you can input a string into the in len() function to evaluate the number of characters it contains), add characters using the following rules until there are seven characters in the string:
Note that once a character is added to a string with an odd number length, it will have an even length, and vice versa. If the length is still less than seven it should continue as if it were even or odd respectively.
When the characters have been added, or if the name was already seven or more characters long, replace all instances ‘E’ and ‘e’ in the string with ‘3’ (remember that ‘E’ and ‘e’ are distinct characters). Then return the string.
The built in len() function in Python takes in a single string as its parameter and evaluates to an int value representing the number of characters in the string.
Example: len(“games”) returns 5.
Play around with this in your interactive Python interpreter!
You’ll also need to use subscripts:
>>> mystring = 'hello world'
>>> mystring[0]
'h'
>>> mystring[1]
'e'
>>> mystring[2]
'l'
>>> mystring[3]
'l'
>>> mystring[4]
'o'
Use brackets with an index inside [some index]
to access individual characters inside a string. Note that the index of the first character is 0, meaning that the last character’s index is len(some string here)  1
Upload your screenshot here!
Upload files





