I need you to write four .java files: Chaser.java, Runner.java,
Random.java, and Custom.java that produce the characteristics given
below. There is a base class Creature, with source code in
Creature.java. Four classes derived from Creature shall be as follows:
Notes for CreatureWorld.java
This program exhibits the behavior of 4 types of "creatures" as they
interact with each other in a world consisting of a region on your
terminal screen. It can be thought of as a simple "artificial life"
simulation of the interaction of these creatures in their small world.
To keep it simple, the interaction between the creatures is restricted
in a way that is described below. Each creature is implemented as a
Java object, and so it is an instance of some Java class.
The code I have already written is below. They are the files
CreatureWorld.java and Creature.java. When writing the code please try
to keep at a level at which I can understand. I do not consider
myself an advanced programmer and want to be able to interpret the
code given to me instead of just having a program that compiles.
a. The "Chaser" class. A Chaser tries to catch other creatures. When
it gets a chance to interact with another Creature, a Chaser moves to
a location which, of all its neighboring locations on the screen, is
nearest to the other creature. A Chaser creature appears on the screen
as a red filled circle 15 pixels in diameter, with its center at the
Chaser's location.
b. The "Runner" class. A Runner tries to escape from other creatures.
In each interaction with another creature, a Runner moves to a
location which, of all its neighboring locations, is farthest from the
other creature. A Runner creature appears on the screen as a green
filled square 15 pixels on a side, with its center at the Runner's
location.
c. The "Random" class. A Random doesn't care about other creatures.
When it gets a chance to interact with another creature, a Random
creature moves to a randomly selected location, no matter where the
other creature is. The new location must be no more than 20 pixels
horizontally and 20 pixels vertically of its previous location. A
Random creature appears on the screen as a blue 'X', with the center
of the X at the Random's location. The 'X' should be 15 pixels high
and 15 pixels wide.
d. Here you get to use your imagination. A Custom creature can respond
to the other creature in any way you like, as long as the way it does
this is different from what the other 3 types of creature do, and as
long as it does something. In addition, it can display itself in any
way you like, as long as it is different from the other 3 types, and
as long as it displays visibly, at its current location. Within these
constraints, anything is fine, so have fun.
In any case, no creature (even a Custom one) should ever move outside
the boundaries of the "world"; when it gets to an edge, it should move
as best it can within the constraints of the boundary. (This must be
true even if the Creature it is reacting to is somehow outside the
boundary)
When the program runs, the user is given the opportunity to select the
types of Creature to observe, their initial location, and how many
steps to run the simulation. To exit the simulation, the user can
close the application window.
The base class constructor in Creature.java may handle all of the
initialization you need; but since constructors are not inherited, you
have to define your own in the derived classes. The constructor must
take 2 int arguments, the initial x and y coordinates of the Creature.
Override the reactTo() and paint() methods to do the appropriate thing
for each derived class; the ones in the base class are abstract, so do
not include method bodies. (the base class instance variables have
protected visibility, so they are directly accessible within methods
of derived classes.) These methods will be called from the
CreatureWorld application, after the user clicks the START button to
begin the simulation. The basic animulation loop in CreatureWorld
looks like this (some pseudocode here):
// loop for the right number of animation steps
for(int step = 1; step<=steps; step++) {
// get a Rectangle specifying the size of the "world"
Rectangle r = Rectangle_For(world);
// react each pair of Creatures in order of their indices
int nCreatures = Number_Of_Creatures();
for(int ic1 = 0; ic1 < nCreatures; ic1++) {
for(int ic2 = 0; ic2 < nCreatures; ic2++) {
// make Creature indexed ic1 move in reaction to
// creature indexed ic2; but don't bother if ic1 == ic2
if (ic1 == ic2) continue;
Creature c1 = Creature_Indexed(ic1);
Creature c2 = Creature_Indexed(ic2);
c1.reactTo(c2,r);
}
}
// display the Creatures at their new location
Graphics g = Graphics_For(world);
for(int ic = 0; ic < nCreatures; ic++) {
Creature c1 = Creature_Indexed(ic);
c1.paint(g);
}
}
The loop structure ensures that each Creature gets to reactTo() every
other one before the result of each animation step is displayed; but
the order in which these reactions take place depends on the order in
which the Creatures were added, and different behaviors can happen as
a result. If there are more than 2 Creatures involved, their
interactions can be hard to predict (that's why simulations are
useful).
To implement the paint() method, note that a java.awt.Graphics object
is passed as argument. The Graphics class defines lots of instance
methods for doing graphics. Here are some suggested prototypes for
some of the methods that I would prefer you to use:
public void drawLine(int x1, int y1, int x2, int y2);
public void drawOval(int x, int y, int width, int height);
public void drawRect(int x, int y, int width, int height);
public void drawString(String str, int x, int y);
public void fillOval(int x, int y, int width, int height);
public void fillRect(int x, int y, int width, int height);
public void setColor(Color c);
Note that the x, y location for rectangles and ovals is the upper left
"corner" of the rectangle or oval.
Make the reactTo() method take two arguments: a Creature to react to,
and a Rectangle that specifies the limits of allowed movement. The
Creature must not move outside that Rectangle. Rectangle is a class in
the java.awt package. It is a somewhat unusual class, in that its
instance variables are public:
public int x; // The x coordinate of the upper left corner of the
rectangle.
public int y; // The y coordinate of the rectangle.
public int width; // The width of the rectangle.
public int height; // The height of the rectangle.
Rectangle also defines many instance methods. One you may find useful
has this prototype:
//Checks whether this rectangle contains the point at the specified
location (x, y)
public boolean contains(int x, int y)
The hard part is coming up with an algorithm for finding a "best"
position for a Chaser or a Runner creature to move to, when it reacts
to another creature. Here's a hint for one possible way to do this. A
creature has a certain x,y location on the "world Canvas". The
neighboring locations are the 9 pixels with coordinates
x-1,y-1 x,y-1 x+1,y-1
x-1,y x,y x+1,y
x-1,y+1 x,y+1 x+1,y+1
So, all combinations of offsets from the current x and y coordinates
can be tried with a doubly-nested for loop. You can pick the one that
is "best" according to whether the creature wants to be closer or
farther from the location of the other creature (but remember to stay
within the boundaries of the Rectangle passed to the reactTo()
method).
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
/***********************************************************
* class CreatureWorld
* CreatureWorld extends JFrame to create a GUI "artificial life"
simulation.
***********************************************************/
public class CreatureWorld extends JFrame {
/**
* Initialize the application JFrame.
*/
public CreatureWorld() {
super("Creature World"); // initialize JFrame, with a title
// set size and location of the application JFrame
setBounds(APPLOC_X,APPLOC_Y,APPFRAMEWIDTH,APPFRAMEHEIGHT);
// get the JFrame's contentPane, to add JComponents to
Container thisPane = getContentPane();
// create panel for North region of Frame
// holds messageLabel
JPanel northPanel = new JPanel();
JPanel messagePanel = new JPanel();
messageLabel = new JLabel("Welcome to the Creature World! Place
Creatures, then START");
northPanel.add(messageLabel);
thisPane.add(northPanel,BorderLayout.NORTH);
// create panel for South region of Frame
// holds two further JPanels:
// one for steps JTextField and its associated JLabels,
// one for Creature placement buttons
JPanel southPanel = new JPanel();
southPanel.setLayout(new GridLayout(2,1));
// set up the panel for displaying steps info
JPanel stepsPanel = new JPanel();
stepsPanel.add(new JLabel("Steps:",JLabel.RIGHT));
stepsField = new JTextField(" 100");
stepsPanel.add(stepsField);
stepsPanel.add(new JLabel(" Steps done: ",JLabel.RIGHT));
stepsLabel = new JLabel("0 ",JLabel.LEFT);
stepsPanel.add(stepsLabel);
// set up the panel for displaying Creature placement buttons
// ActionListener for a Creature placement button will just
// call startCreaturePlacement(ActionEvent e)
ActionListener startCreaturePlacement =
new ActionListener() { // anon. inner class
public void actionPerformed(ActionEvent e) {
startCreaturePlacement(e); }};
JPanel buttonPanel = new JPanel();
buttonPanel.add(new JLabel("Creature types: "));
JButton chaserButton = new JButton("Chaser");
JButton chaserButton = new JButton("Chaser");
chaserButton.setBackground(Color.red);
buttonPanel.add(chaserButton);
chaserButton.addActionListener(startCreaturePlacement);
JButton runnerButton = new JButton("Runner");
runnerButton.setBackground(Color.green);
buttonPanel.add(runnerButton);
runnerButton.addActionListener(startCreaturePlacement);
JButton randomButton = new JButton("Random");
randomButton.setBackground(Color.blue);
buttonPanel.add(randomButton);
randomButton.addActionListener(startCreaturePlacement);
JButton customButton = new JButton("Custom");
buttonPanel.add(customButton);
customButton.addActionListener(startCreaturePlacement);
southPanel.add(buttonPanel); southPanel.add(stepsPanel);
thisPane.add(southPanel,BorderLayout.SOUTH);
// put Start button in East region
JButton startButton = new JButton("START");
thisPane.add(startButton,BorderLayout.EAST);
startButton.addActionListener(new ActionListener () {
public void actionPerformed(ActionEvent e) {
doSimulation();
}});
// put Clear button in West region
JButton clearButton = new JButton("CLEAR");
thisPane.add(clearButton,BorderLayout.WEST);
clearButton.addActionListener(new ActionListener () { // anon.
inner class
public void actionPerformed(ActionEvent e) { // implement
actionPerformed
doClear(); // to call our private method
}});
// create the world JPanel. Use an anonymous inner class to
// override the paintComponent() method to display such Creatures
as
// may exist
world = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g); // superclass version clears
background
// Ask each Creature to draw itself
for(int i=0; i<creatureVec.size(); i++) {
Creature c = (Creature) creatureVec.get(i);
c.paint(g);
}
}
};
world.setBackground(Color.white);
world.setSize(WORLDWIDTH, WORLDHEIGHT);
// the world gets a MouseListener
world.addMouseListener(new MouseAdapter() { // anon. inner class
public void mouseClicked(MouseEvent e) { // override
mouseClicked
handleMouseClicked(e); // to call our private method
}});
thisPane.add(world,BorderLayout.CENTER);
// give the overall Frame a WindowListener to handle closing
events
addWindowListener(new WindowAdapter() { // anonymous inner class
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}});
}
// a mouse clicked event handler for the world: place a Creature
private void handleMouseClicked(MouseEvent e) {
// if we're not placing a Creature, explain and return
if(placeCreatureType == NONE) {
announce("Select a Creature type first, then click to place!");
return;
}
// okay, now we're placing a kind of Creature.
// get the x,y coordinates of the mouse click
int x = e.getX(); int y = e.getY();
// pointer to the new creature we will create
Creature newCreature = null;
// create a new Creature of the appropriate type
switch(placeCreatureType) {
case CHASER:
newCreature = new Chaser(x,y);
break;
case RUNNER:
newCreature = new Runner(x,y);
break;
case RANDOM:
newCreature = new Random(x,y);
break;
case CUSTOM:
newCreature = new Custom(x,y);
break;
default:
System.err.println("Shouldn't happen! Bad creature type: " +
placeCreatureType);
System.exit(-1);
}
// put it in the creatures vector
creatureVec.add(newCreature);
// display the new creature, and finish up
world.paintImmediately(world.getVisibleRect());
// done placing
placeCreatureType = NONE;
setCursor(Cursor.getDefaultCursor());
announce("Place another creature, or push START.");
}
// user wants to place a Creature
private void startCreaturePlacement(ActionEvent e) {
// we use CROSSHAIR_CURSOR to indicate placement mode
setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
String label = e.getActionCommand();
// user wants to place a Chaser
if(label.equals("Chaser")) {
placeCreatureType = CHASER;
announce("Click to place a Chaser...");
}
// user wants to place a Runner
if(label.equals("Runner")) {
placeCreatureType = RUNNER;
announce("Click to place a Runner...");
}
// user wants to place a Random
if(label.equals("Random")) {
placeCreatureType = RANDOM;
announce("Click to place a Random...");
}
// user wants to place a Custom
if(label.equals("Custom")) {
placeCreatureType = CUSTOM;
announce("Click to place a Custom...");
}
}
// user wants to clear the world of Creatures
private void doClear() {
// get rid of current Creatures
creatureVec.clear();
// indicate we are not in creature-placement mode
placeCreatureType = NONE;
setCursor(Cursor.getDefaultCursor());
stepsLabel.setText("0");
// clear the world display
world.paintImmediately(world.getVisibleRect());
announce("Welcome to the Creature World! Place some Creatures,
then START");
}
private void doSimulation() {
if (creatureVec.size() < 2) {
announce("You need at least 2 creatures. Please place more!");
return;
}
int steps = 0;
// read the text in the stepsField to find how many steps in the
animation
try{
steps = Integer.parseInt(stepsField.getText().trim());
} catch (NumberFormatException ex) {
announce("Formatting error in Steps text field. Try again");
return;
}
announce("Running...");
// loop for the right number of animation steps
for(int step = 1; step<=steps; step++) {
// get a Rectangle specifying the size of the "world"
Rectangle r = new
Rectangle(world.getSize().width,world.getSize().height);
// react each pair of Creatures
for(int ic1 = 0; ic1 < creatureVec.size(); ic1++) {
for(int ic2 = 0; ic2 < creatureVec.size(); ic2++) {
// make Creature indexed ic1 move in reaction to
// creature indexed ic2; but don't bother if ic1 == ic2
if (ic1 == ic2) continue;
Creature c1 = (Creature) creatureVec.get(ic1);
Creature c2 = (Creature) creatureVec.get(ic2);
c1.reactTo(c2,r);
}
}
// display the Creatures at their new location
world.paintImmediately(r);
// show how many steps we've done
stepsLabel.setText(Integer.toString(step));
// guess we have to explicitly repaint the JLabel..?
stepsLabel.paintImmediately(stepsLabel.getVisibleRect());
// sleep for a short interval so everything doesn't happen at
once
try {
Thread.sleep(msBetweenFrames);
} catch (Exception ex) {}
}
// done!
announce("Click START to continue, CLEAR to start over");
}
// print a string in the messageLabel
private void announce(String s) {
messageLabel.setText(s);
}
// just paint the world with the background color
private void clearWorld() {
int width = world.getSize().width;
int height = world.getSize().height;
world.getGraphics().clearRect(0,0,width,height);
}
// named constants corresponding to the Creature types
public static final int NONE = 0;
public static final int CHASER = 1;
public static final int RUNNER = 2;
public static final int RANDOM = 3;
public static final int CUSTOM = 4;
// initial dimensions of the application frame
private static final int APPFRAMEWIDTH = 600;
private static final int APPFRAMEHEIGHT = 400;
// initial location of the application frame on the display
private static final int APPLOC_X = 50;
private static final int APPLOC_Y = 50;
// initial dimensions of the world panel
private static final int WORLDWIDTH = 400;
private static final int WORLDHEIGHT = 300;
// milliseconds to pause between frames of the animation
private int msBetweenFrames = 60;
private JPanel world;
private Vector creatureVec = new Vector();
private int placeCreatureType = NONE;
private JTextField stepsField;
private JLabel stepsLabel;
private JLabel messageLabel;
/**
* Start the CreatureWorld application
*/
public static void main(String args[]) {
(new CreatureWorld()).setVisible(true);
}
}
import java.awt.*;
/***********************************************************
* class Creature
* Creature is a base class intended to be subclassed to
* model various kinds of creatures. See the README for details.
***********************************************************/
public abstract class Creature {
/**
* Initialize a Creature with a x,y location.
*/
protected Creature(int x, int y) {
this.x = x;
this.y = y;
}
/**
* Change this Creature's location to respond to
* the other Creature.
* This method is abstract; it
* must be overridden in derived classes. The Creature must
* never move outside the limits of the given Rectangle.
* @param other The other creature.
* @param world A rectangle that specifies the limits of the world.
*/
public abstract void reactTo(Creature other, Rectangle world);
/**
* Display the Creature at its current location on the
* given Graphics object. This method is abstract; it
* must be overridden in derived classes
* @param g The Graphics object to use for display.
*/
public abstract void paint(Graphics g);
/**
* return the current x coordinate of the Creature's location.
*/
public final int getLocationX() { return x; }
/**
* return the current y coordinate of the Creature's location.
*/
public final int getLocationY() { return y; }
/** return, as a double, the distance in pixels
* between this Creature and another.
*/
public double distanceTo(Creature other) {
double dx = this.x - other.x;
double dy = this.y - other.y;
return Math.sqrt(dx*dx + dy*dy);
}
/** return, as a double, the angle in radians
* between this Creature and another.
*/
public double angleTo(Creature other) {
double dx = this.x - other.x;
double dy = this.y - other.y;
return Math.atan2(dy,dx);
}
protected int x; // current x coordinate of Creature
protected int y; // current y coordinate of Creature
} |