Problem Set 3 - Due Tue, Feb 21 at 23:59*
*The deadline is Tue. because of the Mon. Feb 20 holiday
Reading
- Slides and notebooks from Lec 04 Divide/Conquer/Glue with Pictures and Lec 05 Booleans and Conditionals. You do not need (in fact, should not use material from) Lec 6 Sequences and Loops in this problem set.
- Problems and solutions from Lab 04 Conditionals, Pictures, DC&G
- Think Python, Sections 5.1 -- 5.7
About this Problem Set
This problem set is intended to give you practice with conditionals and the Divide, Conquer, and Glue (DCG) problem-solving strategy.
- In Task 1, you will write functions using conditionals to create a rock-paper-scissors game.
- In Task 2 (Partner task), you will create quilt patterns using functions. Use this shared Google Doc to find a pair programming partner and record who your pair partner is. Remember, you can talk with other individuals and teams about high-level problem-solving strategies, but you cannot share any code with them.
- The CS111 Problem Set Guide gives an overview of psets, including a detailed description of individual and partner tasks.
- Follow the style guidelines in the CS 111 Code Style Guide
- The PS03 FAQ covers the most common issues students encounter when working on PS03. Consult it before asking in the Google Group.
- In Fall 2016, students spent in average 2.3 hours on Task 1 (min = 0.8 hours, max = 6 hours) and 3.7 hours on Task 2 (min = 1 hours, max = 8 hours).
All code for this assignment is available in the ps03
folder in
the cs111/download
directory within your cs
server account.
This assignment also uses the Otter Inspector program that you used in PS2
to help you do a final check of Task 1 and your Honor Code form before you submit.
Task 1: Rock, Paper, Scissors
This is an individual problem which you must complete on your own, though you may ask for help from the CS111 staff.
In this problem, you will define functions to play the game rock-paper-scissors. This is a game in which both you and your opponent each choose one of the three hand gestures rock, paper, or scissors, and the winner is chosen according to the following diagram:

Your task is to define the following five functions in the provided file named rockPaperScissors.py
:
def isValidGesture(gesture):
"""Returns True if gesture is one of the strings 'rock', 'paper', or
'scissors', and False otherwise."""
# Flesh out the body of this function
def randomGesture():
"""Randomly returns one of 'rock', 'paper', or 'scissors',
with equal probability."""
# Flesh out the body of this function
def beats(gesture1, gesture2):
"""Returns True if the first gesture beats the second gesture, i.e.,
if the first and second gesture are rock/scissors or scissors/paper,
or paper/rock, respectively. Returns False otherwise.
The output is unspecified if one or both gestures is invalid."""
# Flesh out the body of this function
def play(yourGesture,opponentGesture):
"""Plays rock/paper/scissors game with your gesture vs. opponent's
gesture. If both gestures are valid, displays one of 'Game is
a tie!', 'You win!', or 'Opponent wins!'. Otherwise, indicates
the first gesture that is invalid."""
# Flesh out the rest of the body of this function
def playComputer(yourGesture):
"""Plays rock/paper/scissors with your gesture against a computer
opponent that randomly chooses a gesture. First displays the choice
of the computer, and then displays the result of the game."""
# Flesh out the rest of the body of this function
To test the functions, run the program in the Canopy editor window. No output will be produced, since none of the functions are called. Enter sample calls of your functions in the interactive console to test that they work correctly. Here are some examples:
Sample Output
In []: isValidGesture('paper')
Out[]: True
In []: isValidGesture('spock')
Out[]: False
In []: randomGesture()
Out[]: 'scissors' # You might get a different result because of randomness
In []: randomGesture()
Out[]: 'rock' # You might get a different result because of randomness
In []: randomGesture()
Out[]: 'scissors' # You might get a different result because of randomness
In []: beats('paper', 'rock')
Out[]: True
In []: beats('scissors', 'rock')
Out[]: False
In []: beats('rock', 'rock')
Out[]: False
In []: play('rock', 'paper')
Opponent wins!
In []: play('rock', 'scissors')
You win!
In []: play('rock', 'rock')
Game is a tie!
In []: play('rook', 'scissors')
Your gesture (rook) is invalid
In []: play('rock', 'sissors')
Opponent's gesture (sissors) is invalid
In []: play('spock', 'sissors')
Your gesture (spock) is invalid
In []: playComputer('paper')
Computer chooses rock
You win!
# You might get different printout because of randomness
In []: playComputer('paper')
Computer chooses paper
Game is a tie!
# You might get different printout because of randomness
In []: playComputer('paper')
Computer chooses paper
Game is a tie!
# You might get different printout because of randomness
In []: playComputer('paper')
Computer chooses scissors
Opponent wins!
# You might get different printout because of randomness
In []: playComputer('rock')
Computer chooses paper
Opponent wins!
# You might get different printout because of randomness
Notes
-
The Python function call
random.randint(a, b)
(wherea
andb
are integers anda
≤b
) returns a randomly chosen integer in the range [a
,b
] (inclusive). For example,random.randint(1, 10)
returns one of the integers between 1 and 10 with equal probability. The filerockPaperScissors.py
begins with animport random
declaration so that therandom.randint
from therandom
module can be use in this program. -
An important computer science principle is Don't Repeat Yourself (DRY). In this problem, you should avoid repeating yourself by using some of the functions you define as helper functions in other functions. For example, you should call the
beats
function within theplay
function rather than repeating the logic of which gesture beats which other gestures within theplay
function. -
Once you're satisifed with testing all your functions within Canopy, open the
otterInspect.py
file in yourps03
folder. You needn't look at the content of this file (and you must not modify it); just Run it using the green play button on Canopy. It should bring up a web page that does some tests on your five functions. If it reports errors, there are problems in your code you need to fix. This is a good way to check for any unexpected errors before you submit your code. We will use the Otter Inspector to grade your code.Notice that the page will also tell you that your Honor Code form values are missing. Ignore it for now; you will fill out the honor code in Task 3.
On some web browsers, Otter Inspector will open a new tab each time you run it. To avoid confusion, close the web page that it opens after you have looked at it.
- For fun, check out the variant game rock-paper-scissors-lizard-Spock. Consider implementing this as an ungraded challenge problem. (But don't change your exisiting
rockPaperScissors.py
file; make a new file with a different name if you decide to pursue this.)

