CS21 Lab6: Graphics and Functions

Due Saturday (March 7th) before midnight

This lab assignment requires you to write one program, left_shark.py, in Python. First, run update21. This will create the cs21/labs/06 directory and copy over any starting-point files for your programs. Next, move into your cs21/labs/06 directory and begin working on the Python programs for this lab. The pwd command helps you verify that you are in the correct sub-directory.

$ update21
$ cd cs21/labs/06
$ pwd
We will only grade files submitted by handin21 in this directory, so make sure your programs are in this directory!

Introducing Left Shark

You may have heard about Left Shark from his appearance during the Superbowl half time show this year. If you are not aware of Left Shark's free-styling dance moves, you can see him in all his shark glory in the following clip:

Following the Superbowl, Left Shark has become an Internet superstar. He even has his own diary in the New Yorker. It turns out Left Shark is a big fan of CS21. He saw some of the awesome artwork you did for the last assignment, so he gave us a call asking for our help to write a game using graphics to help him brush up on his dance moves. With your help, we can get Left Shark back on the Aquatic Dance Circuit (ADC).

For this lab, we're going to help out Left Shark by helping him practice his dance moves. You will write a game (with some help from us) that creates two sharks, Left Shark and Right Shark. Right shark will build up a dance routine, one dance move at a time, and the player will control Left Shark using the keyboard to repeat the dance moves in the same order that Right Shark did them in. If the player gets all the moves correct, the player will move on to the next level where Right Shark will add one more move to the same dance routine.

Right shark will perform this new routine (with the one extra move added to the end) and the player will attempt to match the new routine. This keeps going until the player misses a move. At that point the game is over. This is similar to the electronic game Simon that you might have played when you were a kid. However, in this case you are repeating dance moves instead of pushing colored buttons.

Here is a video of someone playing the version of the game written for this lab.

Since this is the first program where you will be writing functions other than the main() function, we're going to guide you closely on this lab, describing in detail each of the functions you should be creating. In later labs, what functions you create will be part of your design and will be up to you, but for this lab you must write the functions exactly as we describe them.

You should read this entire lab carefully before you begin and you should write your program incrementally one function at a time. Do not move on until you have thoroughly tested that function. This is really crucial to success: test, test again, and then test some more! Come up with your own test cases and verify that the program is producing the right output on those functions before moving on.

Getting to Know the Sharks
Before we dive into the lab, let's first meet our sharks. Luckily, you do not have to create the sharks from scratch. There is a shark module that you can import to draw and move our sharks. Let's open a python window and check it out.
$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from shark import *
>>> from graphics import *
>>> from time import sleep
>>> win = GraphWin("Shark Tank", 500, 500)
>>> leftShark = Shark(Point(250,250))
>>> leftShark.draw(win)
When you do this you should get the following window:

The shark is now in it's default position, both arms up and both legs down. This is the natural state of the Left Shark as seen in the wild. I think we can all agree Left Shark is a pretty shark in this state, but Left Shark was born to dance so let's look at how we can make him dance.

The shark object supports many of the same methods(move(), draw(), undraw(), getCenter()) that we are used to seeing with the other graphics objects. It also has some new methods such as moveLimb() which is described below.

To move our shark, we can use the moveLimb() method. This method takes two arguments, both strings. The first one is the limb to move and must be one of the following: "leftArm", "rightArm", "leftLeg", or "rightLeg". The second argument is the position to move the limb and must be one of the following: "up" or "down". Continuing our example from above, here is the code to move the shark's left arm down:

>>> leftShark.moveLimb("leftArm", "down")

after that line of code the shark should look like this:

To put him back in his natural state call the moveLimb() again but this time with "up" for the position:

>>> leftShark.moveLimb("leftArm", "up")

For this game, a dance move is moving one of the limbs for a small period of time (say about a second or so) and then back to the limb's natural position. For the arms, the natural position is "up", and for the legs, the natural position is "down". So to do one dance move, we'll move the limb, sleep for a second, then move the limb back to its normal position. For our example, here's how we can make our shark do a dance move with the right leg:

