Problem Set 4 - Due Tue, Feb 26 at 23:59

Reading

  1. Slides and notebooks from Lec Functions and Lec 06 and 07: Booleans, Logical Expressions, Predicates, and Conditionals You do not need (in fact, should not use material from) Lec 08 Sequences and Simple Loops.
  2. Problems and solutions from Lab 03 Functions and Lab 04 Conditionals
  3. Think Python, Chapter 5: Conditionals (Secs 5.1 -- 5.7)

About this Problem Set

This problem set is intended to give you practice with conditionals, functions and the Divide, Conquer, and Glue (DCG) problem-solving strategy.

  1. In Task 1 (individual task), you will write functions using conditionals to create a rock-paper-scissors game.
  2. In Task 2 (partner-optional task), you will make some Blob simulations using functions that involve conditionals.
  3. In Task 3 (partner-optional task), you will create a quiz that uses functions with conditionals and user input to determine which animal you are.


Unlike the previous two problem sets, this one has three tasks, and two of them are partner-optional. In Tasks 2 and 3, having a partner is optional, but strongly recommended. You can choose to work with a partner on both of the tasks, or just one of the tasks if you prefer. However, if you choose both of the tasks, you should work with the same partner on both tasks. If you want to have a partner, use this shared Google Doc. Remember, you can talk with other individuals and teams about high-level problem-solving strategies, but you cannot share any code with them.

Other notes:

 


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, 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

Interactive Testing in Canopy

One way to test your functions is to first run your program in the Canopy editor window (to load the function definitions) and then 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

Automatic Testing in Canopy

Interactive testing can be tedious. As an alternative, we have provided a special main conditional block at the bottom of rockPaperScissors.py that includes test cases that will be automatically run every time you run your program in Canopy. This section begins as follows

if __name__ == "__main__":
    """All code that tests the above functions should be nested                       
    within this special "main" conditional.                                        
    """
    print("isValidGesture('paper') -->", isValidGesture('paper'))
    print("isValidGesture('spock') -->", isValidGesture('spock'))
    print("\nrandomGesture() -->", randomGesture())
    print("randomGesture() -->", randomGesture())
    ... many more tests below ...

Feel free to add or comment out tests in this block as desired.

Notes


Task 2: Blob Simulator

In this problem, having a partner is optional, but is strongly recommended. If you want to have a partner, use this shared Google Doc.

This task involves a whimsical simulation system with circular objects called blobs that follow rules to move around the four quadrants of a screen. Here's a short video of this simulation in action:

We give you most of the code for this simulation system and ask to fill in a small missing part of the code that implements the rules specifying the behavior of the blobs. These rules involve using conditionals.

You need to understand what the system is supposed to do in order to complete your part of the task. Most of this task description is an explanation of the blob simulation and the code that we've provided for you, so you'll need to do a lot of reading before you can start coding.

Background

This problem uses a cs1graphics layer called quadrants that is contained within an 800x600 canvas. The (0,0) point of the quadrants layer is in center of the canvas, and there are horizontal and vertical lines going through this point that divide the quadrants layer into 4 quadrants. As usual in cs1graphics, x coordinates increase to the right, and y coordinates increase going down.

The quadrants layer hosts a whimsical simulation system in which circular blobs move around the layer. Each blob is just a colored, borderless cs1graphics circle that is contained within the quadrants layer. The reference point of a blob is it center, so we refer the x or y location of a blob, we are referring the coordinates of its center point relative to the quadrants layer.

On each step of the simulation, the blob is first moved according to the moving rules, and then might be scaled according to the scaling rules:

Moving Rules

Scaling Rules

Example

We illustrate the rules in the context of a simple system with a single purple blob with initial radius 50 and initial location (-75,25). Below we show a sequence of state snapshots of this system in which each picture of a blob on the quadrants layer is accompanied by text showing the state number and the properties of the blob, The state number also appears in magenta in the upper left corner of quadrants.

