As always, run update21 to create your cs21/labs/10
directory and create your programs for lab 10 in this directory. Since this is a graphics assignment, make use of the Zelle Graphics Documentation.
So far in this course we have used many different types of objects (Circles
, Points
, lists
, strings
, etc), but we haven't defined our own types. The goal of this lab is to practice writing your own classes (with constructors, instance variables, and methods). In the file aquarium.py
, you will create an aquarium of swimming fish, like the videos shown below. In a separate file called fish.py
, you will have a Fish
class with methods to control the fish behavior. Pick one fish and watch its movement - when it reaches one side of the tank it wraps around and comes back on the other side, continuing to swim in the same pattern indefinitely.
This aquarium is interactive - over time fish will get "sick" (and some fish may start out sick). Your job is to save the fish by clicking on sick fish to make them healthy. If you make all the fish healthy, or if all the fish get sick, the game is over.
Here is an example where sick fish turn red. In this case the user won by saving all the fish:
Enter the number of fish: 30
Here is an example where all the fish got sick:
Enter the number of fish: 30
Here is another example aquarium where the sick fish turn black. This example includes fish bobbing and bubbles (these steps are optional, see below for more details).
Each of the sections below describes a requirement and some suggestions for implementation. One high-level suggestion is to try to mirror existing methods for other graphics objects (.draw(win)
, etc). Feel free to modify your class, but check with your instructor if you make major modifications.
Your program should contain a Fish
class (in a separate fish.py
file) with the following:
The constructor __init__
. Building on the Circle
constructor, your fish constructor should have at least three parameters: center
(a Point
), radius
(int
), and a color. In your constructor, you should set up the body (a circle), the eye (a circle), and a tail (your choice, could be a circle, rectangle, polygon, or oval). Feel free to include other objects as well like gils or fins. Make the parts of the fish scale as the radius scales, but it is okay to hard-code the ratios.
Instance variables. These are somewhat flexible, but we recommend having an instance variable for fish parts you might need later on (body
, tail
, eye
), and/or an instance variable that is a list of all the fish pieces (part_lst
) (so they can be moved or drawn using a loop). Also keep an instance variable for the fish's original color
, and the fish's sick
status, since this will change throughout the game. Finally, you should have a velocity
instance variable that is chosen randomly. This variable could be positive for fish that swim right, or negative for fish that swim left. Alternatively, you could have a speed
instance variable that is always positive, and a direction
variable to keep track of left/right.
The method draw(window)
, where window
is of type GraphWin
. This is similar to the draw method for all other graphics objects. Inside your draw method, you should draw all the parts that make up your fish.
A move(dx, dy)
method, that should work exactly like the move method for other graphics objects.
A swim()
method, which should call the move(..)
method to make the fish swim one "step" in its selected direction, at its selected speed.
Other methods similar to other graphics objects: setFill(color)
, getRadius()
, and getCenter()
.
Methods related to the fish being sick or healthy (these are up to you, but you should have a way to make the fish change color when it's sick, and a way for the fish to return to its original color when it's healthy). You may also want to include a method to see if a fish is clicked or not. One recommendation (not requirement) is to have a boolean instance variable self.sick
that is True if the fish is sick and False if it is healthy. Then you could have a getter for this instance variable, as well as a method makeSick()
which would change the fish's status to sick and also change its color. Finally, you could have a makeHealthy()
method that changes the fish's status back to healthy and changes its color back to the original color.
Here is some example test code to try in the main
of the fish.py
file:
# create two fish
pt1 = Point(width/4,height/2)
size = width/13
onefish = Fish(pt1,size,"red")
onefish.draw(win)
pt2 = Point(width/2,height/4)
size = width/15
twofish = Fish(pt2,size,"blue")
twofish.draw(win)
The user should be able to select the number of fish at the beginning of the game (you may assume the user enters a valid input). The fish should be different in at least the following ways:
Each fish should swim in a "circle" around the tank, wrapping around one side and emerging on the other. This wrapping functionality should be done outside the Fish
class, since fish do not know about the window they are swimming in. We recommend making a wrapping function that checks the position of each fish and sees if it needs to be "wrapped".
In main (in the file aquarium.py
), you should create a list of Fish
, based on user input. Make sure to import the Fish class. Fish should be located randomly throughout the window. If setting up the fish constructor input becomes a bit long, feel free to move this to another helper function. In main, continue to loop over the fish, moving them one by one. Each time a fish moves, it has a small probability of becoming sick. When it gets sick it should change to a different color (red, black, gray, green, up to you).
To control the speed of the game, you can use time.sleep(seconds)
at the beginning of each loop through the fish. Set seconds
to whatever looks good to you (0.1, 0.5, etc). To use this functionality, also remember to import time
at the top.
A note about RGB color schemes. When creating fish colors, it may be helpful to avoid fish colors that match the "sick" color. For example, if you had a light sick color, you could color the rest of your fish darker colors. When using RGB, higher values mean lighter colors. For example, (255,255,255) is white, and (0,0,0) is black. Or if you had sick fish that were red, you could set the red component for the rest of the fish to be close to 0 to avoid red fish.
During the animation loop, the user should be able to click on a fish to make it healthy. Since we want the animation to progress even when the user is not clicking, we'll use the win.checkMouse()
function. Unlike win.getMouse()
, which pauses the program until the user clicks, win.checkMouse()
will allow the program to keep running, and if the user doesn't click, it will return None
. So if the user's click is not None
, check how close the click point is to each of the fish (similar to Lab 6), and change the color if it is inside the body of the fish (no need to worry about the case when the click is on the tail).
Here is a template for using win.checkMouse()
:
click = win.checkMouse()
if click != None:
# do something with the click
If the user makes all the fish healthy, they should win and the game should end. It might be helpful to have a separate function that loops through all the fish and returns True if they are all healthy. On the flip side, if all the fish get sick, the user loses and the game is over. You could have another function that returns True if all the fish are sick. Alternatively, you could have a function for getting the number of sick fish, and then test if that number is 0 vs. the total number of fish.
Although you will not be submitting your top-down design (TDD) this week, we strongly encourage you to write your full design first. Design your main()
function, classes and methods, and any additional helper functions. Your main()
function should be relatively short and easy to read. Implement and test the program one function or method at a time. A good program design and good use of functions is part of the grade.
There are many ways to extend this assignment. A few suggestions are below, but feel free to come up with your own extension.
Adding another class - the example below shows a bubble class (similar to the fish class). Bubbles float upwards and wrap around the bottom of the window. The example below does not include game play, but your version should still include that. You could create another type of fish or sea creature.
You could modify the getting sick process to include a contagious element. So if fish comes in contact with a sick fish, that is what makes it sick (as opposed to completely random).
Make the fish (and/or bubble) bob up and down. One hint for this is to use a trig function like sine or cosine.
Enter the number of fish: 40
Enter the number of fish: 20
After you have turned in the full program, please edit the QUESTIONS-10.txt
file in your cs21/labs/10
directory and answer the questions in that file.
Don't forget to run handin21
to turn in your lab files! You may run handin21
as many times as you want. Each time it will turn in any new work. We recommend running handin21
after you complete significant work on any part of the program.
Idea for this lab: Joe O'Rourke