The bees are coming!
Create a better soldier
In this project, you will create a tower defense game called Ants Vs. SomeBees. As the ant queen, you populate your colony with the bravest ants you can muster. Your ants must protect their queen from the evil bees that invade your territory. Irritate the bees enough by throwing leaves at them, and they will be vanquished. Fail to pester the airborne intruders adequately, and your queen will succumb to the bees' wrath. This game is inspired by PopCap Games' Plants Vs. Zombies ®.
This project combines functional and object-oriented programming paradigms, focusing on the material from Chapter 2.5 of the lecture notes. The project also involves understanding, extending, and testing a large program with many related parts.
This project includes several files, but all of your changes will be made to the first one. You can download all of the project code as a zip archive.
||The game logic of Ants Vs. SomeBees.|
||A series of unit tests to test various parts of your project.|
||Graphics for Ants Vs. SomeBees.|
||General functions for displaying simple two-dimensional animations.|
||Utility functions for 61A.|
||A directory of images used by the graphical version of the game.|
This is a two-week project. You'll work in a team of two people, person A and person B. In each part, you will do some of the work separately and some together with your partner. For example, if a problem is marked A1, then it is a solo problem for person A. Both partners should read, think about, and understand the solution to all questions.
Start early! The amount of time it takes to complete a project (or any program) is unpredictable. Ask for help early and often -- the TAs and lab assistants are here to help.
In the end, you and your partner will submit one project. Person-specific problems are graded individually and do not affect your partner's score. There are 22 possible points for each person. If you choose to work alone, you must complete the entire project, including all of the questions posed to both partners.
The only file that you are required to submit is
You do not need to modify any other files to complete the project. To submit
the project, change to the directory where the files are located and run
A game of Ants Vs. SomeBees consists of a series of turns. In each turn, new bees may enter the ant colony. Then, new ants are placed. Finally, all insects (ants, then bees) take individual actions: bees sting ants, and ants throw leaves at bees. The game ends either when a bee reaches the ant queen (you lose), or the entire bee flotilla has been vanquished (you win).
The Colony. The colony consists of several places that are chained
exit of each
Place leads to another
Placing Ants. There are two constraints that limit ant production.
Placing an ant uses up some amount of the colony's food, a different amount for
each type of ant. Also, only one ant can occupy each
Bees. When it is time to act, a bee either moves to the
exit of its current
Place if no ant blocks its path,
or stings an ant that blocks its path.
Ants. Each type of ant takes a different action and requires a
different amount of food to place. The two most basic ant types are the
HarvesterAnt, which adds one food to the colony during each turn,
ThrowerAnt, which throws a leaf at a bee each turn.
Most concepts in the game have a corresponding class that encapsulates the
logic for that concept. For instance, a
Place in the colony holds
insects and connects to other places. A
Bee stings ants and
advances through exits.
The game can be run in two modes: as a text-based game or using a graphical user interface (GUI). The game logic is the same in either case, but the GUI enforces a turn time limit that makes playing the game more exciting. The text-based interface is provided for debugging and development.
The files are separated according to these two modes.
knows nothing of graphics or turn time limits. All graphical elements are
graphics.py. It is
possible to complete this project without ever reading the graphics files.
To start a text-based game, run
To start a graphical game, run
When you start the graphical version, a new window should appear:
In the starter implementation, you have unlimited food and your ants only
throw leaves at bees in their current
Place. Try playing a game
anyway! You'll need to place a lot of
ThrowerAnts (the second
type) in order to keep the bees from reaching your queen.
You have also been provided a testing file
tests.py that runs a
series of unit tests for the project. To test your project, you can run
python3 tests.py -v
This command runs all of the unit tests along with any doctests in ants.py.
-v generates more verbose output. If you would like
to learn more about Python's built-in unit testing framework, read the
documentation on the unittest module.
Most problems have associated tests. Make sure that the tests for each problem
pass before moving on.
Problem 1 (0 pts). Answer the following questions with your partner
after you have read the entire
ants.py file. If you cannot
answer these questions, read the file again.
runis not a method)!
Hive, a subclass of
Place, is the starting location of the bees. Unlike most instances of
Hiveclass does not have an
exit. Explain how and when
Bees leave the
armorattribute? What happens when
Problem 2 (2 pts). Add food costs and implement harvesters.
Currently, there is no cost for deploying any type of
Ant, and so
there is no challenge to the game. You'll notice that
out with a base
food_cost of 0. Override this value in each of the
subclasses listed below with the correct costs.
Now there's no way to gather more food! To fix this issue, implement the
HarvesterAnt class. A
HarvesterAnt is a type of
Ant that adds one food to the
colony.food total as its
Try playing the game again. Once you have placed a
HarvesterAnt, you should accumulate food each turn. Vanquishing
the bees using the default game setup is now possible, but should be
Problem 3 (2 pts). Add code to the
that tracks entrances. Right now, a
Place keeps track only of its
exit. We would like a
Place to keep track of its
entrance as well. A
Place needs to track only one
However, simply passing an entrance to a
Place constructor will
be problematic; we will need to have both the exit and the entrance before we
can create a
Place! (It's a chicken
or the egg problem.) To get around this problem, we will keep track of
entrances in the following way instead. The
should specify that:
Placealways starts with its
exit, then the
entranceis set to that
Problem A4 (2 pts). (This problem description was updated on
10/8/12; new text appears in italics and old text is crossed out.) Add
water to the colony. Currently there are only two types of places, the
Hive and a basic
Place. To make things more
interesting, we're going to create a new type of
Only an ant that is
watersafe can be deployed to a
Water place. In order to determine whether an
watersafe, add a new attribute to the
watersafe that is
False by default. Since bees
can fly, make their
overriding the default.
Now, implement the
add_insect method for
that, in addition to doing the same thing as a First call
Place would do,
immediately kills the added insect if it is not
Place.add_insect to add the insect, regardless of whether it is
watersafe. Then, if the insect is not watersafe, reduce the insect's armor to
0 by invoking
reduce_armor. Do not copy and paste
code. Try to use methods that have already been defined and make use of
inheritance to reuse the functionality of the
Once you've finished this problem, play a game that includes water. To
mixed_layout that includes water, add the
--water option (or
-w for short) when you start the
python3 ants_gui.py --water
Problem A5 (3 pts). Implement the
FireAnt has a special
reduce_armor method that, when
FireAnt's armor reaches zero or lower, will reduce the armor of
Bees in the same
Place as the
3 (a fiery end indeed).
Hint: If you iterate over a list, but change the contents of that list at the same time, you may not see all the elements. As the Python tutorial suggests, "If you need to modify the list you are iterating over, you must iterate over a copy." Remember that damaging a bee may cause it to be removed from its place.
Once you've finished implementing the
FireAnt, give it a
implemented with the value
This attribute tells the game that you've added a new type of
FireAnt, be sure to test your program by
playing a game or two! A
FireAnt should destroy any co-located
Bees when it dies.
Problem B4 (2 pts). Implement the
nearest_bee method for
ThrowerAnt class. In order for a
attack, it must know which bee it should hit. The provided implementation will
only hit bees in the same
Place. Your job is to fix it so that a
throw_at the nearest bee in front of
it that is not still in the
nearest_bee method returns a random
the nearest place that contains bees. Places are inspected in order by
be able to
Bee in front of it that is not
still in the
Hive. Make sure that your ants do the right
Problem B5 (3 pts). Now that the
ThrowerAnt has been
completed, implement two subclasses of
Beethat is found after following at least 4
entrancetransitions. So the
Bees that are in the same
Placeas it or the first 3
Places in front of it.
Beethat is found after following at most 2
entrancetransitions. So the
ShortThrowercan only hit
Bees in the same
Placeas it and 2
Places in front of it.
Neither of these specialized throwers can
Bee that is exactly 3
Places away. Placing a single
one of these (and no other ants) should never win a default game.
To implement these behaviors, modify the
nearest_bee method to
max_range attributes, and only
return a bee that is in range.
For the base class,
min_range to 0
max_range to 10. Then, implement the subclasses
ShortThrower with appropriately
constrained ranges and correct food costs.
implemented class attribute of
Try playing a game with your newly implemented ants. Be sure that they do
what you expect them to! You can try running
ants_gui.py with the
--full option to go up against a full swarm of bees in a
multi-tunnel layout, and add
--insane if you want a real challenge!
If the bees are too numerous to vanquish, you might need to create some new ants
in Phase 2.
Problem A6 (1 pts). We are going to add some protection to our
AntColony by implementing the
is an ant that does nothing each turn (already the default
Ant class). A
WallAnt is useful because it has
Problem A7 (3 pts). Implement the
Bees that pass by, but is never seen.
NinjaAnt is not able to be attacked by a
because it is hidden, nor does it block the path of a
flies by. To implement this behavior, first modify the
to include a new class attribute
blocks_path that is
True by default. Set the value of
False in the
Second, modify the
blocked to return
False if either there is no
Ant in the
place or if there is an
blocks_path attribute is
Bees will just fly past
Finally, we want to make the
NinjaAnt damage all
Bee's that fly past. Implement the
action method in
NinjaAnt to reduce the armor of all
Bees in the same
place as the
NinjaAnt by 1, overriding the default
action method inherited from
For a challenge, try to win a default game using only
Problem B6 (1 pts). Currently there are no ants that can be placed on
Water. Implement the
ScubaThrower, which is a
ThrowerAnt that is more costly and
watersafe, but otherwise identical to its base class.
Water should not cause it to
Problem B7 (3 pts). We will now implement the new offensive unit
HungryAnt, which will eat a random
place, instantly killing the
Bee. After eating a
Bee, it must spend 3 turns digesting before eating again.
To implement, give
attribute that holds the number of turns that it takes all
HungryAnts to digest (default to 3). Also, give each
HungryAnt an instance attribute
digesting that counts
the number of turns it has left to digest (default is 0, since it hasn't eaten
anything at the beginning).
Now we implement the
action method of the
to check if it's digesting; if so, decrement its
Otherwise, eat a random
Bee in its
place (killing the
Bee and restarting the
Problem 8 (5 pts). (This problem description was updated on
10/8/12; new text appears in italics and old text is crossed out.)
BodyguardAnt. Right now, our ants are quite frail.
We'd like to provide a way to help them last longer against the onslaught of
the bees. Enter the
BodyguardAnt differs from a normal
Ant because it can
occupy the same
Place as another ant. When a
is added to the same
Place as another ant, it shields the other ant
and protects it from damage. Attacks should damage the
first and only hurt the protected ant after the
BodyguardAnt has an instance attribute
ant that stores
the ant contained within the bodyguard. It should start off as
indicating that no ant is currently being protected. Give
contain_ant method that takes an
Ant argument and
ant instance attribute to that argument.
Now, change your program so that a
BodyguardAnt and another
Ant can simultaneously occupy the same
Ant.containerclass attribute that indicates whether an ant can contain another. For all
BodyguardAnt.containerattribute should be
Ants a new method,
can_contain, that takes an
otherant as an argument and returns
Trueif and only if:
add_insectmethod of the
Placeclass will immediately cause an error. Change
add_insectso that the
Placecontains the container ant and the container ant contains the other ant:
Antcurrently occupying this
Placecan contain the
Antwe are trying to add, then simply tell it to do so.
Antwe are trying to add can contain the
Antcurrently occupying this
Place, then have it do so and set this
Place's ant to be the newly added
Antcan contain the other, then raise the same assertion error as before.
Almost done! Just a few more things to do.
BodyguardAntperishes, we need to make sure the ant it currently contains (if it contains one) takes the
BodyguardAnt's place. Override the
reduce_armormethod so that, if the
BodyguardAntperishes, it will set its place's ant to be the ant it currently contains. (Remember to use inheritance!)
BodyguardAnts still perform their action. Override the
BodyguardAnt containing another ant is removed, then
the ant it is containing should be placed where the
used to be. However, because this behavior was not specified initially, it
is optional and will not be tested for correctness. If you prefer a different
behavior for removing
BodyguardAnts, you may implement any
alternative you wish.
Problem 9 (4 pts). (This problem description was updated on
10/8/12; new text appears in italics). Implement the
QueenAnt. The queen is a
ThrowerAnt that inspires
her fellow ants through her bravery. Any ant positioned behind the queen
(between the queen and the colony) deals double the damage that it normally
would. Damage doubling only applies to ants in the same tunnel as the
queen, not adjacent tunnels. Damage doubling need only occur after the
QueenAnt performs her action; not when she is placed.
However, with great power comes great responsibility. The Queen is goverened by three special rules:
AntColony.simulate, the bees win the game
len(self.queen.bees) > 0, where
the ant colony. Normally, the
queen attribute of an
AntColony is an instance of a
Place. As part of
action of a
colony.queen should be replaced by a new object, a
QueenPlace (a class that you must add). A
QueenPlace has a
bees property method that evalutes
to the list of all bees that are either in the original
colony.queen location or the
place of the
You should not have to change the implementation of
AntColony.simulate or manipulate the location of bees in any
special way. You may assume that a
colony.queen attribute will
be used for only one purpose: to check whether
len(self.queen.bees) > 0. Thus, a
instance does not need to support other
Place methods, such as
There can be only one true queen. Any queen beyond the first one is an
impostor and should die immediately (its armor reduced to 0) upon taking its
first action. Impostor queens should not affect the colony's
queen attribute. You can detect impostor queens by counting the
number of times that an instance of a
QueenAnt has been
constructed, using a class attribute. Any
QueenAnt beyond the
first one created is an impostor. You should not have to search through the
colony places to find other queens.
The true (first) queen cannot be removed. Attempts to remove the queen
should have no effect (but should not cause an error). You will need to
remove_insect method of
Place to enforce
Extra Credit (2 pts). Implement two final thrower ants that do no
damage, but instead replace the
action method of a
instance that they
throw_at with a new method that alters the
Bee's behavior for some duration.
We will be implementing two new ants that subclass
SlowThrowerapplies a slow effect for 3 turns.
StunThrowerapplies a stun effect for 1 turn.
In order to complete the implementations of these two ants, you will need to set their class attributes appropriately and implement the following three functions:
actionmethod and returns a new
actionmethod which performs the original action on turns where
colony.timeis even and does nothing on other turns.
actionmethod and returns a new
actionmethod which does nothing.
bee, and a
duration. It then takes the bee's original action along with the "affected action" (the result of calling
effecton the original action) and replaces the bee's action with a new action method that will call the affected action for
durationturns and then will go back to calling the original action every turn.
Make sure to test your code! Your code should be able to apply multiple effects on a target (each new effect applies on top of whatever action method the bee already has at that point, and the target returns to the previous action when the new one runs out).
You are now done with the project! If you weren't able to vanquish the bees' insane-mode assault plan before, do your new ants help? Add some water or design your own layout to keep things interesting.
Feel free to design additional ants, layouts, and assault plans and post them to Piazza.
Acknowledgements: Tom Magrino and Eric Tzeng developed this project with John DeNero. Jessica Wan contributed the artwork. Joy Jeng and Mark Miyashita invented the queen ant.