state: 0; radius=50.0; location=(-75.0,-125.0)
state 1: radius=50.0; location=(-25.0,-125.0)
state: 2: radius=100.0; location=(25.0,-125.0)
state: 3; radius=200.0; location=(125.0,-25.0)
state: 4; radius=200.0; location=(325.0,175.0)
state: 5; radius=200.0; location=(125.0,175.0)
state 6: radius=100.0; location=(-75.0,175.0)
state 7: radius=50.0; location=(-175.0,175.0)
state 8: radius=25.0; location=(-225.0,175.0)
state 9: radius=12.5; location=(-250.0,175.0)
state 10: radius=6.25; location=(-262.5,50.0)
state 11: radius=6.25; location=(-268.75,-12.5)
state 12: radius=6.25; location=(-262.5,-12.5)
state 13: radius=6.25; location=(-256.25,-12.5)
 

The Simulation

The blobSimulator.py file in the ps04 folder contains almost all of the code for simulating the behavior of blobs on the quadrant layer. Although you don't need to understand this code in detail, here we give an overview of key aspects of the code.

A blob is simply a borderless, colored circle that "lives" in a layer:

# Do not edit or change makeBlob
def makeBlob(color, radius, x, y, containingLayer):
    """Create a new blob with the specified properties,
    and add it to the container layer. Return the blob.                                                  
    """
    blob = Circle(radius, Point(x, y))
    blob.setFillColor(color) 
    # remove borders, which don't look good when scaled
    blob.setBorderColor('transparent') 
    containingLayer.add(blob)
    return blob

The run function runs the simulation on a given list of blobs for a specified number of steps:

# Do not edit or change run
def run(blobs, steps):
    """Run simulation on the given blobs for the specified number of steps"""

    # Print initial state of system
    printSystemState(blobs)

    # Run simulation for given number of steps
    for i in range(steps):
        nextState(blobs)

The run function (and a few other functions) uses a simple loop to perform an action for every blob in a list of blobs. You are not required to understand loops for this assignment, but using loops in the simulation code simplifies it considerably.

The core part of the run function is calling the nextState function on a list of blobs. This increments the state number in the upper left corner of the quadrants layer, and updates the state of each blob according to the movement and scaling rules explained above:

# Do not edit or change nextState
def nextState(blobs):
    """Run one step of the simulation for the given blobs"""
    incrementStateNumber() # Increment state number in the upper left corner 
    for blob in blobs:
        update(blob)
    printSystemState(blobs)

The update function is not provided; your task is to flesh it out. See the details below.

The printSystemState function (provided) displays the current state of the simulation:

# Do not edit or change printSystemState
def printSystemState(blobs):
    """Display the current state of the simulation for the given blobs"""
    print('-'*55)
    print('state: ' + stateLabel.getMessage())
    for blob in blobs:
        printBlob(blob)

For example, here is the initial state of a system with five test blobs:

-------------------------------------------------------
state: 0
purple blob: radius=50.0; location=(-75.0,-125.0)
cyan blob: radius=30.0; location=(10.0,-250.0)
red blob: radius=40.0; location=(300.0,150.0)
yellow blob: radius=160.0; location=(-25.0,275.0)
green blob: radius=125.0; location=(-275.0,-200.0)

The printBlob function (provided) displays the current state of a single blob. It uses several helper functions (all provided):

# Do not edit or change printBlob, blobState, blobX or blobY
def printBlob(blob):
    """Print a line describing the current state of a blob"""
    print(blobState(blob))

def blobState(blob):
    """Returns a string describing the current state of a blob"""
    return str(blob.getFillColor()) + ' blob:'\
            + ' radius=' + str(blob.getRadius())\
            + '; location=' + '(' + str(blobX(blob))\
                            + ',' + str(blobY(blob)) + ')'

def blobX(blob):
    """Returns x coordinate of center of blob"""
    return blob.getReferencePoint().getX()

def blobY(blob):
    """Returns y coordinate of center of blob"""
    return blob.getReferencePoint().getY()

There is other code in blobSimulator.py for creating the quadrants layer and its enclosing canvas and adding to its upper left corner a text label that contains the current step number of the simulation. See blobSimulator.py for all the details.


Your Task

