14
\$\begingroup\$

After suggestions given in this question, modifications to the code has been done.

Only thing I could not do is to decide, where to place the starveTime property (in Shark class or somewhere in a common place) because starveTime is not specific to Shark, it is common to any eating creature (other fish), currently Shark persist eating creature.

Please review from OOPS aspect/memory usage/... aspect.

Here is the modified code.

/* point.java */

/**
 * The Point class defines a location (x,y) in the Ocean.
 * 
 * @author mohet01
 *
 */
public class Point {
    /**
     * Here top-left of Windows screen is considered as origin.
     * x is an x-coordinate of a location in an ocean 
     * y is an y-coordinate of a location in an Ocean
     */
    private int x;
    private int y;

    /**
     * Constructor creates a Point object below co-ordinates
     * @param x
     *          is an x-coordinate of a Critter location in an Ocean
     * @param y
     *          is an y-coordinate of a Critter location in an Ocean
     */
    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }

    /**
     * This method returns the x-coordinate of Critter in an ocean
     * @return
     *          x-coordinate of a Critter location in an Ocean. 
     */
    public int getX(){
        return this.x;
    }

    /**
     * This method returns the y-coordinate of Critter in an ocean
     * @return
     *          y-coordinate of a Critter location in an Ocean. 
     */
    public int getY(){
        return this.y;
    }

}

/* Critter.java */

/**
 * The abstract class Critter defines a base class for anything(which can be empty)
 * that can exist at a specific location in the ocean.
 * @author mohet01
 *
 */
public  abstract class Critter  {

    /**
     * Below data member defines a location of a Critter in an Ocean
     */

    Point location;


    public Critter(int x, int y){
        location = new Point(x,y);
    }

    public Point getLocation(){
        return location;
    }

    /**
     * This method computes the new value of location(which can be EMPTY) property of Critter.
     * No operation is performed as this is a base class.
     */
    public abstract Critter update(Ocean currentTimeStepSea);


}

/* Shark.java */

/**
 * The Shark class defines behavior of a Shark in an Ocean.
 * @author mohet01
 *
 */
public class Shark extends Critter{



    /**
     * Below data member is the number of simulation time steps that a Shark
     *  can live through without eating.
     */
    private static int starveTime;

    /**
     * Below data member specifies the hunger of each shark you add to the 
     * ocean.
     */
    private int hungerLevel;  


    /**
     * Constructor will create a new location for Shark
     * @param x
     *          is the x-coordinate of location(which can be EMPTY) of Shark
     * @param y
     *          is the y-coordinate of location(which can be EMPTY) of Shark
     */
    public Shark(int x, int y, int starveTime, int hungerLevel){
        super(x,y);
        //need to remove this below line and assign it when class is loaded
        starveTime = starveTime;
        //Sharks are well-fed at birth
        this.hungerLevel = hungerLevel;
    }

    /*
     * This method provides the starvation time of Shark creature
     */
    public static int getStarvationTime(){
        return starveTime;
    }
    /**
     * isSharkStarving() checks the hunger level of shark, if reached to starveTime level
     * @param x
     *              is the x-coordinate of the cell whose contents are queried.
     * @param y 
     *              is the y-coordinate of the cell whose contents are queried.
     * @return the boolean value
     */
    private boolean isSharkStarving(){
        return (this.hungerLevel == (starveTime+1));  
    }




    /**
     * This method updates the shark cell based on the behavior of it's
     * nearest neighborhood
     * @param currentTimeStepSea
     *                          Ocean in the current time step
     * @param nextTimeStepSea
     *                          Ocean that has to look like in next time Step 
     *                          
     */
    @Override
    public Critter update(Ocean currentTimeStepSea){

        boolean gotTheFish = false;
        //Check all the 8 neighbors of a Shark Cell for fish
        gotTheFish = Utility.checkFishAsNeighbor(this, currentTimeStepSea);

        //Updating Shark Cell
        if(gotTheFish){
            /*
             * 1) If a cell contains a shark, and any of its neighbors is a fish, then the
             * shark eats during the time step, and it remains in the cell at the end of the
             * time step.  (We may have multiple sharks sharing the same fish.  This is fine;
             * they all get enough to eat.)
             */
            return this; //return currentTimeStep Shark

        }else{
            /* 
             * 2) If a cell contains a shark, and none of its neighbors is a fish, it gets
             * hungrier during the time step.  If this time step is the (starveTime + 1)th
             * time step the shark has gone through without eating, then the shark dies
             * (disappears).  Otherwise, it remains in the cell.
             * 
             */
            this.hungerLevel++;
            if(isSharkStarving()){
                return new Empty(this.getLocation().getX(), this.getLocation().getY());
            }
            else{
                return this; //return currentTimeStep Shark
            }
        }



    }



}

