Game of Life -SOLUTION

IntroductionThe "Game of Life" is a biological simulation created by John Conway and consists of a "biosphere" that is divided into cells. Each cell is inhabited by a life form that I will refer to as "the Critter".  Critters will live or die depending upon a simple set of rules (described below). 
Resources
They can be found either through the UNIX course directory: /home/219/assignments/assignment2 or the [shortcut web link]. Starting code examples (such as  "Mode.java" and a parts of "Biosphere.java", "Critter.java", "GameOfLife.java") can be found in the [code] subdirectory.  The starting code just initializes the 7 biospheres and displays them. It is your responsibility to implement the remaining functionality. You can also find in the assignment directory a few text files that show sample runs of some of the more complex biosphere's and some sample debugging messages in the assignment directory or via the [shortcut to the output directory].
Assignment description
For this assignment the biosphere is simulated with a 10  x 10 array of 'Critters' (Figure 1). Empty elements will be displayed as spaces (a 'Critter' object whose appearance is set to a space), while elements containing a 'Critter' will either appear as a '*' (Regular Critter) or a '!' (Fertile Critter). Any of the 100 squares can contain a Critter. The simulation will begin with the Critters in some starting pattern as selected by the user (Figure 1: Top). I suggest that you start by using the simpler biospheres first and work your way up to the more complex ones.   The more biospheres that your program can properly handle, the higher your grade will be. After initializing the starting positions, the program will simulate the births and deaths of the critters over time on a turn-by-turn basis. The user can either hit <enter to run another turn, 'q' or 'Q' to end the simulation. To help you test/debug your program you should make use of the 'Mode' class (described below). Debug mode can be toggled by entering 'd' or 'D' when prompted to proceed to another turn.


For each turn that passes your program must scan the entire biosphere:


  1. Squares that currently contain a critter: the program determines which critters die and which critters are unaffected by the turn change.

  2. Squares that are currently empty: the program determines if a new critter will be born into a square.



The births and deaths of critters is based solely upon the number of neighbors. You are to assume that all births and deaths occur simultaneously. Thus you need two arrays of type Critter. One array, which I refer to as 'previous' in "Biosphere.java", contains the positions of the critters previous to the turn and is used to determine how many critters neighbor a particular square.  The second array, which I call 'current' in "Biosphere.java", will initially contain the same pattern of critters as the other array.  As the program scans the previous array, critters will die from and be born into the 'current' array.  Make sure that you do not change the pattern of critters of the previous array while you are scanning  because as I said earlier, births and deaths occur simultaneously.
Birth and death conditions for Critters:
1) If a square contains a critter:

a) The critter will die if:  (i) It has 0 or 1 neighbors - it dies of loneliness    (ii) It has 4 to 8 neighbors - it dies of overcrowding. 
b) The critter will go on living as-is (won't die) if it has 2 - 3 neighbors.

2) If the square is empty: a critter will be born there if it has exactly 3 neighbors. (Fertile critters count as two neighbors)
 
How do you determine the neighbors for a particular critter? Picture the following two-dimensional array that is a subset of the biosphere array:



 
 
 


 
X
 


 
 
 



The square marked with an "X" is a square where we are trying to determine if a birth or death will occur.  In the cases below I will use a "*" (star) to represent the neighboring critters.  The squares that you need to scan will be the squares adjacent to the square in middle (i.e., above, below, left side, right side, and the four diagonals).  Note: In the examples below I assume that the square to be scanned is an inner square, you will have to write up the exceptional conditions for other cases (e.g., the top and bottom rows, far left and far right columns as well as the four corners).
e.g. 1, (critter already in square to be scanned) - critter dies of loneliness (no neighbors or just one neighbor).

No neighbors                                                                              One neighbor

e.g. 2, (critter already in square to be scanned) - critter dies from overcrowding (in this example the critter has four neighbors - it also dies if there are more than four neighbors) 

Four neighbors

e.g. 3, (empty square) - critter will born into the empty square because there is exactly 3 neighbors. 

Empty square (critter born)

e.g. 4, (critter already in square to be scanned) - no change to the critter (two or three neighbors)

Two neighbors