The only thing you must do in this problem is to flesh out the skeleton of the update function in blobSimulator.py so that it implements all the movement rules (Rules M1 through M4) and scaling rules (Rules S1 and S2) explained above.

def update(blob):
    """Update the state of a blob according to the movement and scaling rules
    in the ps04 task description.
    """
    pass # Replace this stub with your code. 

To implement the rules, you only need to use conditional statements involving the following blob operations:

Notes:

 


Task 3: Animal Quiz

In this problem, having a partner is optional, but is strongly recommended. If you want to have a partner, use this shared Google Doc.

What animal are you? In this problem, you will write a quiz program that determines your animal based on answers to three questions:

Note that above and below, previous references to `fluffy` have been replaced by `furry`.

The answers to these three questions determine the animal shown in this table:

like meat? like cold? prefer furry? animal
True True True polar bear
True True False orca
True False True tiger
True False False komodo dragon
False True True yak
False True False clam
False False True bunny
False False False tortoise

Your task is to flesh out the following four function definitions in the provided file named animalQuiz.py:

def boolFromResponse(response): 
    """Return True if the `response` string begins with a 'Y' or 'y'; 
    return False in all other cases. 
    The .lower() and .startswith() methods on strings are 
    helpful in this function.
    """
    # Flesh out the body of this function

def boolFromUser(prompt): 
    """Prompt the user with the yes/no question in the `prompt` string.
    Return True if the user response begins with a 'Y' or 'y'; 
    return False for all other user responses.
    You *must* use `boolFromResponse` in this function."""
    # Flesh out the body of this function
Important Note:The following skeleton for chooseAnimal fixes two issues with the original version in the download folder:
  1. The third parameter should be named prefersFurry, not prefersSmooth or prefersFluffy.
  2. The docstring now clarifies that likesMeat, likesCold, and prefersFurry are boolean values.
def chooseAnimal(likesMeat, likesCold, prefersFurry):
    """Based on the three boolean values `likesMeat`, `likesCold`,
    and `prefersFurry`, return the animal string determined by the table 
    in the description for PS04 Task 3."""
    # Flesh out the body of this function

def animalQuiz():
    """Prompt the user with three yes/no questions, and then display
    the animal determined by their answers. See the 
    PS04 Task 3 description for exact wording.
    You *must* use `boolFromUser` and `chooseAnimal` in this function."""
    # Flesh out the body of this function

Interactive Testing in Canopy

One way to test your functions is to first run your program in the Canopy editor window (to load the function definitions) and then enter sample calls of your functions in the interactive console to test that they work correctly. Here are some examples:

Sample Output for boolFromResponse

In []: boolFromResponse('Yes')
Out[]: True

In []: boolFromResponse('yes')
Out[]: True

In []: boolFromResponse('yup')
Out[]: True

In []: boolFromResponse('Yellow')
Out[]: True

In []: boolFromResponse('y')
Out[]: True

In []: boolFromResponse('No')
Out[]: False

In []: boolFromResponse('nope')
Out[]: False

In []: boolFromResponse('maybe')
Out[]: False

In []: boolFromResponse('affirmative')
Out[]: False

Sample Output for boolFromUser

In these examples, the text in magenta represents input typed by the user. These examples were updated on Feb 21 to show the correct spacing in Canopy.


In []: boolFromUser('Are you happy? ')

Are you happy? yup
Out[]: True

In []: boolFromUser('Are you happy? ')

Are you happy? NO
Out[]: False

In []: boolFromUser('Are you happy? ')

Are you happy? absolutely!
Out[]: False

Sample Output for chooseAnimal

In []: chooseAnimal(False, False, True)
Out[]: 'bunny'

In []: chooseAnimal(True, False, False)
Out[]: 'komodo dragon'

In []: chooseAnimal(True, False, True)
Out[]: 'tiger'

Sample Output for animalQuiz

In these examples, the text in magenta represents input typed by the user. These examples were updated on Feb 21 to show the correct spacing in Canopy. These examples were updated on Feb 23 to replace all occurences of fluffy by furry.


In []: animalQuiz()
What animal are you? Let's find out!

Do you like to eat meat? Nope