/* Fish.java */

/**
 * The Fish class defines the behavior of a Fish in an Ocean
 * @author mohet01
 *
 */
public class Fish extends Critter{

    /**
     * Constructor will create a new location for Fish
     * @param x
     *          is the x-coordinate of location(which can be EMPTY) of Fish
     * @param y
     *          is the y-coordinate of location(which can be EMPTY) of Fish
     */

    public Fish(int x, int y){
        super(x,y);
    }


    /**
     * This method updates the Fish cell based on the behavior of it's
     * nearest neighborhood
     * @param currentTimeStepSea
     *                          Ocean in the current time step
     * @param nextTimeStepSea
     *                          Ocean that has to look like in next time Step 
     *                          
     */

    @Override
    public Critter update(Ocean currentTimeStepSea){

        int neighborSharkCount=0;
        neighborSharkCount = Utility.countSharkAsNeighbor(this, currentTimeStepSea);
        //Updating fish cell for current & next  time step
        if(neighborSharkCount ==1){
            /*
             * 4) If a cell contains a fish, and one of its neighbors is a shark, then the
             * fish is eaten by a shark, and therefore disappears.
             * 
             */
            return new Empty(this.getLocation().getX(),this.getLocation().getY());
        }
        else if(neighborSharkCount  > 1){
            /*
             *  5) If a cell contains a fish, and two or more of its neighbors are sharks, then
             * a new shark is born in that cell. Sharks are well-fed at birth; _after_ they
             * are born, they can survive an additional starveTime time steps without eating.
             * 
             */
            return new Shark(this.getLocation().getX(),this.getLocation().getY(),Shark.getStarvationTime(),0);

        }
        else {
            /*
             * condition is (neighborSharkCount  < 1)
             * 3) If a cell contains a fish, and all of its neighbors are either empty or are
             * other fish, then the fish stays where it is.
             */
            return this;
        }
    }


}

/* Empty.java */

/**
 * The Empty class defines itself as an entity as it has some meaning/significance
 * being empty in an Ocean. Check update() method for more meaning.
 * @author mohet01
 *
 */
public class Empty extends Critter{


    /**
     * Constructor will create a new location which is Empty
     * @param x
     *          is the x-coordinate of location which is Empty.
     * @param y
     *          is the y-coordinate of location which is empty.
     */

    public Empty(int x, int y){
        super(x,y);
    }

    /**
     * This method updates the Empty cell based on the behavior of it's
     * nearest neighborhood
     * @param currentTimeStepSea
     *                          Ocean in the current time step
     * @param nextTimeStepSea
     *                          Ocean that has to look like in next time Step 
     *                          
     */

    @Override
    public Critter update(Ocean currentTimeStepSea) {

        int neighborFishCount = 0;
        int neighborSharkCount = 0;

        //Check all the 8 neighbors of an Empty cell to count sharks and Fish
        neighborSharkCount = Utility.countSharkAsNeighbor(this, currentTimeStepSea);
        neighborFishCount = Utility.countFishAsNeighbor(this,currentTimeStepSea);

        //Update Empty Cell for next time step.


        if((neighborFishCount >= 2) && (neighborSharkCount <=1)){
            /*
             * 7) If a cell is empty, at least two of its neighbors are fish, and at most one
             * of its neighbors is a shark, then a new fish is born in that cell.
             * 
             */
            return new Fish(this.getLocation().getX(),this.getLocation().getY());

        }else if((neighborFishCount >= 2) && (neighborSharkCount >= 2)){
            /* 
             * 8) If a cell is empty, at least two of its neighbors are fish, and at least two
             * of its neighbors are sharks, then a new shark is born in that cell. (The new
             * shark is well-fed at birth, even though it hasn’t eaten a fish yet.)
             * 
             */
            return new Shark(this.getLocation().getX(),this.getLocation().getY(),Shark.getStarvationTime(),0);
        }
        else{
            /* 
             * 6) If a cell is empty, and fewer than two of its neighbors are fish, then the
             * cell remains empty.
             */
            return this;
        }


    }



}

