$ update21 $ cd ~/cs21/labs/07 $ pwd /home/your_user_name/cs21/labs/07
For this lab we will continue using the Zelle graphics library to play the classic game of Simon. You will practice top-down design by writing your program as several functions that implement the game.
Here is a video of someone playing the verison of the game written for this lab.
We're going to guide you closely on this lab using the principles of top-down design, describing the functions you should be creating. In later labs, this part of the solution design will be up to you.
Let's think about the major steps your program will need to
accomplish in the main()
function:
Now, let's look at all these steps and see if they need further refinement.>
Create a graphics window. A window of 500x500 is a good size to create. Give it the title "Simon". This is just a line of code and doesn't even need to be a function. In fact, it is often bad style to create a separate function for a single line of code.
Create the game board. There are enough steps to creating
the game board it should be a function. This function should take
the GraphWin
object you created in the previous step as a
parameter. We'll draw the board as four equal squares. To help with
drawing the board, you may want to use the setCoords()
method of the GraphWin
class. This will change the
coordinate system to make the rest of the implementation easier for
you. It takes four arguments: setCoords(xll, yll, xur,
yur)
. Where the lower-left corner of the new coordinate
system is (xll, yll) and the upper-right corner of the new
coordinate system is (xur, yur). For
example, win.setCoords(0,2,2,0)
will give the following
coordinate system:
A good choice of colors for the squares are "green4", "red4", "yellow4", and "blue4". These are the ones I used, but feel free to choose other colors if you wish. Your function should take the four rectangle objects you created and return them as a list. This list of rectangle objects will be used in several of our other functions.
Let the player know the game is about to begin.
We'll do this with a Text object. We'll need to display several messages during the game, for example, to tell the player the game is about to begin, if they successfully completed a round, or if they have lost the game. Let's write a helper function to do this. This function should have two parameters. One parameter should be the window object to show the message. The other parameter should be a text string, the message to display to the user. The function should work as follows: It should create a text object, center the text in the middle of the board, set the text large enough to read, and set the color to white so it is easy to read. It should show the text and then wait for the user to click on the window. Once the user clicks on the window, it should get rid of the message and the function should return.Pick a random square to add to the sequence. It's a good idea to keep the sequence of squares to light up as a list. Adding a random square to the list is just a couple lines of code.
Show the sequence of flashing lights. To simulate the
flashing lights of the board game, you can change the color of the
rectangle to a brighter version of that color, pause with
the sleep()
function, and then change it back to its
original color. I suggest "green", "red", "yellow", and "blue" as
the bright colors to change briefly to (and then change back to the
already mentioned "green4", "red4", "yellow4", and "blue4" default
colors). This function should take two parameters. The first one
is a list of the rectangle objects, and the second parameter should
be a list containing the sequence of rectangles to flash (perhaps as
indexes to the rectangle list). This function should go through the
sequence, flashing each rectangle in turn.
Flashing a rectangle is complicated enough to break out into its own function, called from the function described in the previous paragraph. It should handle the flashing of a rectangle by changing to a bright version of its color, pausing for a period of time, and then back to its original color. This additional function should take three parameters. The first one is a list of the four rectangle objects, the second one is an index of the list representing the rectangle to flash, and the third parameter specifies for how long to flash the rectangle.
Let's look a little more closely as to how to implement this function. The first parameter is a list of rectangle objects. You know the order of those rectangles (since you created them). The second parameter is the index of the rectangle to flash (0-3). Inside your function you can create two lists, one for the default colors and one for bright colors, in the same order as your rectangle list. To flash the rectangle you would implement something like the following pseudocode:
function flashRect(rectList, rectIndex, flashTime)
normalColors = ['green4', 'red4', 'yellow4', 'blue4']
brightColors = ['green', 'red', 'yellow', 'blue']
change color of rectList[rectIndex] to brightColors[rectIndex]
wait for flashTime seconds
change color of rectList[rectIndex] to normalColors[rectIndex]
Get button presses from the user. This is complicated
enough to make into a function. The function should have three
parameters. The first one is the GraphWin
window object,
as we'll need to get mouse clicks from the user. The second parameter
is the list of rectangle objects, and the third parameter is the
sequence of rectangles the user should press. The function should
return True
if the sequence was matched by the correct
mouse clicks and it should return False
as soon as the
user clicks the wrong rectangle.
You might have noticed that in the description above, we have glossed
over how to tell if the user has clicked the right rectangle. As you
may have guessed, this is complicated enough to make into its own
function. This function should have just one parameter,
a GraphWin
object. It should return an integer,
representing the index of the rectangle that was clicked (from our
rectangle list). This function works by getting the point of the
mouse click from the user and seeing what rectangle that point lies
in. The image of the board drawn with coordinates should help you
determine which points lie within each rectangle.
Let the player know if they won or lost. We can do this by using the helper function we created for displaying text messages earlier.
Think about the data structures you are using in this program and how they are used as inputs to the functions. One import data structure is the list of the four rectangle objects. Another is a list of rectangles to flash. Think about the best way to represent this list. Hint: you already have a list of rectangle objects so you can take advantage of that fact.
Incrementally test your code. Write placeholder versions of all
the functions you plan to implement that just return some constant
value. As you work through the lab you will fill in the functions
with working implementations. But you should always be able to run
your program without it just plain crashing because some function is
not defined. First have a simple main()
which calls
your createBoard()
and then call
getMouse()
to have the program wait. Try printing the
output from the getMouse()
function to make sure you
understand how the setCoords()
method has changed the
coordinate system. Do not move on till this is working.
Then add some code to your main()
function to test
your showMessage()
function. Display a message to the
user saying "Click to Start" to see that your program is working as
you expect. It is the time for more testing!
Do this for every stage of you program. Every time you write a new function, run your code with debugging print statements to verify that your program is working how you think it should. It is much easier to debug your code incrementally.
You may run handin21 as many times as you like, and only the
most recent submission will be recorded. This is useful if you realize,
after handing in some programs, that you'd like to make a few more
changes to them.