Your task for this, the final project of the semester, is to complete the implementation of a simple version of the classic frogger game. In this game, each of several frogs has to cross a busy road and a swift-flowing river to reach the safety of lily pads on the other side. While crossing the road the frogs must avoid getting run over by cars; to cross the river they must hop from log to log (to avoid being eaten by crocodiles).
We are providing you with starter code that uses
the Tkinter
graphics package and the threading
concurrency package (both of which are standard in most installations of
Python).
You’ll discover that cars and logs are implemented as threads,
which execute concurrently (in parallel with one another). Frogs, on the
other hand, are implemented as event handlers, which execute in
response to arrow key presses in the little game window.
If you try out the code you’ll find that the cars, logs, and frogs all move, but they completely ignore one another. Your task is to implement the interactions, so that careless frogs get run over—or die if they fall in the water—and frogs that land on logs move with them up or down the river.
If you wish, you may work in teams of two on this project. If you choose this option, I recommend that one of you implement the interaction between frogs and cars, and the other implement the interaction between frogs and logs.
Right now, the locations of the cars, the logs, and the (single) active
frog are kept in X
and Y
fields of the
corresponding objects.
I recommend you break these out into a separate, global
locations
object, accessible to all your code.
This will make it easy for cars to tell when they run
over a frog, for logs to move the active frog, and for the frog to tell
when it has jumped under the wheels of a car or into the croc-infested
water.
Beware, however, that because this is a concurrent program, it is possible
for, say, both frog code and log code to try to move the active frog at
more or less the same time. This can lead to subtle bugs.
You will want to learn about
Lock
or
RLock
objects (either will do), which can be used to force threads to take
turns trying to change locations. The most straightforward way to
synchronize your program is
probably to put a lock object in the locations
object;
define getter and setter methods that read, write, or update
X
and Y
fields together; and protect the bodies of
those methods with lock acquire
and release
operations. The key is for each method call to move the world from one
consistent state to another (e.g., “if the frog is on top of this
log, move both of them together one unit up or down the river”).
In certain cases (e.g., on some Linux distributions), Python may be installed with a non-thread-safe version of Tkinter. If you encounter a strange “out of stack space” error message the moment you run your code, this may be what’s going on; check with the TA or professor for help. (Standard PC and Mac installations of IDLE should be fine.)
Please read the grading
standards web page carefully and follow its instructions.
In particular, note that you will need to create a README.txt
or README.pdf
file, and you will need to turn your code in
using Blackboard.
Many students have started too late on past assignments.
Don’t do that this time! There isn't a huge amount of code to
write, but it may take you several days of occasional study and
intervening contemplation to understand what's going on.
For extra credit (counted at the end of the semester; may raise your final grade), you might consider the following possibilities.