Task 2: Quilts
This task is a partner problem in which you are required to work with a partner as part of a two-person team.
The CS111 Etsy shop wants to expand the items offered on their site.
Quilts would be an item that could generate a lot of revenue.
Below is an example of one of the CS111 Etsy quilt designs, which we will call quilt
.

You and several other CS111 students have been hired as interns at the CS111 Etsy shop to help design quilts. Your project is to use pictures to generate the quilt design shown above.
This quilt is just one sample of the quilt design. In your code, you will write functions that will take color parameters, and therefore can generate lots of different quilts, in the same pattern as above, but with color variation.
The picture.py
module, which contains picture functions you have
learned about in Lecture 04
and Lab 04, is provided in the ps03 folder. You
may use the functions defined in that module to help create your
quilts. All the functions that you will define for this problem will
be in a file you create called ps03Quilt.py
.
Your goal is to write a quilt
function that takes several color parameters
and returns a picture corresponding to the quilt shown above when
quilt
is invoked with appropriate colors. This picture is ultimately
generated by combining primitive pictures created by the following
two functions that you have already seen in class. They can be found in
the picture.py
file:
patch (c):
"""Returns a rectangular patch of color c with a
black border that fills a given picture frame.
"""
triangles(c1,c2):
"""Returns a picture that consists of two triangles filling the
given picture frame: a black-bordered triangle of color c1
in the lower left corner of the frame; and a black-bordered
triangle of color c2 in the upper right corner of the frame.
"""
For example, below are the pictures generated by some sample invocations of these functions:
![]() |
![]() |
|
---|---|---|
patch('red') |
triangles('red', 'blue') |
Divide, Conquer, and Glue
The key to solving the problem of defining the quilt
function is to note that the picture
it returns can be decomposed into smaller pictures that are used more than once in the larger picture.
For example, by wishful thinking we can image that the upper right quadrant is a picture
returned by a function we'll call block
:
![]() |
---|
block('darkseagreen','lightcyan2','royalblue', |
'navy','lightskyblue','darkslateblue') |
![]() |
The whole quilt
picture can be decomposed into four copies of the picture returned by block
that
have different rotations. Once we figure out how to define the block
function, we can an combine four rotated copies of the block
picture to form the
desired picture for quilt
. This is an excellent illustration of the
divide, conquer, and glue problem solving strategy we will use
throughout this course:
-
Divide the problem into subproblems. Here, the whole
quilt
problem can be solved if we can solve the problem of generating the picture in its upper right quadrant. -
Conquer the subproblems by solving them. In this case, the solution to the subproblem is a function named
block
that generates the picture in the upper right quadrant of thequilt
picture. - Glue the solutions to the subproblems together to form the solution to the whole problem.
Here, we combine four rotated versions of the picture returned by
block
to construct the picture returned byquilt
.
But how do we solve the problem of defining the block
function?
By applying the divide, conquer, and glue strategy again! In this case,
you should decompose the picture returned by block
itself into four quadrants.
You can then define functions quadrant1
, quadrant2
, etc. that return
pictures for these quadrants. Of course, all of these functions will take appropriate
color parameters.
Continue this way, decomposing each problem into smaller subproblems,
until you reach problems that can be solved using our primitives
(e.g., patch
and triangles
). For an example of this sort of
decomposition, see the quilt example from lecture 04.
Starting the Assignment
Begin by creating a file named ps03Quilt.py
. Remember to include the line:
from picture import *
at the top of your file, so that all of the functions in the picture.py
file are available to you.
Then use incremental programming to make progress on your quilt. That is, make approximations
to your quilt picture or parts of your quilt picture and use displayPic
to display
these pictures along the way.
You can work in one of two directions:
-
In the bottom-up strategy, you start with smaller helper functions, like those defined in the Helper Functions section below, and build up pictures of increasing complexity until you have correctly defined the full quilt picture.
- In the top-down strategy, you start by defining the
quilt
function in terms of theblock
function. But theblock
function is itself complex, so you can't really test it yet. So for testing purposes, you instead provide a so-called stub picture for the block function. For example, as a very crude approximation, you might define an initial version of theblock
function that returns the result of callingtriangles
on two of its parameters. You can then testquilt
with this approximation toblock
. Then you continue the top-down descent by defining initially-crude-approximations for functions that return the quadrants of theblock
picture. You continue this top down process until you reach pictures that are correctly implemented bypatch
andtriangles
, at which point you now have the correctquilt
function rather than just an approximation.
Helper Functions
As mentioned in Task 1, a general principle of computer science is Don't Repeat Yourself (DRY), which means you should avoid writing the same pattern of code more than once. If you find yourself writing the same or similar code more than once in your program, you should write functions that capture the patterns of the repeated code and invoke the functions instead.
The divide, conquer, and glue process of defining quilt
naturally
exposes the need for numerous auxiliary functions. As part of working
on this task, you must define and use the following helper functions:
def patch2x2(c):
"""Returns a picture consisting of four rectangular patches of color c
with black lines between the patches. (Remember that patch(c) returns
a picture with a black border.)"""
def triangles2x2(c1, c2):
"""Returns a 2x2 picture similar to triangles(c1, c2) except that each
large triangle is composed out of three smaller fragments (two
triangles and one square patch)."""
def bandana(c1, c2):
"""Returns a 2x2 picture with solid c1-colored patches in the lower left
and upper right quadrants, and triangles(c1, c2) in the upper left
and lower right quadrants."""
def corner(p1, p2):
"""Returns a picture which divides the picture space into four
quadrants and places p1 in the lower left quadrant and p2 in the
remaining three quadrants."""
def stripe(p1, p2, p3):
"""Returns a picture which divides the picture space into four
quadrants and places p1 in the lower left quadrant, p2 in upper left
and lower right quadrants, and p3 in the upper right quadrant."""
def lowerLeft(p):
"""Returns a picture which divides the picture space into four
quadrants and places the given picture in the lower left quadrant.
Nothing is in the other quadrants."""
def lowerLeftNest(p1, p2):
"""Returns a picture in which picture p2 is placed in the the lower left
quadrant of picture p1."""
def lowerLeftNestSelf4(p):
"""Returns a picture that has four copies of picture p: one full size one,
and three successively smaller versions descending in the
lower left quadrant."""
Here are example invocations of patch2x2
, triangles2x2
, and bandana
:
![]() |
![]() |
![]() |
||
---|---|---|---|---|
patch2x2('red') |
triangles2x2('red', 'blue') |
bandana('red', 'blue') |
Note that the corner
, stripe
, lowerLeft
, lowerLeftNest
, and lowerLeftNestSelf4
functions take pictures as arguments, not colors. To illustrate these helper functions, suppose that pictures picP1
, picP2
, and picP3
are defined as:
![]() |
![]() |
![]() |
||
---|---|---|---|---|
picP1 |
picP2 |
picP3 |
Then here are examples of calling the corner
, stripe
, lowerLeft
, lowerLeftNest
, and lowerLeftNestSelf4
functions with these pictures:
![]() |
![]() |
|||
---|---|---|---|---|
corner(picP1, picP2) |
stripe(picP1, picP2, picp3) |
|||
![]() |
![]() |
![]() |
||
lowerLeft(picP1) |
lowerLeftNest(picP1, picP2) |
lowerLeftNestSelf4(picP2) |
Notes:
-
Defining the above helper functions and using them in your quilt solution is required and not optional.
-
You can use
lowerLeftNestSelf4
in conjunction withtriangles
to create one of the patterns that appears in the quilt:lowerLeftNestSelf4(triangles('darkslateblue', 'navy'))
- Stepping back, your high-level goal in this problem is not only to generate the correct quilt picture, but to do so in a way that takes advantage of the power of abstraction to eliminate unnecessary repeated patterns in your code to make it as simple and elegant as possible. You want solution code that has the same level of abstraction and elegance as the code in slide 32 for the Lec 04.
Testing
As you implement the above required helper functions and your own helper functions, you'll want to test them!
You should put the following testing code at the end of ps03Quilt.py
:
# ------------------------- Testing -----------------------
def blueQuilt():
"""Returns the blue-themed quilt described at the beginning
of the quilt problem"""
def autumnQuilt():
"""Returns the autumn quilt described at the end of the quilt problem"""
# All code that displays pictures or prints output should be nested
# within this special "main" header at the end of ps03Quilt.py
if __name__ == "__main__":
closeAllPics() # Use this to close all previously created pictures.
# Uncomment particular lines below to test your code.
# Add more test cases for functions that you define!
#displayPic(patch2x2('red'))
#displayPic(triangles2x2('red', 'blue'))
#displayPic(bandana('red', 'blue'))
#displayPic(corner(triangles('red', 'blue'), patch('red')))
#displayPic(stripe(patch('red'), patch('blue'), patch('green')))
#displayPic(lowerLeft(triangles('red', 'blue')))
#displayPic(lowerLeftNest(patch('red'), triangles('red', 'blue')))
#displayPic(lowerLeftNestSelf4(triangles('darkslateblue', 'navy')))
#displayPic(blueQuilt())
#displayPic(autumnQuilt())
# Extra invocation to displayPic to fix picture glitch
# (a simple picture is sometimes needed to have previous pictures display)
displayPic(patch('red'))
Recall that in order to see your pictures, you'll need to to invoke
displayPic
with an argument which corresponds to the picture you
want to display. As indicated in the above code, you should put all
invocations of displayPic
at the very end of ps03Quilt.py
indented within the special header if __name__ == "__main__":
Notes:
-
There is a glitch that sometimes happens with pictures as follows: a complex picture may not display unless there is a simple picture displayed afterwards. To play it safe, it is wise to always display a simple picture last, like this:
displayPic(complex picture here) # this is the one you want to see displayPic(patch('red')) # simple picture here
- The
closeAllPics()
function call closes all previously created pictures. The above testing code calls this first so you will only see the pictures from clicking the Run button, and not pictures from previous Runs.
Completing the Assignment
You should define all the helper functions specified above. In addition to that, you should define other functions that capture other patterns that appear in the quilt. It is also helpful to define local variables within your functions to give names to pictures that you generate as part of your solution.
Each of your functions should be short: no more than a few statements long (say 7 or 8). If you find yourself defining a longer function, consider how you can break it up into smaller parts (preferably in a way that permits one or more of the smaller functions to be used multiple times).
Your final Task.
Oh no! A new intern just designed this new quilt shown below, using the same pattern as above, but with different colors. She forgot how she actually created this one. She asks you to figure out which color parameters were used in what order to create the pattern below.
To solve this problem, invoke your quilt
function to
produce the quilt shown below. These are the names of the colors
she used: firebrick
, sienna
, peachpuff
, darkred
, gold
, and orange3
.
![]() |
---|
The autumn version of the quilt |
Task 3: Honor Code File
As in PS02, your honor code submission for this pset will involve
defining entering values for the variables in the honorcode.py
file.
Remember to run otterInspect.py
one final time before you submit
to check that all your rock/paper/scissors functions work,
and that your honor code form is complete.
How to turn in this Problem Set
Soft-copy submission
- Save your final
rockPaperScissors.py
file in theps03
folder. Be sure to include your name in a comment at the top of this file. - Each team member should save their
ps03Quilt.py
file in theirps03
folder. This file should contain a comment with names of both partners at the top. - Save your filled-out
honorcode.py
file in theps03
folder as well. - Note: It is critical that the name of the folder you submit is
ps03
, and your submitted files arerockPaperScissors.py
,ps03Quilt.py
, andhonorcode.py
. In other words, do not rename the folder that you downloaded, do not create new files or folders (other thanps03Quilt.py
) and do not delete or re-name any of the existing files in this folder. We have automated scripts to check your electronic submission: an improperly named folder or a folder with impropertly named files will not count as a valid submission. - Drop your entire
ps03
folder in yourdrop
folder on thecs
server using Cyberduck by 11:59pm on Tuesday, February 21, 2017. - Failure to submit your code before the deadline will result in zero credit for the code portion of PS3.
Hard-copy submission
There is no hard copy submission. Problem set submission is entirely electronic.