Do you like cold weather? yup!

Do you like furry things? Y

Your animal is the yak

In []: animalQuiz()
What animal are you? Let's find out!

Do you like to eat meat? unsure

Do you like cold weather? maybe

Do you like furry things? absolutely

Your animal is the tortoise

Automatic Testing in Canopy

Interactive testing can be tedious. As an alternative, we have provided a special main conditional block at the bottom of animalQuiz.py that includes calls to testing functions that will be automatically run every time you run your program in Canopy. This section looks like:

if __name__ == '__main__':
    '''Uncomment calls to these testing functions when ready'''
    # testBoolFromResponse()
    # testChooseAnimal()
    # print(boolFromUser('Are you happy? '))
    # animalQuiz()

If you uncomment the line # testBoolFromResponse(), then running animalQuiz.py in Canopy should produce the following output:

PASSED: boolFromResponse('Yes') returned expected value True
PASSED: boolFromResponse('yes') returned expected value True
PASSED: boolFromResponse('Yes!') returned expected value True
PASSED: boolFromResponse('yes!') returned expected value True
PASSED: boolFromResponse('Y') returned expected value True
PASSED: boolFromResponse('y') returned expected value True
PASSED: boolFromResponse('Yup') returned expected value True
PASSED: boolFromResponse('Yow!') returned expected value True
PASSED: boolFromResponse('yellow') returned expected value True
PASSED: boolFromResponse('yzz') returned expected value True
PASSED: boolFromResponse('No') returned expected value False
PASSED: boolFromResponse('no') returned expected value False
PASSED: boolFromResponse('Nope') returned expected value False
PASSED: boolFromResponse('nosiree') returned expected value False
PASSED: boolFromResponse('N') returned expected value False
PASSED: boolFromResponse('n') returned expected value False
PASSED: boolFromResponse('maybe') returned expected value False
PASSED: boolFromResponse('probably') returned expected value False
PASSED: boolFromResponse('affirmative') returned expected value False
PASSED: boolFromResponse('ABC') returned expected value False
PASSED: boolFromResponse('xyz') returned expected value False
PASSED: boolFromResponse('123') returned expected value False
PASSED: boolFromResponse('CS111') returned expected value False

If any of the lines begins ***FAILED, this indicates a bug in your function that you need to fix.

If you only uncomment the line # testChooseAnimal(), then running animalQuiz.py in Canopy should produce the following output:

PASSED: chooseAnimal(True, True, True) returned expected value 'polar bear'
PASSED: chooseAnimal(True, True, False) returned expected value 'orca'
PASSED: chooseAnimal(True, False, True) returned expected value 'tiger'
PASSED: chooseAnimal(True, False, False) returned expected value 'komodo dragon'
PASSED: chooseAnimal(False, True, True) returned expected value 'yak'
PASSED: chooseAnimal(False, True, False) returned expected value 'clam'
PASSED: chooseAnimal(False, False, True) returned expected value 'bunny'
PASSED: chooseAnimal(False, False, False) returned expected value 'tortoise'

If you only uncomment one of the other two lines, then running animalQuiz.py will test the boolFromUser or animalQuiz function, which will involve prompting you for input and printing a response.

Notes

In []: 'Wendy Wellesley'.lower()
Out[]: 'wendy wellesley'

In []: 'CS111'.lower()
Out[]: 'cs111'

In []: 'SHOUT!'.lower()
Out[]: 'shout!'

In []: 'the quick brown fox'.lower()
In []: 'the quick brown fox'
In []: 'Wendy Wellesley'.startswith('Wend')
Out[]: True

In []: 'Wendy Wellesley'.startswith('Well')
Out[]: False

In []: 'Wendy Wellesley'.startswith('W')
Out[]: True

In []: 'Wendy Wellesley'.startswith('w') 
Out[]: False # Case matters!

 


Task 4: Honor Code File

Your honor code submission for this pset will involve defining entering values for the variables in the honorcode.py file.


How to turn in this Problem Set

Softcopy (electronic) submission

Hardcopy (paper) submission

There is no hardcopy submission. Problem set submission is entirely electronic.