/* Ocean.java */

/**
 * The Ocean class defines an object that models an ocean full of sharks and
 * fish.
 * @author mohet01
 *
 */
public class Ocean {

    /**
     * Define any variables associated with an Ocean object here. These
     * variables MUST be private.
     * 
     */
    //width of an Ocean
    private int width;
    //height of an Ocean
    private int height;

    /*
     * @Mat I preferred, 2d array of references to Critter objects
     * rather than List. Reasons(correct me),
     * 1) To display an array of ocean, it adds more logic in paint() method.
     * 2) Checking 8 nearest neighbors of each Critter looks inefficient,
     * For example: for an ocean of SEEFE  
     *                              FEEFE a 2x2 ocean, If i maintain
     * a list of Critter for this 2x2 ocean, i need to traverse 
     * S->E->E->F->E->F to get my first nearest neighbor of Shark,
     * In contrast, With 2d array, I would just use modulo operation as
     * mentioned in update() method.  Let us see what happens!!!
     *  
     */
    private Critter[][] oceanMatrix;


    /**
     * Constructor that creates an empty ocean with below dimension
     *  
     * @param width
     *            is the width of the ocean.
     * @param height
     *            is the height of the ocean.
     * 
     */
    public Ocean(int width, int height){
        this.oceanMatrix = new Critter[height][width];
        this.width = width;
        this.height = height;
        for (int row = 0; row < height; row++) {
            for (int col = 0; col < width; col++) {
                oceanMatrix[row][col] = new Empty(row,col);
            }
        }
    }


    /**
     * This method adds Critter in an ocean.
     * @param object
     *              is the Critter object to be added in Ocean.
     */
    public void addCritter(Critter object){
        Point p = object.getLocation();
        int x = p.getX();
        int y = p.getY();
        /*
         * @Mat I understand that, location property make sense to be be moved 
         * to corresponding Critter<type> class as it's property, which i did, But 
         * also captured location property of a Critter Object in Ocean class(with
         * above 3 lines of code) which is redundant and not relevant, But 2d array
         * is more efficient than list, for checking neighbor in update() method.
         * Are we Breaking SRS????
         * So, Instead of List am using 2d array. Let us see what happens!!!
         */
        oceanMatrix[x][y] = object;
    }


    /**
     * This method returns either Critter Object reference
     * 
     * @param x
     *            is the x-coordinate of the cell whose contents are queried.
     * @param y
     *            is the y-coordinate of the cell whose contents are queried.
     */
    public Critter cellContents(int x, int y) {
        return oceanMatrix[x][y];
    }


    /**
     * getWidth() returns the width of an ocean Object.
     * 
     * @return 
     *          the width of the ocean.
     * 
     */
    public int getWidth() {
        return this.width;
    }

    /**
     * getHeight() returns the height of an Ocean object.
     * 
     * @return
     *          the height of the Ocean.
     */

    public int getHeight() {
        return this.height;
    }





    /**
     * timeStep() performs a simulation time step as described in README.
     * 
     * @return
     *          an ocean representing the elapse of one time Step.
     */

    public Ocean timeStep() {

        Ocean nextTimeStepSea = new Ocean(width, height);

        for (int row = 0; row < this.height; row++) {
            for (int col = 0; col < this.width; col++) {
                Critter creature = this.cellContents(row, col);
                nextTimeStepSea.addCritter(creature.update(this));
            }
        }
        return nextTimeStepSea;
    }





}

/* Utility.java */

/**
 * The Utility class provides some utility functions which are used
 * by multiple classes like Fish, Shark etc...
 * @author mohet01
 *
 */
public final class Utility {


    /**
     * Don't let anyone instantiate this class.
     */
    private Utility() {}




    /**
     * This method checks the existence of at-least one fish as nearest 
     * neighbor surrounding Creature's  cell
     * @param sea
     *          is the ocean of currentTimeStep
     * @return 
     *          returns true on at-least one fish existence otherwise false
     * 
     */
    public static boolean checkFishAsNeighbor(Critter creature, Ocean sea){
        //get Creatures location
        Point p = creature.getLocation();
        int x = p.getX();
        int y = p.getY();
        int row,col;

        for(int i = x-1;i <= x+1; i++){
            for(int j = y-1; j <= y+1; j++){
                /*
                 * Problem statement says(as per link): 
                 * http://www.cs.berkeley.edu/~jrs/61bf06/hw/pj1/readme
                 * You can also refer to locations such as (4, 0) or (-4, 3), 
                 * which are both the same as (0, 0) in a 4x3 ocean.
                 * so modulo is being performed for given i & j
                 */
                row = mod(i, sea.getHeight());
                col = mod(i, sea.getWidth());
                if(sea.cellContents(row, col) instanceof Fish)      
                    return true;
            }
        }
        return false;
    }


