// // SimObject.java // import java.util.Random; import java.util.Enumeration; import java.util.Vector; import java.awt.Color; import java.awt.Graphics; abstract public class SimObject implements Runnable { // OVERVIEW: A SimObject is an object than represents a simulator object. // It has a grid (a circular reference to the grid containing this object), // location (integer row and column), initialization state (boolean // that is true if it has beein initialized), and thread pause state (boolean that // is true if the thread is paused). // A typical SimObject is < grid, row, col, initialized, paused >. // // Note: SimObject is an abstract class. The executeTurn method must // be implemented by subclasses. // Constants (in milliseconds) static final int TURN_DELAY = 500; static final int TURN_RANDOM = 50; // Rep: protected Grid grid; //@ghost public boolean isInitialized; private boolean isPaused; protected boolean dead; protected int hp; protected boolean destroyable = true; // subclasses have access to mrow and mcol protected int mx; protected int my; /*@non_null@*/ private Random random; // Used to get random numbers. // AF(c) = < grid, mrow, mcol, isInitialized, isPaused > // RI(c) = // if isIntialized then // grid != null // && grid.validLocation (mx, my) // && grid.getObjectAt (mx, my) == this //@invariant isInitialized ==> grid != null // If isInitialized is true, the grid is not null. public SimObject() // EFFECTS: Creates a new, uninitalized SimObject. //@ensures !isInitialized { dead = false; random = new Random(); hp = 1; } public void setDead(boolean b){ dead = b; } synchronized public void decreaseHP(int n){ //Modifies: this //Effects: If this is destroyable, decrease hp of this by n. // And if this_post.hp <= 0, die() if(destroyable){ hp = hp-n; if(hp<=0){ die(); } } } synchronized public void increaseHP(int n){ //Modifies: this //Effects: decrease hp of this by n. And if hp_post <= 0, die() hp = hp+n; } synchronized public void die(){ //Modifies: this //Effects: remove this from the grid setDead(true); grid.removeObjectAt(getX(),getY()); } final public void delay(int ms) // EFFECTS: Stalls for ms milliseconds. { try { Thread.sleep(ms); } catch (InterruptedException ie) { ; } } //@requires isInitialized public void executeTurn(){ // EFFECTS: Executes one turn for this object. If the SimObject is dead, // make the thread sleep for an hour. if(dead){ delay(3600000); //you go sleep for an hour } } public Color getColor() //@ensures \result != null; // EFFECTS: Returns the color that represents this SimObject, for // painting the grid. { return Color.green; } //@nowarn Post // ESC/Java spec doesn't know that Color.green is not null. final public Grid getGrid() // REQUIRES: init has previously been called for this. // EFFECTS: Returns the grid containing this cell. // NOTE: The rep is exposed. The Grid associated with a SimObject is part of the data abstraction. //@requires isInitialized; //@ensures \result != null; { return grid; } //@ensures \result.elementType == \type(SimObject) //@ensures \result != null; //@requires isInitialized; synchronized public Enumeration getNeighbors() { //@nowarn Deadlock // REQUIRES: The grid must be locked as long as the result of getNeighbors is used. // Otherwise, the neighbors could change if another object moves! // EFFECTS: Returns an Enumeration that yields all the objects adjacent to this // in the grid. Vector objects = new Vector(); //@set objects.elementType = \type(SimObject) //@set objects.containsNull = false for (int x = mx - 1; x <= mx + 1; x++) { for (int y = my - 1; y <= my + 1; y++) { if (x != mx || y != my) { // don't count yourself if (grid.validLocation(x, y)) { try { SimObject obj = grid.getObjectAt(x, y); if (obj != null) { objects.addElement(obj); } } catch (Exception e) { throw new RuntimeException ("BUG: Invalid exception: " + e); } } } } } return objects.elements(); } //@nowarn Exception final public int getX() // REQUIRES: this is initialized. // EFFECTS: Returns the x location that this cell is located in. //@requires isInitialized; { return mx; } final public int getY() // REQUIRES: init has previously been called for this. // EFFECTS: Returns the y location that this cell is located in. //@requires isInitialized; { return my; } public void setX(int x){ this.mx = x; } public void setY(int y){ this.my = y; } final public void init(int x, int y, Grid grid) // REQUIRES: init has not previously been called for this. // The cell is at (row, col) on grid. // MODIFIES: this // EFFECTS: Initializes the cell at row, col on grid with isPaused = true. // NOTE: This method is final, that means subtypes cannot override this method. //@requires !isInitialized; //@requires grid != null; //@ensures isInitialized; { this.mx = x; this.my = y; this.grid = grid; this.isPaused = true; Thread thread = new Thread(this); if(this instanceof UserTank){ thread.setPriority(Thread.MAX_PRIORITY-1); }else if(this instanceof Shell){ thread.setPriority(Thread.MAX_PRIORITY); }else if(this instanceof ComputerTank){ thread.setPriority(Thread.MIN_PRIORITY); }else { thread.setPriority(Thread.MIN_PRIORITY); } thread.start(); //@set isInitialized = true; } public boolean isPaused() // EFFECTS: Returns the value of paused. { return isPaused; } public void pauseObject() // EFFECTS: Sets paused to true. { isPaused = true; } public void resumeObject() // EFFECTS: Sets paused to false. { isPaused = false; } public void run() // REQUIRES: this has been initialized //@also_requires isInitialized // We use also_requires instead of requires, because we are adding a precondition // to an inherited method. This violates the substitution principle --- subtypes // should make preconditions weaker, not stronger. // EFFECTS: The object thread. If the object is not paused, executes one turn by calling // the executeTurn method, and sleeps for a time and repeats. If the object is paused, // does nothing (until the object is unpaused). { while (true) { if (!isPaused) { executeTurn(); } delay(random.nextInt(TURN_RANDOM)); } } public void drawImage(Graphics g,int x, int y, int w, int h){ } }