The purposes of this assignment are:
- To practice implementing classes
- To practice implementing interfaces
- To learn how to work within a much larger program
- To have fun creating a competitive critter.
Thanks to Marty Stepp, Stuart Reges, and Steve Gribble for sharing this assignment with me.
Program Overview:
You will complete a set of classes that define the behavior of various
animals or critters. You will be provided with several classes that
implement a graphical simulation of a two dimensional world with many
animals of different types moving around on it. Different kinds of animals
move in different ways. As you implement the required classes you are
defining those differences.
During each round of the simulation each critters is asked which
direction it wants to move. A critter can move one square to the north,
east, south, or west or the critter may choose to remain in its current
location. The world the critters are moving around in is of finite size, but
it is a torus,
like a doughnut; if a critter goes off the right edge of the 2d grid they
pop up on the left edge and vice versa. Likewise if a critter goes off the
south edge of the 2d grid they pop up on the north edge and vice versa. The
critter world is divided into cells that have integer coordinates like the
2d arrays we studied in class. The upper left cell has coordinates (0,0). x
coordinates increase to the right and y coordinates increase as you go down.
The default size of the world is 60 cells across and 50 cells high, although
this could be altered.
Like the color histogram program you are not writing the main method, nor are you writing
the code that controls the simulation. Your code will not be in control of
the overall program execution. Also, you won't write the code that actually
creates the various critter objects. Instead you are implementing a series of
classes to specification ("to 'spec' ") and the existing code will use the
classes you implement. The existing classes are clients of the classes you
will be implementing. As we talked about in class this is how most programs
are written. They are not written by a single person, rather by a team of
people.
You may find yourself wanting to move your critter several spaces at
once, but this is not possible. The only way a critter can move is if the
simulator asks the critter to move and the critter simply responds with the
direction it wants to move. The simulator will then move the critter one
space in the appropriate direction. This is a good introduction to the kind
of programming that is done with objects and if you move on to the follow
class, CS307, you will be doing a lot of programming in this style. (If you
want to move several spaces you will have to have your class remember what
its next move should be so it can tell the simulator the next time it asks.)
As the simulation runs critters can collide with each other by moving
into the same location. Again, the simulator takes care of all of this, not
the critters. When two critters enter the same cell they fight to the death
in a deadly game of rock-paper-scissors. The winning animal survives and the
losing animal is removed from the simulation. (Again, most of this is
handled by the simulation, not by the critters.) Critters simply pick a value for rock, paper, or scissors
when the simulator asks them. The outcome of the rock paper scissors duels
are:
| |
|
|
Critter #2 |
|
| |
|
ROCK |
PAPER |
SCISSORS |
| |
ROCK |
random winner |
#2 wins |
#1wins |
| Critter #1 |
PAPER |
#1 wins |
random winner |
#2 wins |
| |
SCISSORS |
#2 wins |
#1 wins |
random winner |
Provided files:
There are multiple support files you will have to download from the
assignment web page.
The main method is in the CritterMain class. Run the main
method in this class to run a simulation.
Each of the four classes you complete will implement the following
provided Critter interface.
public interface Critter {
// methods to be implemented
public int fight(String opponent);
public Color getColor();
public int getMove(CritterInfo info);
public String toString();
// constants for directions
public static final int NORTH = -2;
public static final int SOUTH = 4;
public static final int EAST = 3;
public static final int WEST = 19;
public static final int CENTER = 11;
// constants for rock-paper-scissors game
public static final int ROCK = 28;
public static final int PAPER = -10;
public static final int SCISSORS = 55;
} |
Recall, as we discussed in class, that an interface is a plan, a blueprint.
You can review chapter 9 of the book for more details. Recall each of your
classes will have to include a clause in the class header to indicate they
are implementing the Critter interface.
public class Bear implements Critter{ ...
You will have to provide a working version of the 4 methods specified in
the Critter interface. The details of these are different for each critter.
One critter class has already been completed, the Stone class.
Stones are not very active critters. Stones are displayed with the letter S
via the toString method, they are the color gray, they always
stay at their current location and never try to move, and they play rock -
paper - scissors by always picking rock. Here is the Stone
class in its entirety. Notice Stone objects don't have any fields (a.k.a.
instance variables), because
they never do anything different.
import java.awt.Color;
public class Stone implements Critter {
// the Stone class doesn't need any fields because Stone
objects
// always do the same thing
public int fight(String opponent) {
// good ol' rock... nothing beats
that!
return ROCK;
}
public Color getColor() {
// gray
return Color.GRAY;
}
public int getMove(CritterInfo info) {
// does not move
return CENTER;
}
public String toString() {
// displays stone as S
return "S";
}
} |
Notice the Critter interface defines five constants for the
various directions and three more constants for the three choices in the
rock - paper - scissors game. You can refer to these constant directly in
your code (NORTH, SOUTH, ROCK, etc.)
because you are implementing the interface. Alternatively you could refer to
them via the interface name. (Critter.NORTH,
Critter.PAPER, etc.)
Your code should not depend on the specific values assigned to these
constants, although you may assume they will always be of type int. In
other words a simulation may be run where Critter.NORTH is not
equal to -2. A critter can stay in its current location by returning
CENTER from its getMove method.
Critters to implement:
Here is where you actually get to write code! You will complete four
critter classes. Each class must have only one constructor and that
constructor must accept exactly the parameter(s) described below. For
random moves each possible choice must be equally likely. You may either use
an object of type Random or the Math.random()
method to obtain pseudorandom numbers.
Bear:
| constructor |
public Bear() |
| fight behavior |
always scissors |
| color |
brown. To do this you will have to create a new Color
object using a Color constructor. A decent brown is (red = 128, green =
128, blue = 64). For example, Color c = new Color(128, 128, 64); |
| movement behavior |
alternates between north and west in a zig-zag pattern.
(first north, then west, then north, then west, ...) |
| toString |
B |
Lion:
| constructor |
public Lion(int steps) |
| fight behavior |
always paper |
| color |
yellow |
| movement behavior |
moves the given number of steps in a random direction
(north, east, south, or west) then chooses a new random direction and
repeats. Here is one of those times you will want to move many steps,
but you can only move one. You will have to track how many steps you
have taken in the current direction. |
| toString |
L |
The Lion constructor accepts a parameter representing the
distance or number of steps a particular Lion will walk in a
straight line before choosing a new, random direction. For example, a
Lion constructed with a parameter value of 8 will uses its first 8
moves to walk in a single random direction. Then after the 8th move the
Lion will choose a new random direction (which could be the
same as the original one, and repeat the process. Recall the whole purpose
of fields in classes and objects is to remember things!
Tiger:
| constructor |
public Tiger(Color c) |
| fight behavior |
alternate between scissors and paper. (first
scissors, then paper, then scissors, then paper ... ) |
| color |
the color passed to the constructor |
| movement behavior |
first 5 steps south, then 5 steps west, then
5 steps north, then 5 steps east. (a clockwise square pattern, one step
at a time), then repeats |
| toString |
T |
The Tiger constructor accepts a parameter representing the
color in which that given Tiger should be drawn. Different
Tigers could be different colors. This color should be returned
each time the getColor method is called on a given Tiger.
For example, a Tiger object constructed with a parameter value
of Color.RED will return that color from its getColor
method and will therefore appear red on the screen.
Wolf:
| constructor |
public Wolf() |
| fight behavior |
you decide |
| color |
you decide |
| movement behavior |
you decide |
| toString |
you decide, but it must be a single
character |
You get to decide the behavior of the objects of type Wolf
and thus how to implement the Wolf class. Your constructor in
the Wolf class must accept no parameters as shown above.
More on the Wolf class:
Part of your grade on this assignment will be based on implementing
creative and non-trivial behavior in your Wolf class. Here are
some hints and ideas.
Each time a critter is asked to move its getMove method is
called by the simulation. Look at the header of the getMove
method from the Critter interface.
public int getMove(CritterInfo info);
The getMove method is passed an object of type
CritterInfo that provides useful information about the area of the
world the critter is in. None of the other critters (Stone, Bear,
Lion, Tiger) use this information in any way, but your Wolf
class can. You may wish to use the information in the CritterInfo
object to guide a Wolf objects behavior. For example. you can
find out the wolf's current x and y coordinates by calling the getX
and getY methods, while the getWidth and
getHeight methods return the size of the simulation world. You can
find out what is around the wolf by calling the getNeighbor
method and passing one of the direction constants as a parameter. The
getNeighbor method returns the display character (as a String
object) for whatever is in that location. A period, ".",
indicates an empty cell. Here are the methods you may call on the
CritterInfo object:
public interface CritterInfo {
// Returns the height of the world.
public int getHeight();
// Takes a direction as a parameter (one of the constants
NORTH,
// SOUTH, EAST, WEST or CENTER from the Critter interface).
It
// returns the display character for the critter that is one
unit away
// in that direction (or "." if the square is empty).
// If multiple critters occupy this space, returns a randomly
chosen
// one of them.
public String getNeighbor(int direction);
// Returns the width of the world.
public int getWidth();
// Returns a critter's current x-coordinate in the world.
public int getX();
// Returns a critter's current y-coordinate in the world.
public int getY();
} |
Also notice the fight method provides a String
that is the display character for the other opponent in the fight. Again,
none of the other critter classes use this information in any way, but your
Wolf class can.. For example if the opponent equals "B" it is
likely you are fighting a Bear (or some other, crafty wolf...)
Your Wolf can return any String of length 1 from its
toString method. and any color you like from its getColor
method. Critters are asked what display color and display String
to use on each round of the simulation, so you can have a Wolf
that displays itself differently over time. Keep in mind that the
toString text is also passed to other animals when they fight your
Wolf. You may wish to develop a strategy to fool other animals
(not shown here or other wolves) in the simulation.
Implementation Guidelines:
Download all the required files and add them to DrJava folder. The
provided GUI and simulation can run even if you haven't completed all of the
required critter classes. To do this, edit the file CritterMain.java
and comment out lines that refer to critter types that you have not
completed. For example, commenting out the following line would prevent the
simulation from trying to place any Tiger objects.
model.add(NUM_START, Tiger.class);
I recommend commenting out all the critters in the main
method of CritterMain except Stone and running the
program to make sure you have downloaded all the file and have set them up
correctly. Then implement the Critter classes one at a time, testing after
each one is complete. I recommend going from Bear to Lion
to Tiger to Wolf. A critter class will not compile
without having implementations of all the methods from the Critter
interface. It is a good idea to test each method after you write it before
going on to the next method. You can do this by creating "stub" versions of
the methods you are not done with. The stub method returns a place holder
value until you replace it with the correct statements. For example, your
could create a stub of fight for the Tiger that always
returns Scissors while working on the Tiger's
getMove behavior. When the getMove behavior is
done and tested go back to the fight method and implement it
correctly for Tigers.
For each critter you implement it will be
impossible to implement correctly if you do not have the necessary fields to
track the state of the object. Before trying to write the code for each
class figure out what fields will be necessary for that critter to achieve
the desired behavior. Do this before you start typing code. (Don't fiddle!
Think first!)
It is sometimes hard to tell from the simulation if a critter is behaving
correctly, particular the fight behavior. You can test this by using
println statements. For example in the Tiger class you could have a
println showing what will be returned. You may want to alter
the CritterMain class to add a single Tiger and look at its
fight behavior via the println statements. Does it alternate
between scissors and paper correctly?
Style Guidelines and grading:
Some of the points for this assignment will be awarded on the basis of
how much energy and creativity you put into creating an interesting and
effective Wolf class. Style points will also depend on the basis to simply
and clearly implement each critter's required behavior.
Turn in your files named Bear.java, Lion.java, Tiger.java, and Wolf.java using the
turnin program.
|