Problem Set 2 - Due Mon, Feb 13 at 23:59
Reading and helpful Information
- The CS111 Problem Set Guide gives an overview of psets, including a detailed description of individual and partner tasks
- Goldwasser & Letscher, Chapter 3, sections 3.3-3.6, pages 103-110
- Slides and notebooks from Lec 2 and Lec 3.
- Problems and solutions from Lab 2 and Lab 3
- Our CS 111 Code Style Guide, which has been updated with information about functions.
About this Problem Set
This problem set will give you practice with abstraction, problem-solving by recognizing patterns, and functions.
- In both tasks you will create your own functions and practice breaking down a problem into simpler parts.
- Students with last names from M to Z are expected to make use of the CS 111 Google Group this week to either ask a question or answer one.
-
Pair programming: Use this shared Google Doc to find a pair programming partner and record who your pair partner is. Please do not use cs111-spring17 to find partners.
- Collaboration and honor code: 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.
- PS02 FAQ covers the most common issues students encounter when working on PS02, consult it before asking in the Google Group.
- Follow the practices discussed in our CS111 Code Style Guide.
- In previous semesters, students spent in average 3.2 hours on Task 1 (min = 1 hour, max = 10 hours) and 3.6 hours on Task 2 (min = 1 hour, max = 9 hours).
All code for this assignment is available in the ps02
folder in
the cs111/download
directory within your cs
server account.
This assignment also uses the Otter Inspector program that you encountered in Lab 3,
to help you do a final check of Task 1 and your Honor Code form before you submit.
Task 1: Diamond Pattern
This is an individual problem which you must complete on your own, though you may ask for help from the CS111 staff.
In the provided diamonds.py
,
define a zero-parameter function named diamondPattern
.
When diamondPattern
is invoked in the interactive pane,
it should display the following pattern of asterisks:
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
In Canopy, open diamonds.py
. Notice that it contains the following variable definitions.
# pre-defined strings
zeroStar = ' '
oneStar = ' * '
twoStar = ' * * '
threeStar = '* * *'
empty = ''
Notes
-
By using only these strings and no others,
diamondPattern
must print out the pattern shown above. There are possible solutions where one of these strings may not be necessary. -
This task would be easy (but tedious) if you could invoke
print
40 times, one for each of the 40 lines in the pattern. But we require that the print function may appear at most 5 times in your program. This is possible if you define helper functions to abstract over common patterns. Your goal is to make your program as simple and elegant as possible. -
In this problem, you are not allowed to use features of Python that we have not studied yet, even if you know them. In particular, you are not allowed to use conditionals (i.e., if statements) or loops (i.e., for loops or while loops).
-
You may concatenate these strings to form larger strings, but you are not allowed to create any new strings delimited by quotation marks.
-
You may not use the newline character
\n
. -
The goal of this problem is two-fold: to recognize patterns, and to write functions that can generate these patterns and combine them together to produce the entire drawing. We strongly recommend that you spend some time thinking about how to break the drawing into different patterns that can be captured by simple functions. If you are having difficulty recognizing patterns, we have a diagram to help you with this process. Access the diagram only after pondering on the pattern on your own.
-
This problem has many possible solutions. There is a particularly elegant solution that uses one single print statement. While we don't require you to do this, you might want to challenge yourselves.
- In the description so far we have been referring to the function by its name only, but when you invoke it in the interactive console, you'll need to include the parantheses
At the very end, once you're satisifed with your diamondPattern
function,
open the otterInspect.py
file in your ps02
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 evaluating whether your diamond pattern matches the
expected one.
This is a good way to check for any unexpected errors before you submit 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.
Task 2: Eyes Eyes Eyes
This task is a partner problem in which you are required to work with a partner as part of a two-person team.
Your goal is this task is to generate the following picture:

As you can imagine, you'll need an incremental approach to solving this problem.
First you write code to create a single face (implemeted with a Layer
) and position
it in its correct position on the canvas. Then, you decide on how to abstract over this code by
converting it into a function with parameters that can generate every face. You'll call
this function eyes
. In order to generate the entire image, you'll write a second function,
eyesPicture
(a zero-parameter function), which will call the eyes
function
eight times to draw each face on the Canvas object.
Initial code for the structure of the eyesPicture
and how to test it are given in the
file eyes.py
.
Specifications for drawing a face
-
Every face (a layer container) is composed of the head (rectangle with thick border), a nose (a path), and the two eyes (which are identical -- composed of a white ellipse and a filled black circle). From this point on we will refer to this face as a box.
-
The border of the box has width 10.
-
The whites of the eyes are ellipses whose width is 30% of the enclosing box and whose height is 80% of the box.
-
The centers of the eyes are located at 30% and 70% of the box width in the horizontal direction, and 45% of the box height in the vertical direction.
-
The black pupils of the eyes are circles whose radius is 10% the height of the box. They are centered in the enclosing white ellipses.
-
The top of the nose is at the center of the box. The tip of the nose is at a point 45% of the box width in the horizontal direction and 90% of the box height down in the vertical direction. The thickness of the nose line is 5.
-
The eight colors used for the boxes are: yellow, magenta, orange, pink, cyan, red, blue, and green.
- All the boxes in the picture have corners whose coordinates are multiples of 100.
Problem-solving: Start on paper
We recommend that before opening Canopy, you complete the following steps with paper and pen. (Of course, working with your partner.)
- Use the picture below to identify the width and height values for every face,
keeping track on them based on each color, in a table like below:
- yellow --> width = ?, height = ?
- magenta --> width = ?, height = ?
- etc.

