import java.util.Random; import java.util.Enumeration; import java.util.Vector; import java.awt.Color; 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 = 200; // Rep: protected Grid grid; //@ghost public boolean isInitialized; private boolean isPaused; // 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 { random = new Random(); } final public void delay(int ms) // EFFECTS: Stalls for ms milliseconds. { try { Thread.sleep(ms); } catch (InterruptedException ie) { ; } } //@requires isInitialized abstract public void executeTurn(); // EFFECTS: Executes one turn for this object. 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; } 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); 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(TURN_DELAY + random.nextInt(TURN_RANDOM)); } } } //Class borrowed from Saturday Night Simulator and CS201J Website - PS5