Title: Game of Life: Polymorphic Example
Course: CPS 161: An Intro to Programming with Java - DL MM MASTER

Game of Life: Polymorphic Example

Earlier Game of Life with an Array of Ants

In the Absolute Java Textbook, the author has a problem in the back of one of the chapters where he suggests trying to create a game of life where the organisms are polymorphic and all derived from the class Organism.

This example will use an Interface in a different way than you are accustomed. We will use an Interface to provide us a way to call back to our main or more global program. The Organism class will be an abstract class, and we will have an array of Organism. Note that some of the array cells will be empty and contain null values.

I have played around with his problem description and modified the rules a little. After you see this solution, you too can play around with the rules. We are in effect trying to model biological environments.

My rules:

2 Organisms: Ants and Predators

- My array above consists of an Array of Organisms. The empty spots are filled with nulls and provide places for the other organisms to move into, and also a place to have new babies born.
- On each world update, Ants have the following rules:
  - After 2 iterations, an Ant will try to have a baby. A baby can only happen if there is an empty spot for the baby and a neighboring Ant.
  - On each update where a Baby isn't created, the Ant will try to move to some random empty cell if possible.
- On each world update, Predators have the following rules:
  - Predators consume Ants to stay alive. If a Predator doesn't eat within 5 turns, the predator will die. If a Predator hasn't not eaten an Ant in the last 2 iterations, it will eat an Ant which shows up adjacent to it.
  - Eating an Ant, will cause a Predator to occupy the Ant's location.
  - After 6 turns a Predator will try to have a baby. Predator babies can only happen if there is an empty cell and a neighboring Predator.
  - If a predator doesn't make a baby, then it moves to an empty cell if possible.
- An organism should only update itself once on every world update cycle.

LifeNeeds.java


package gameOfLife;

enum OrganismType {Ant, Predator, Empty};

class WorldLocation
{
    int row=0;
    int col=0;
}

public interface LifeNeeds {
    int getNeighborCount(int row, int col, OrganismType organismType);
    WorldLocation getOrganismNeighbor(int row, int col, OrganismType organismType);
    void die(int row, int col);
    void placeBaby(int row, int col, Organism organism);
    void move(int newRow, int newCol, int row, int col);
    void eat(int newRow, int newCol, int row, int col);
}


Organism.java


package gameOfLife;

public abstract class Organism {

    private int previousUpdateCount=0;
    public abstract void update(int row, int col, int updateCount);

    Organism(int cnt)
    {
        previousUpdateCount = cnt;
    }

    boolean checkProcessed(int updateCount)
    {
        if (updateCount > previousUpdateCount)
        {
            previousUpdateCount = updateCount;
            return false;
        }
        return true;
    }


    void printDebug( String info, int row, int col)
    {
        System.out.println(info + " row ="+ row + " col="+ col);
    }


}

class Ant extends Organism
{
    int makeBabyCount =  (int)(2*Math.random());
    LifeNeeds lifeNeeds=null;

    Ant(int cnt,  LifeNeeds lifeNeeds)
    {
        super(cnt );
        this.lifeNeeds = lifeNeeds;
    }

    public String toString()
    {
        return " A ";
    }
    public  void update(int row, int col, int updateCount)
    {
        int antCount = lifeNeeds.getNeighborCount(row, col, OrganismType.Ant);
        WorldLocation emptyLocation =
                lifeNeeds.getOrganismNeighbor(row, col, OrganismType.Empty);

        if (emptyLocation != null)
        {
            if (makeBabyCount >= 2 && antCount > 0 && antCount < 4)
            {
                Ant baby = new Ant(updateCount, lifeNeeds);
                lifeNeeds.placeBaby(emptyLocation.row, emptyLocation.col, baby);
                makeBabyCount = 0;
                printDebug("Ant baby ", row, col);
            }
            else
                lifeNeeds.move(emptyLocation.row, emptyLocation.col, row, col);
        }
        makeBabyCount += 1;

    }
}

class Predator extends Organism
{
    int makeBabyCount = (int)(6*Math.random());
    int hungerCount = (int)(2*Math.random());
    LifeNeeds lifeNeeds = null;

    Predator(int cnt, LifeNeeds lifeNeeds)
    {
        super(cnt);
        this.lifeNeeds = lifeNeeds;
    }

    public String toString()
    {
        return " P ";
    }
    public  void update(int row, int col, int updateCount)
    {
        int antCount = lifeNeeds.getNeighborCount(row, col, OrganismType.Ant);
        if (antCount >= 2 && hungerCount >= 2)
        {
            WorldLocation wl =lifeNeeds.getOrganismNeighbor(row, col, OrganismType.Ant);
            lifeNeeds.eat(wl.row, wl.col, row, col);
            hungerCount = 0;

            printDebug("Predator Eats ", row, col);
        }
        hungerCount += 1;
        if (hungerCount >= 5)
        {
            printDebug("Predator Dies ", row, col);
            lifeNeeds.die(row, col);
        }
        int predatorCount = lifeNeeds.getNeighborCount(row, col, OrganismType.Predator);
        WorldLocation worldLocation = lifeNeeds.getOrganismNeighbor(row, col, OrganismType.Empty);

        if (worldLocation != null)
        {
            if (predatorCount > 0)
            {
                if (makeBabyCount >= 6)
                {
                    Predator predator = new Predator(updateCount, lifeNeeds);
                    lifeNeeds.placeBaby(worldLocation.row, worldLocation.col, predator);
                    makeBabyCount = 0;
                    printDebug("Predator baby ", row, col);
                }
                else
                    lifeNeeds.move(worldLocation.row,  worldLocation.col, row, col);
            }
            makeBabyCount += 1;
        }
    }
}