-
Make a decision on how you want the face to be drawn in the layer. Will it be centered on (0, 0) (this way, some corners will have negative coordinates), or will it have its top-left corner on (0, 0) resulting into the entire face laying on the left-lower quadrant (this way, none of the corners has negative coordinates).
-
Based on the decision you made above, draw one face respective to the reference point of the layer. Start with a simple face, like the magenta, red, or blue face (which are laid horizontally). Use a paper with a grid to make it easy to draw where the eyes and the nose go. This activity will allow you to figure out the relation of all elements of the face to the only two given quantities you can use for this drawing: width and height of box (these relations can be expressed with math formulas, such as multiplication or additions). At this step, don't consider how the face will be positioned on the canvas yet. Simply draw it.
-
Now, look at the faces that are vertical and upside down. You might have noticed that they are rotated versions of the horizontal face. Calculate such a rotation angle for all eight faces. Add these angle values to the table of colors you started earlier.
- Finally, complete the table with the values of x, y coordinates where you need to move the reference point of the layer.
Incremental Development
Now that you have solved the problem on paper, you can start implementing this solution with Python code. But, you still have to do that incrementally, to make sure that you are not making mistakes, which are harder to find if you write first the entire code.
-
Create first the magenta face by initially using the hard-coded values you calculated in the problem-solving phase. Then, move the layer to the desired position and add it to the canvas. Does the face look like what you expected? If not, keep changing the code until the face looks the right way.
-
Now think of replacing the hard-coded values with variables and wrap the code into a function
eyes
with parameters. Since the canvas will not be part of this function, your function should return the layer object that represents the face. -
Call this function with the values for the magenta figure. Is the result the same as the previous step?
-
Call the
eyes
function with different arguments (values) to make other faces display in the canvas. - If this is working, you're ready for the final steps.
Final Steps
-
Until this point, you used a Canvas object outside any function (as a global variable) to test how the
eyes
function work. Remember toclose
the canvas object anytime you want to run the script, so that you don't need to restart the Kernel very often. -
The width and height of canvas are respectively 900 and 600.
- Once you are sure the
eyes
is working as expected and you can place all of faces in the expected positions, wrap all the calls ofeyes
into the following zero-parameter function, titledeyesPicture
.
def eyesPicture():
"""Function to generate the complete image. Creates a new Canvas object
when invoked. Returns this object.
Example:
In [1]: myPicture = eyesPicture()
In [2]: myPicture.close()
"""
canvas = Canvas(...)
# Add all eight eye patterns to the canvas.
canvas.add(eyes(...)) # 1st eye pattern
canvas.add(eyes(...)) # 2nw eye pattern
canvas.add(eyes(...)) # 3rd eye pattern
canvas.add(eyes(...)) # 4th eye pattern
canvas.add(eyes(...)) # 5th eye pattern
canvas.add(eyes(...)) # 6th eye pattern
canvas.add(eyes(...)) # 7th eye pattern
canvas.add(eyes(...)) # 8th eye pattern
return canvas
-
Calling
eyesPicture
should cause the entire eyes picture to be drawn. An example for how to call the function is shown in the function docstring above. -
Review your
eyes
function. Are you repeating certain expressions or statements more than once? Use abstraction to avoid repetition. For example, you can save the result of an expression in a variable, you can create helper functions for repeated statements, etc. - The file
eyes
also contains examples of using the helper functions fromcs1graphicsHelper
andgraphicsState
, which will be useful when generating the individual faces.
Task 3: Honor Code Form
Rather than filling out a Google Form for your honor code submission
as you did last time,
you will enter those values into the appropriate variables in the honorcode.py
file.
Remember to run otterInspect.py
one final time before you submit
to check that your diamonds
program works,
and that your honor code form is complete.
How to turn in this Problem Set
Soft-copy submission
- Save your final
diamonds.py
file in theps02
folder. - Each team member should save their
eyes.py
file in theirps02
folder. This file should contain a comment with names of both partners at the top. - Save your filled-up
honorcode.py
file inps02
as well. - Note: It is critical that the name of the folder you submit is
ps02
, and your submitted files arediamonds.py
,eyes.py
, andhonorcode.py
. In other words, do not rename the folder that you downloaded, do not create new files or folders, 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 will not count as a valid submission. - Drop your entire
ps02
folder in yourdrop
folder on thecs
server using Cyberduck by 11:59pm on Monday, Feb 13, 2017. - Failure to submit your code before the deadline will result in zero credit for the code portion of PS2.
Hard-copy submission
There is no hard copy submission. Problem set submission is entirely electronic.