How does all of this scanning relate to the 10 x 10 biosphere? You must perform a neighbor count for each of the 100 squares. 
Fertile Critters '!':
When counting neighbors to determine births, Fertile Critters are counted as two critters.  When counting neighbors to determine deaths Fertile Critters are counted as one neighbor just like Regular Critters. Except for determining births Regular and Fertile critters are treated identically. The location of Fertile critters is fixed when the program starts. Critters cannot change from one state to another. The only type of critters that can be born into the simulation are Regular (and not Fertile) Critters.
Required classes for this program:
 
 
UserInterface: similar to the  ["GameInterface class" covered during the week of Jan 31 - Feb 6] in the 'Dice example covered in tutorial class is responsible for all input and output. e.g., displaying the initial menu (biosphere selection), getting user input and the end of each turn, determining if the user's selection was valid/determining which option was selected.
Mode: Its sole purpose is to determine if the program is operating in debugging mode. By default the program will not be operating in debugging mode.
public class Mode {
     public static boolean debug = false;
}
When the program is in debugging mode, messages about the state of the program will be displayed. The exact content of the messages is left to your discretion but this mode should be implemented early on to aid in testing/debugging. Example debugging message:  if debug mode is on when checking for births the program could display the (row/column) being checked, the neighbor count and if a birth will occur at that location. (You can then compare the resulting debugging messages vs. the values you expect from manually tracing your program).
method () {
     // Toggle debug mode
    if (Mode.debug == false)
          Mode.debug = true; // Same logic to toggle debugging to off.

    // Example of displaying debugging messages only when the program is in debug mode
   if (Mode.debug == true)
         System.out.println("<<< This is a purely simulated debugging message ");
}

Unlike the other classes this class can be purely static (it need not be instantiated). More details to be provided in lecture "Advanced Java" section (so don't miss it or make sure you get the notes from someone if you can't attend).
Critter: Instances of this class will store information about each critter. At an absolute minimum your implementation of the Critter class must include an attribute that is used to store information about the critter's appearance 
e.g., private char appearance;
Whenever the your program runs through the loops to display individual squares of the biosphere, the character that is displayed for a particular square will be determined by the appearance of the critter (star, exclamation mark or a space) i.e., the Biosphere's 'display()' method will call the Critter classes' appropriate 'display()' method for each square in the biosphere.
Biosphere: This class will store information about the simulated world (not coincidentally called a 'biosphere'). As noted the biosphere will take the form of an array of references to Critter objects. All data and actions directly related to the simulation world will be the attributes and methods of this class. One important task that should be handled by this class will be the maintenance of the biosphere: scanning the squares in order to determine where Critters will be born and where they will die.  Break down this main task into sub-methods as much as possible e.g., the main job of scanning the biosphere should be broken down into smaller jobs such as: i) scanning the inner parts of the biosphere (rows 1 - 8 and columns 1- 8: 64 squares) ii) scanning the top row only (row=0) iii) scanning the bottom row (row=9) iv) scanning the left-most column (column=0) v) scanning the right-most column (column=9) vi) scanning the four corners.  Your grade could be adversely affected because of the loss of style marks if you only implemented one very large method that handled all the above tasks.
GameOfLife: The 'Driver' for the program that should contain a reference to the user interface class:
public static void main (String [] args) {
    UserInterface anInterface = new UserInterface ();
                 :                        :       
}
Note: the starting driver in the assignment directory only implements very bare bones functionality. Because it has no user interaction (runs simulation for only one turn) it directly instantiates a biosphere inmain(). You should re-write the driver so an interface is instantiated here and the user interface class instantiates and calls methods of class "Biosphere".
UML class diagram
You should create a UML diagram for class "Biosphere" and class "Critter". The diagram should be the 'full' version and show parameters and return values of methods as well the relationship between the two classes plus multiplicity (see the notes which introduce Object-Oriented concepts). The class diagram can be drawn using any structured drawing program (even PowerPoint). However it must be correct and it must be understandable by the marker. The image that you submit via D2L must be in one of the following formats: 'gif', 'jpg', 'png' or 'pdf''.
Marking
To help you ensure that you haven't missed anything here is a [marking checklist]
Using pre-written Java code
 
You will need to use the built in code class Scanner for input. And of course you must use the starting code in the A2 directory: /home/219/assignments/assignment2/code. Beyond that (and common sense operators and functions such as those for output and mathematical operators), unless you told otherwise, you will need to write your own code and cannot use other pre-written Java classes or operators.
Powered by