Life.java


package gameOfLife;


import java.util.Scanner;

public class Life implements LifeNeeds{
    static final int NUM_ROWS= 8;
    static final int NUM_COLS = 10;
    Organism[][] world =  new Organism[NUM_ROWS][NUM_COLS];
    Scanner keyboard = new Scanner(System.in);

    Life()
    {
        System.out.println("Enter in Ant and Predator density (number between 0.0 and 1.0");

        double densityAnts = keyboard.nextDouble();
        double densityPredators = keyboard.nextDouble();

        for (int i=0; i < NUM_ROWS; i++)
        {
            for (int j=0; j < NUM_COLS; j++)
            {
                double prob = Math.random();
                if ( prob < densityAnts)
                    world[i][j] = new Ant(0, this);
                else if (prob < (densityAnts + densityPredators))
                    world[i][j] = new Predator(0, this);
                else
                    world[i][j] = null;
            }
        }
    }

    void display()
    {
        for (int r=0; r < NUM_ROWS; r++)
        {
            for (int c=0; c < NUM_COLS; c++)
            {
                if (world[r][c] != null)
                    System.out.print(world[r][c]);
                else
                    System.out.print("   ");
            }
            System.out.println();
        }
    }




    void worldupdate(int count)
    {
        int totalEmpties = 0, totalAnts=0, totalPredators = 0;

        for (int r=0; r < NUM_ROWS; r++)
        {
            for (int c=0; c < NUM_COLS; c++)
            {
                Organism currentOrganism = world[r][c];
                if (currentOrganism == null)
                {
                    totalEmpties += 1;
                }
                else if (!currentOrganism.checkProcessed(count))
                {
                    if (currentOrganism instanceof Ant)
                        totalAnts += 1;
                    else if (currentOrganism instanceof Predator)
                        totalPredators += 1;
                    currentOrganism.update(r, c, count);
                }



            }
        }
        System.out.println("      *******     ");
        System.out.println("Total Empties="+totalEmpties + " Total Ants="+totalAnts + " Total Predators ="+totalPredators);
    }

    void playGame()
    {
        String response = null;
        int count = 1;
        display();

        do
        {
            worldupdate(count++);
            display();
            System.out.println("Do you want to keep going (y/n)");
            response = keyboard.next();
        } while ('y' == response.charAt(0));
        System.out.println("Game is over");
    }



    @Override
    public void die(int row, int col) {
        world[row][col] = null;
    }
    @Override
    public void placeBaby(int row, int col, Organism organism) {
        world[row][col]=organism;
    }
    @Override
    public void move(int newRow, int newCol, int row, int col) {
        world[newRow][newCol] = world[row][col];
        world[row][col] = null;
    }
    @Override
    public void eat(int newRow, int newCol, int row, int col) {
        world[newRow][newCol] = world[row][col];
        world[row][col] = null;
    }

    @Override
    public int getNeighborCount(int row, int col, OrganismType organismType) {
        int count = 0;
        for (int r= row -1; r <= row+1; r++)
        {
            for (int c= col-1; c <= col+1; c++)
            {
                if (r == row && c == col) continue;
                if ( r < 0 || c < 0) continue;
                if ( r >= NUM_ROWS  || c >= NUM_COLS) continue;
                Organism org = world[r][c];

                switch(organismType)
                {
                case Ant:
                    if (org instanceof Ant)
                        count += 1;
                    break;
                case Predator:
                    if (org instanceof Predator)
                        count += 1;
                    break;
                case Empty:
                    if (org == null)
                        count += 1;
                }
            }
        }
        return count;
    }
    @Override
    public WorldLocation getOrganismNeighbor(int row, int col,
            OrganismType organismType) {

        // Find the total number of objects matching organismType
                int index =  getNeighborCount(row, col, organismType);
        // we want to pick a random index for the object matching organismType
                index = (int)(index * Math.random());
                int count = 0;

                WorldLocation wl = new WorldLocation();
                for (int r= row -1; r <= row+1; r++)
                {
                    for (int c= col-1; c <= col+1; c++)
                    {
                        wl.row = r;
                        wl.col = c;

                        if (r == row && c == col) continue;
                        if ( r < 0 || c < 0) continue;
                        if ( r >= NUM_ROWS  || c >= NUM_COLS) continue;

                        Organism org = world[r][c];

                        switch(organismType)
                        {
                        case Ant:
                            if (org instanceof Ant)
                            {
                                if (index == count++)
                                    return wl;
                            }
                            break;
                        case Predator:
                            if (org instanceof Predator)
                            {
                                if (index == count++)
                                    return wl;
                            }
                            break;
                        case Empty:
                            if (org == null)
                            {
                                if (index == count++)
                                    return wl;
                            }
                        }
                    }
                }
                return null;
    }

    public static void main(String[] args) {

        Life life = new Life();
        life.playGame();

    }

}


Last Updated: November 18, 2013 12:48 PM