import java.awt.Point; /** Extend this class to create your own Creature. <p>Override the {@link #run} method to implement the Creature's AI. Within that method the following actions are available (each of which takes some time steps to execute): <ul> <li> {@link #moveForward} <li> {@link #moveBackward} <li> {@link #turnLeft} <li> {@link #turnRight} <li> {@link #attack} <li> {@link #look} </ul> </p> <p>Example: <pre> <font color=888888> public class Rover extends Creature { public void run() { while (isAlive()) { </font> if (! moveForward()) { attack(); turnLeft(); } <font color=888888> } } } </font></pre> </p> <p>Each creature executes in its own thread. This means that you must be very careful when using static fields to always used threadsafe classes or to access them through synchronized accessor methods.</p> <p>Morgan McGuire <br>morgan@cs.williams.edu</p> */ // Most methods that will be called by subclasses are synchronized. // This is intended to help prevent a Creature that spawns Threads // from corrupting itself. public abstract class Creature implements Entity, Runnable { private int id; private Simulator simulator; private Point position; private Direction direction; /** Called by creature operations to enforce the time cost of those operations. Can also be called to make a creature sit still for "n time steps". */ final protected void delay(int n) { simulator.delay(n); } final public Type getType() { return Type.CREATURE; } /** Returns the number of time steps since the simulation started */ final public int getTime() { return simulator.getTime(); } /** Subclass constructors must not invoke any of the parent class methods from their constructor. Instead, perform initialization at the beginning of the run() method. */ protected Creature() { } /** Name of this species of creature. */ final public String getClassName() { return getClass().getName(); } /** Allows GUI browsers to display your name as author of this creature.*/ public String getAuthorName() { return "Anonymous"; } /** Allows GUI browsers to display information and credits about your creature.*/ public String getDescription() { return "Override getDescription to display information about your Creature subclass."; } /** Each creature has a unique number that it can use to distinguish itself from others. The id is not valid until run() is called on the creature; do not reference it from the constructor.*/ final public int getId() { return id; } /** Create an observation describing this creature */ public Observation observeSelf() { return new Observation(getPosition(), getClassName(), getId(), getDirection(), getTime()); } /** The coordinates of the next position this Creature will enter if it moves <i>n</i> times, regardless of whether that position is currently empty. (fast)*/ protected Point getMovePosition(int n) { return getDirection().forward(getPosition(), n); } /** Same as {@link #getMovePosition} with the argument <code>n = 1</code> */ protected Point getMovePosition() { return getMovePosition(1); } /** Returns true if this observation describes a Creature that is not of this species */ protected boolean isEnemy(Observation obs) { assert obs != null; return (obs.type == Type.CREATURE) && (! obs.className.equals(getClassName())); } /** Returns the manhattan distance from current position to obs. */ protected int distance(Point p2) { Point p = getPosition(); return Math.abs(p.x - p2.x) + Math.abs(p.y - p2.y); } /** Uses the pointer hashCode from Object.*/ final public int hashCode() { // Prevent users from overriding hashcode, which is used by // the simulator. return super.hashCode(); } /** Uses the pointer comparision from Object. */ final public boolean equals(Object ob) { // Prevent users from overriding equals, which is used by // the simulator. return super.equals(ob); } final public char getLabel() { return getClassName().charAt(0); } /** Override this method to make your creature think and move. Executes as soon as the creature is injected into the world. Your creature will stop moving when the run method ends (but stay alive), so most implementations will be an intentional infinite loop. If you want to know when your creature has been converted, test {@link isAlive} or attempt an action and catch the {@link ConvertedError}. */ public void run() { } /** Returns the position of this Creature. This is fast. If the Creature is dead, throws Error.*/ final public synchronized Point getPosition() { return simulator.getPosition(this); } /** Direction this creature is facing. This is fast. If the creature is dead, throws Error.*/ final public synchronized Direction getDirection() { return simulator.getDirection(this); } /** Returns true if this creature is alive. A creature that is not alive should allow its run method to exit. */ final public synchronized boolean isAlive() { return simulator.isAlive(this); } /** The simulation calls this on the creature when it is first added to the world. Do not invoke this yourself. */ final void setSimulator(Simulator s, int _id) { if (simulator != null) { throw new IllegalArgumentException("Cannot invoke setSimulation twice."); } simulator = s; id = _id; } /** Call to move your creature forward 1 square. If the creature is blocked, it will not move. Moving takes about {@link Simulator#MOVE_FORWARD_COST} time steps. @return true if the creature successfully moved forward.*/ synchronized protected boolean moveForward() { delay(simulator.MOVE_FORWARD_COST); return simulator.move(this, 1); } /** Call to move your creature backward 1 square without changing its facing direction. If the creature is blocked, it will not move. Moving takes about {@link Simulator#MOVE_BACKWARD_COST} time steps. @return true if the creature successfully moved forward.*/ synchronized protected boolean moveBackward() { delay(simulator.MOVE_BACKWARD_COST); return simulator.move(this, -1); } /** Look forward. Returns a description of the first non-empty square observed, which may be any distance away. <p>Takes about {@link Simulator#LOOK_COST} time steps. The result of the look is accurate at the end of the delay, but of course whatever is seen might have moved by the time the creature actually makes its response. */ synchronized protected Observation look() { delay(simulator.LOOK_COST); return simulator.look(this); } /** Rotate counter-clockwise 90 degrees. This takes {@link Simulator#TURN_COST} time steps. */ synchronized protected void turnLeft() { delay(simulator.TURN_COST); simulator.turnLeft(this); } /** Rotate clockwise 90 degrees. This takes {@link Simulator#TURN_COST} time steps. */ synchronized protected void turnRight() { delay(simulator.TURN_COST); simulator.turnRight(this); } /** Attack the creature right in front of you. If there is a creature of a different species present in that spot, that creature will be destroyed and a new creature of the same type as this one will be created in its place. The new creature will face in the opposite direction as this one; i.e., they will be face to face. <p>Whether there is a creature present or not, this takes about {@link Simulator#ATTACK_COST} time steps. The actual attack occurs at the <b>beginning</b> the delay. @return true if the attack succeeded. */ synchronized protected boolean attack() { boolean result = simulator.attack(this); delay(simulator.ATTACK_COST); return result; } }