    /**
     * This method counts number of Shark  as nearest neighbor surrounding
     * Creature's  cell
     * @param sea
     *          is the ocean of currentTimeStep
     * @return 
     *          returns number of Shark surrounding creature
     * 
     */

    public static int countSharkAsNeighbor(Critter creature, Ocean sea){
        int neighborSharkCount = 0;
        //get Creatures location
        Point p = creature.getLocation();
        int x = p.getX();
        int y = p.getY();
        int row,col;

        for(int i = x-1;i <= x+1; i++){
            for(int j = y-1; j <= y+1; j++){
                /*
                 * Problem statement says(as per link): 
                 * http://www.cs.berkeley.edu/~jrs/61bf06/hw/pj1/readme
                 * You can also refer to locations such as (4, 0) or (-4, 3), 
                 * which are both the same as (0, 0) in a 4x3 ocean.
                 * so modulo is being performed for given i & j
                 */
                row = mod(i, sea.getHeight());
                col = mod(i, sea.getWidth());
                if(sea.cellContents(row, col) instanceof Shark)     
                    neighborSharkCount++;
            }
        }
        return neighborSharkCount;
    }



    /**
     * This method counts number of Fish  as nearest neighbor surrounding
     * Creature's  cell
     * @param sea
     *          is the ocean of currentTimeStep
     * @return 
     *          returns number of Fish surrounding creature
     * 
     */

    public static int countFishAsNeighbor(Critter creature, Ocean sea){
        int neighborFishCount = 0;
        //get Creatures location
        Point p = creature.getLocation();
        int x = p.getX();
        int y = p.getY();
        int row,col;

        for(int i = x-1;i <= x+1; i++){
            for(int j = y-1; j <= y+1; j++){
                /*
                 * Problem statement says(as per link): 
                 * http://www.cs.berkeley.edu/~jrs/61bf06/hw/pj1/readme
                 * You can also refer to locations such as (4, 0) or (-4, 3), 
                 * which are both the same as (0, 0) in a 4x3 ocean.
                 * so modulo is being performed for given i & j
                 */
                row = mod(i, sea.getHeight());
                col = mod(i, sea.getWidth());
                //if((sea.cellContents(row, col).getClass().getName()).equals(Fish.class))
                if(sea.cellContents(row, col) instanceof Fish)
                    neighborFishCount++;
            }
        }
        return neighborFishCount;
    }


    /**
     * This method performs the modulo operation using euclidean divison
     * 
     * @param n
     *            is the numerator
     * @param d
     *            is the denominator
     * @return 
     *            Remainder 
     */

    private static int mod(int n, int d) {
        if (n >= 0)
            return n % d;
        else
            return d + ~(~n % d);
    }

}

import java.util.Random;

/* SimText.java */

/* DO NOT CHANGE THIS FILE (except as noted). */
/* (You may wish to make temporary or insert println() statements   */
/* while testing your code. when you're finished testing and debugging, */
/* though, make sure your code works with the original version of this file. */

/**
 * The SimText class is a program that runs and animates a simulation of Sharks
 * and Fish.
 * 
 * The SimText program takes up to four parameters. The first two specify the
 * width and height of the ocean. The third parameter specifies the value of
 * starveTime. For example, if you run
 * 
 * java SimText 25 25 1
 * 
 * then SimText will animate a 25x25 ocean with a starveTime of 1. If you run
 * "java SimText" with no parameters, by default SimText will animate a 50x25
 * ocean with a starveTime of 3. With some choices of parameters, the ocean
 * quickly dies out; with others, it teems forever.
 * @author mohet01
 *
 */

public final class SimText {



    /**
     * Don't let anyone instantiate this class.
     */
    private SimText(){}