>>> leftShark.moveLimb("rightLeg", "up")
>>> sleep(1)
>>> leftShark.moveLimb("rightLeg", "down")
Below is the full documentation on the shark object. Note that you will not need to use all of these methods to complete the lab. They are there for completeness and may be useful if you decide to do the extra challenges.
class Shark(__builtin__.object)
 |  Methods defined here:
 |  __init__(self, center)
 |      Creates a new shark object. 
 |      Parameters:
 |          center: The point to locate the center of the shark
 |      Returns: nothing
 |  draw(self, window)
 |      Draws the shark. 
 |      Parameters:
 |          window: the window to draw the shark in
 |  getCenter(self)
 |      Returns the center point of the shark.
 |  getLimbStatus(self, limb)
 |      Returns the status ("up" or "down") of a limb.
 |      Parameters: limb: the limb to check the status of (one of:
 |            "leftLeg", "rightLeg", "leftArm", or "rightArm")
 |      Returns: the status of the limb (either "up" or "down") or
 |            None if the limb wasn't recognized as a valid limb.
 |  move(self, dx, dy)
 |      Moves the shark.
 |      Parameters:
 |              dx: the number of pixels to move the shark in the x direction
 |              dy: the number of pixels to move the shark in the y direction
 |  moveLimb(self, limb, direction)
 |      Moves a limb on the shark.
 |      Parameters: 
 |         limb: the limb to move (one of: "leftLeg", "rightLeg",
 |               "leftArm", or "rightArm")
 |         direction: the direction to move in, either "up" or "down"
 |  setBellyColor(self, color)
 |      Sets the belly color of the shark.
 |      Parameters:
 |           color: any valid color string
 |  setSkinColor(self, color)
 |      Sets the skin color of the shark.
 |      Parameters:
 |           color: any valid color string
 |  undraw(self)
 |      Undraws the shark.
Writing your program

For this lab we have given you some starter code in the file left_shark.py. Before writing your own code go through this code and make sure you understand everything this starter code is doing.

Just as in our other programs, the program will be started by running the main() function. This function is heavily commented to show what it is doing and what you need to do. The first part of main() creates the objects and variables needed for the rest of the program. The game is played in the while loop in main(). Once we get out of the while loop, the game is over and there is a small amount of code after the loop to cleanly exit the program.

You'll want to focus on the while loop in main, which plays the game for us. The game is played in a series of rounds and playing one round of the game is done with the function playOneRound(). This handles the playing of the game and is described below. This function returns True if the player won the round. If the player wins the round, you'll then need to add a new random limb to the moves list, update the level text object and play another round. If the player loses the round the game is over and you can exit the while loop and quit the game.

playOneRound(): This function takes the list of dance moves and then moves the shark on the right accordingly. Then the player controls the left shark using the keyboard with the following key mappings:

Do not deviate from these key mappings! Changing the key mappings will make it harder to grade and make the graders sad that they can't get past level 1 in the game.

After each key press, you should move the limb corresponding to the key press, and then check to see if it was the correct limb. If it was not, then you should immediately return False. Otherwise, wait for the next key press and check again. If the user gets all the moves right, return True.

This function will use three functions that we will write later. sharkDanceMove(add your parameters here) will handle the movements of a shark to do one dance move, getKey() will get a valid key press from the user, and getLimbFromKey(key) will take a key press and return the limb that maps to that key. You will need to figure out what are the correct parameters for sharkDanceMove() based on the description of the function below. With these three functions in mind, the pseudo code for the playOneRound() function will look like this:

  # Have right shark dance the sequence of moves
  for every limb in the moves list:
      use sharkDanceMove() to do one dance move
      pause for 0.2 seconds to separate one move from the next

  # Left shark now matches the moves
  for every limb in the moves list:
      use getKey() to get a key press from the user
      use getLimbFromKey() to turn that key to a limb
      use sharkDanceMove() to make left shark dance one move
      if the move wasn't correct return False
  if we make it through the list, all moves were correct and we can
  return True

getKey(win, validKeys): This function gets a valid key press from the user. Ignore all other key presses and only return after the user presses a key corresponding to a valid dance move. The first parameter is the GraphWin window to listen for key presses. The second parameter is a list of valid characters, e.g. 'e', 'r', etc. It returns the valid key press entered by the user.

To get keyboard input from the user, we will use the getKey() method of the GraphWin object. We have not seen this method before, but it works just like the getMouse() method that we have been using. The only difference is that it waits for a key press (instead of a mouse click) and returns the key pressed.

getLimbFromKey(key): This handles the mapping of our key press to the correct limb name. Nothing a few if statements can't handle, right? Smallish functions like these are know as helper functions. They help us keep other functions short and clean.

sharkDanceMove(): This function handles doing one dance move for a shark. To do a dance move we need three things, a shark, a limb to move, and the amount of time we should keep the limb in the dance position before moving it back to it's natural state. We want to do three things in this function, move the limb to the dance position, pause the right amount of time, and then move the limb back to its normal position. We have left the documentation and parameters out of the sample code, so you need to figure out what needs to go in there based on the description above.

Here's some hints and tips for completing this lab.
Optional challenges
These are optional bonus exercises. It is challenging and is not required, and you should not attempt it until you are completely finished with the other lab exercises. It will also not get you any extra points -- you will only be rewarded with extra knowledge and satisfaction. :) If you want to do any of these challenges, first make a copy of your current solution.
$ cp left_shark.py left_shark_extra.py
Add your bonus features to left_shark_extra.py and keep left_shark.py as described in this write-up.

There's lots of ways you can spruce up this game. Here are some suggestions, but feel free to add your own:


Remember you may run handin21 as many times as you like. Each time you run it new versions of your files will be submitted. Running handin21 after you finish a program, after any major changes are made, and at the end of the day (before you log out) is a good habit to get into.