    /**
     * Default parameters. (You may change these if you wish.)
     * 
     */
    // Default ocean width
    private static int width = 50; 
    // Default ocean height
    private static int height = 25; 
    // Default shark starvation time
    private static int starveTime = 3; 
    //Default Shark hunger Level
    private static int hungerLevel = 0;


    /**
     * The {@code MAGIC_NUMBER} is a prime number that is used in LCG 
     * expressions to select random location in an Ocean. 
     */
    private static final int MAGIC_NUMBER = 78887;

    private static final int LOC_MULTIPLE_OF_NUMBER = 8;


    /**
     * paint() prints an Ocean.
     */
    public static void paint(Ocean sea) {
        if (sea != null) {
            int width = sea.getWidth();
            int height = sea.getHeight();

            /* Draw the ocean */
            for (int x = 0; x < width + 2; x++) {
                System.out.print("-");
            }

            System.out.println();

            for (int row = 0; row < height; row++) {
                System.out.print("|");
                for (int col = 0; col < width; col++) {
                    Critter creature = sea.cellContents(row, col);
                    System.out.print(creature.getClass().getName().substring(0, 1));  
                }
                System.out.println("|");
            }
            for (int x = 0; x < width + 2; x++) {
                System.out.print("-");
            }
            System.out.println();
        }

    }




    /**
     * main() reads the parameters and performs the simulation and animation.
     * @param args
     * @throws InterruptedException 
     */
    public static void main(String[] args) throws InterruptedException {
        Ocean sea;

        /**
         * Read the input parameters.
         */

        if (args.length > 0) {
            try {
                width = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                System.out
                        .println("First argument to SimText is not an number");
            }
        }

        if (args.length > 1) {
            try {
                height = Integer.parseInt(args[1]);
            } catch (NumberFormatException e) {
                System.out
                        .println("Second argument to SimText is not an number");
            }
        }

        if (args.length > 2) {
            try {
                starveTime = Integer.parseInt(args[2]);
            } catch (NumberFormatException e) {
                System.out
                        .println("Third argument to SimText is not an number");
            }
        }


        /**
         * Create the initial ocean.
         */

        sea = new Ocean(width, height);


        /**
         * Visit each cell (in a roundabout order); randomly place either Fish,
         * or Shark or Empty Object in each location. 
         * 
         */
        // Create a "Random" object with seed 0
        Random random = new Random(0); 
        int x = 0;
        int y = 0;
        for (int row = 0; row < height; row++) {
            x = (x + MAGIC_NUMBER) % height; 
            if ((x & LOC_MULTIPLE_OF_NUMBER) == 0) {
                for (int col = 0; col < width; col++) {
                    y = (y + MAGIC_NUMBER) % width; 
                    if ((y & LOC_MULTIPLE_OF_NUMBER) == 0) {
                        // Between -2147483648 and 2147483647
                        int r = random.nextInt(); 
                        if (r < 0) { 
                            sea.addCritter(new Fish(x,y)); 
                        } else if (r > 1500000000) { 
                            sea.addCritter(new Shark(x,y,starveTime,hungerLevel)); 
                        }
                    }
                }
            }
            else{
                sea.addCritter(new Empty(x,y));
            }
        }



        /**
         * Perform time steps forever.
         *
         */
        // Loop forever
        while (true) { 
            paint(sea);
            // For fun, you might wish to change the delay in the next line.
            Thread.sleep(1000); // Wait one second (1000 milliseconds)
            sea = sea.timeStep(); // Simulate a timestep
        }



    }/* end main() */

}

\$\endgroup\$
7
\$\begingroup\$

Classes and modelling

I'm going to make some recommendations, some of which are in direct contradiction to previous advice from @MatsMug. Specifically, I'd resist the urge to take the object-oriented modelling too seriously.

This problem is just a variant of the . In most Game of Life implementations, you'll find that most of the logic is in a Board class. In this problem, I wouldn't ask the Shark and Fish classes to do too much. Each Shark should keep track of its own hunger level. That's about it. Each critter does not need to know its coordinates. Quite the opposite is true: the ocean needs to know what critter occupies each cell, since much of the logic is about inspecting the neighbours of each cell. Furthermore, the position is not really part of the state of each critter — once a critter comes into existence, it never moves.

You tried to set Shark.starveTime as a static constant. I understand the instinct to make it a property of the Shark class, but here it's actually a property of the Ocean that is being simulated. It's similar to the idea that the fine structure constant is a property of our Universe, and tweaking its value would lead to a different looking Universe. (Imagine multiple ocean simulations running in multiple threads of the same Java process, each with a different starveTime setting. You don't want the starveTime to be a class variable of Shark. It could be an instance variable of Shark, but ultimately each shark would take on its starveTime value from the ocean it inhabits.)

You currently have Point and Utility classes. As others have pointed out, Point is aimless. Naming a class Utility is a red flag. I think that the solution to both problems is to combine them into a Cell class. A Cell has an x-y coordinate, has an Occupant (a more generic name for what you call a Critter), and knows how to classify and count its neighbouring cells' occupants.

As suggested in the instructions, a solution would rely mostly on duplicating immutable objects rather than mutating objects in place. Therefore, almost all of the instance variables should be final to enforce immutability.


Based on the principles above, I would write the Ocean class and a few inner classes this way. The remaining Fish and Shark classes are rather trivial.

public class Ocean {

    //////////////////////////////////////////////////////////////////////

    public class Cell {
        private final int x, y;
        private Occupant occupant;

        public Cell(int x, int y) {
            this.x = x;
            this.y = y;
            this.occupant = EMPTY_CELL;
        }

        public void putOccupant(Occupant o) {
            this.occupant = o;
        }

        public Occupant getOccupant() {
            return this.occupant;
        }

        /**
         * 1) If a cell contains a shark, and any of its neighbors is a fish, then the
         * shark eats during the timestep, and it remains in the cell at the end of the
         * timestep.  (We may have multiple sharks sharing the same fish.  This is fine;
         * they all get enough to eat.)
         * 
         * 2) If a cell contains a shark, and none of its neighbors is a fish, it gets
         * hungrier during the timestep.  If this timestep is the (starveTime + 1)th
         * timestep the shark has gone through without eating, then the shark dies
         * (disappears).  Otherwise, it remains in the cell.  An example demonstrating
         * this rule appears below.
         * 
         * 3) If a cell contains a fish, and all of its neighbors are either empty or are
         * other fish, then the fish stays where it is.
         * 
         * 4) If a cell contains a fish, and one of its neighbors is a shark, then the
         * fish is eaten by a shark, and therefore disappears.
         * 
         * 5) If a cell contains a fish, and two or more of its neighbors are sharks, then
         * a new shark is born in that cell.  Sharks are well-fed at birth; _after_ they
         * are born, they can survive an additional starveTime timesteps without eating.
         * (But they will die at the end of starveTime + 1 consecutive timesteps without
         * eating.)
         * 
         * 6) If a cell is empty, and fewer than two of its neighbors are fish, then the
         * cell remains empty.
         * 
         * 7) If a cell is empty, at least two of its neighbors are fish, and at most one
         * of its neighbors is a shark, then a new fish is born in that cell.
         * 
         * 8) If a cell is empty, at least two of its neighbors are fish, and at least two
         * of its neighbors are sharks, then a new shark is born in that cell.  (The new
         * shark is well-fed at birth, even though it hasn't eaten a fish yet.)
         */
        public Occupant timeStep() {
            Occupant occ = getOccupant();
            switch (occ.getType()) {
              case SHARK:
                switch (countNeighbors(Fish.class)) {
                  case 0:
                    // 2) Shark gets hungrier and may die
                    return occ.age();
                  default:
                    // 1) Shark eats neighboring fish
                    return new Shark(Ocean.this.getStarveTime());
                }
              case FISH:
                switch (countNeighbors(Shark.class)) {
                  case 0:
                    // 3) Fish lives
                    return occ.age();
                  case 1:
                    // 4) Fish gets eaten by one shark neighbor
                    return EMPTY_CELL;
                  default:
                    // 5) Baby shark replaces this fish
                    return new Shark(Ocean.this.getStarveTime());
                }
              default:
                switch (countNeighbors(Fish.class)) {
                  case 0:
                  case 1:
                    // 6) Cell remains empty
                    return occ.age();
                  default:
                    switch (countNeighbors(Shark.class)) {
                      case 0: case 1:
                        // 7) New fish born
                        return new Fish();
                      default:
                        // 8) New shark born
                        return new Shark(Ocean.this.getStarveTime());
                    }
                }
            }
        }

        private <T extends Occupant> int countNeighbors(Class<T> type) {
            int count = 0;
            for (Cell c : getNeighbors()) {
                if (type.isInstance(c.getOccupant())) {
                    count++;
                }
            }
            return count;
        }

        private Cell[] getNeighbors() {
            return new Cell[] {
                cell(x - 1, y - 1), cell(x - 1, y), cell(x - 1, y + 1),
                cell(x    , y - 1),                 cell(x    , y + 1),
                cell(x + 1, y - 1), cell(x + 1, y), cell(x + 1, y + 1)
            };
        }
    }

    //////////////////////////////////////////////////////////////////////

    public static abstract class Occupant {
        public Occupant age() {
            return this;
        }

        public abstract int getType();
    }

    //////////////////////////////////////////////////////////////////////

    public static final Occupant EMPTY_CELL = new Occupant() {
        @Override
        public int getType() {
            return 0;
        }
    };

    public static final int SHARK = Shark.TYPE_CODE,
                            FISH  = Fish.TYPE_CODE;

    private final int starveTime;
    private final Cell[][] cells;

    /**
     * Constructor
     *
     * @param i width of the toroidal ocean
     * @param j height of the toroidal ocean
     * @param starveTime time steps that sharks can live without eating
     */
    public Ocean(int i, int j, int starveTime) {
        this.starveTime = starveTime;

        // Matrix is in column-major order!
        // (0, 0)   (1, 0)  ...   (i, 0)
        //   .        .             .
        //   .               .      .
        // (0, j)   (1, j)  ...   (i, j)
        this.cells = new Cell[i][j];
        for (int x = 0; x < i; x++) {
            for (int y = 0; y < j; y++) {
                this.cells[x][y] = new Cell(x, y);
            }
        }
    }

    public int getStarveTime() {
        return this.starveTime;
    }

    public void addFish(int x, int y) {
        this.cell(x, y).putOccupant(new Fish());
    }

    public void addShark(int x, int y) {
        this.cell(x, y).putOccupant(new Shark(this.getStarveTime()));
    }

    public int width() {
        return this.cells.length;
    }

    public int height() {
        return this.cells[0].length;
    }

    public Ocean timeStep() {
        Ocean next = new Ocean(width(), height(), getStarveTime());
        for (int x = 0; x < width(); x++) {
            for (int y = 0; y < height(); y++) {
                next.cells[x][y].putOccupant(this.cell(x, y).timeStep());
            }
        }
        return next;
    }

    public int cellContents(int x, int y) {
        return cell(x, y).getOccupant().getType();
    }

    private Cell cell(int x, int y) {
        return this.cells[((x % width()) + width()) % width()]
                         [((y % height()) + height()) % height()];
    }
}
\$\endgroup\$
  • \$\begingroup\$ Sure i will make these changes, another point is, i want to make this simulation browser based rather than taking input from stdin & displaying on stdout, Is it relevant to use applet approach or servlet program approach for the kind of application this is? \$\endgroup\$ – overexchange Mar 14 '14 at 9:26
  • \$\begingroup\$ Servlets are for generating dynamic content on the server side. To run an applet in the browser, all you need is a static HTML file that contains an <applet> or <object> tag that references the JAR file (sort of like how an <img> tag works). \$\endgroup\$ – 200_success Mar 14 '14 at 15:30
  • \$\begingroup\$ If i think of using servlet class, Does one servlet class would suffice for this nature of app? DO i need to use MVC? I want to understand, How to think on this. Becasue here the output is coming every second, as per simtext.java while loop \$\endgroup\$ – overexchange Mar 16 '14 at 1:19
  • \$\begingroup\$ If you wish to refresh the display in the browser about once a second, you will have a miserable experience with server-side programming. You want all of the code to execute on the client. A Java applet could work, and would let you reuse come code. (Frankly, applets, along with ActiveX, Silverlight, and Flash, are a dying technology. Personally, I would choose to rewrite the whole thing as JavaScript that dynamically updates cells of an HTML table.) \$\endgroup\$ – 200_success Mar 16 '14 at 2:13
  • \$\begingroup\$ As i previously said starveTime has to be read only once, when class is loaded. I read starveTime value from command line in simtext.java and as per above discussion, starveTime property should be part of Ocean class, so how do i set starveTime value using static{} block during class load time only? \$\endgroup\$ – overexchange Mar 16 '14 at 7:21
8
\$\begingroup\$

Critter

Your Point class is, as @RoToRa pointed out, underused. This constructor:

Point location;

public Critter(int x, int y){
    location = new Point(x,y);
}

Could be simplified to this:

Point _location;

public Critter(Point location){
    _location = location;
}

Also in this snippet:

/**
 * This method computes the new value of location(which can be EMPTY) property of Critter.
 * No operation is performed as this is a base class.
 */
public abstract Critter update(Ocean currentTimeStepSea);

The comment "no operation is performed as this is a base class" is redundant, because the method is abstract - don't bother writing comments that reword what the code already says.

I'll skip the implementing classes and jump dive straight into the Ocean.


Ocean

You've replaced the lightweight integers you had in your array with objects. That's great, but you still have an array with tons of objects you don't need to care about - the Empty class doesn't need to exist. Let's see why.

enter image description here

  • An ocean has critters and updates periodically.
  • A critter has a location and updates periodically. To do so it needs to know about other critters in the sea, so the Update method takes in a Iterable<Critter>. A critter can also be "dead" and need to be removed from the ocean.

Update

Your current implementation starts like this:

Ocean nextTimeStepSea = new Ocean(width, height);

So at every time step, you're re-creating the entire ocean?! What is it exactly that needs to happen in the Ocean's update method? Every critter must be updated. The easiest is to iterate the _critters list and call each instance's update method, passing in the _critters list as the Iterable<Critter> parameter.

After all critters have updated, the ocean's update method should spawn the new critters (add them to _critters) and destroy (remove them from _critters) the dead ones; you'll want to do that after a first iteration over all critters, for sanity's sake.

Note that the Ocean class doesn't need to expose an addCritter or removeCritter method to do that.


To make your code more object-oriented, you'll need to think in terms of objects, not just in terms of solving a problem. Forget about the oceanMatrix[][] - the ocean doesn't even need to have a size. If you want to have a maximum size, it's not really the ocean's size you're defining, but the boundaries of the virtual "box" a critter is allowed to move in. Thus, I'd probably define these as private static final Point maxMove fields (or whatever the Java equivalent of C# private static readonly Point maxMove is) in the Critter base class. What you don't want to be doing here, is declaring that Point on the Ocean class, and tightly couple critters with the Ocean class by merely referring to it: it's important that critters don't even know an ocean exists.

To facilitate the implementation of your paint method to render the ocean and its critters on-screen, you could implement a Critter getContent(Point) method on the Ocean class that returns either null or the Critter instance that populates the specified coordinate; if the method returns null you draw nothing, if it returns a Critter that's alive (in theory the ocean's update method will have removed dead critters so that wouldn't need to be checked), you can draw a fish if it's a Fish, and you can draw a shark if it's a Shark - that's one simple, naive way of going about it, but it's a start.

The bottom line, is that Ocean must stop being represented as an array of X-Y coordinates with a "content", and start being a composition of objects with their own concerns. I'm sure the Utility class will become useless when everything is in its place.


As for where the starveTime should go... how about deriving a PredatorCritter class from Critter, putting starveTime in there, and deriving Shark from PredatorCritter?

\$\endgroup\$
  • \$\begingroup\$ Mat, In this simulation project, I would like to understand, What is Data Abstraction? What is Encapsulation? Can you let me know your understanding on these in my simulation project? \$\endgroup\$ – overexchange Apr 9 '14 at 5:35
5
\$\begingroup\$

Just one quick observation: There seems to be no point in the Point class (pun not intended). Its fields are private, so you can't ever read anything from it, and you don't actually use it anyway, except for the Critters location property of which the getter getLocation is also never used.

EDIT: Just realized you haven't implemented anything yet, so that's why you don't use location :-) But you still need to create getters for the fields of Point.

\$\endgroup\$
  • \$\begingroup\$ Above is the solution for part I of problem in link \$\endgroup\$ – overexchange Mar 12 '14 at 10:51
  • \$\begingroup\$ Jus edited the question, My question wrt starveTime is, starveTime cannot be a per class variable, it should be common any creature that eats like whale or shrimp. so what should be the solution? \$\endgroup\$ – overexchange Mar 12 '14 at 15:08
  • \$\begingroup\$ @tintinmj As you requested for test cases, I do not have test cases to test this program, Do i need to use JUNIT to create test cases in eclipse? \$\endgroup\$ – overexchange Mar 12 '14 at 18:46
  • \$\begingroup\$ Can somebody respond to my query? \$\endgroup\$ – overexchange Mar 13 '14 at 3:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.