Game of Life - notes on the program

The universe of the Game of Life is a two-dimensional grid of cells, each of which is in one of two possible states, alive or dead. Every cell interacts with its eight neighbours, which are the cells that are horicontally, vertically, or diagonally adjecent. At each step in time, the following transitions occur:

  • Any live cell with fewer than two live neighbours dies (underpopulation).
  • Any live cell with two or three live neighbours lives on the next generation.
  • Any live cell with more than three live neighbours dies (overpopulation).
  • Any dead cell with exactly three live neighbours becomes a live cell (reproduction).

Using the JLayer framework, this game can be modelled in two steps. Class LifeCell is used for coding the game from a cell's point of view and class LifeUniverse adds the global dynamics.


Unit Class LifeCell - Using JLayer Annotations

The code of the LifeCell class models the functionality of a single cell. This is where the annotations of the JLayer framework come into play, with the effect that a class Layer_LifeCell_ is generated that can be used to wrap a two-dimensional array of type LifeCell[][] creating the core of the LifeUniverse.


  @LayerUnit
  public class LifeCell {
	
    @LayerField
    public int state;
	
    @LayerField
    LifeCell[] vector;
	
    @LayerMethod
    public void initState(Random r) { state = r.nextInt(2); }
	
    private int next;
	
    @LayerMethod
    public void nextState(){
      int sum = 0;
      for (int i = 0; i < vector.length; i++) { 
        sum += vector[i].state; 
      }
      if (sum < 2 || sum > 3) {
        next = 0;
      } else if (sum == 3) {
        next = 1;
      } else {
        next = state;
      }
    }
	
    @LayerMethod
    public void updateState() {
      state = next; 
    }

  }
  

Obviously, the field state represents the two possible states of a cell (alive = 1 or dead = 0), the one-dimensional array vector is used to link its eight neigbours and method initState() is used to initialize the state.

The other items model the dynamic aspects. Method nextState() implements the transition rules from above and stores the next state in the local variable next, from where method updateState() takes it to actually update the state of the cell.

The JLayer annotations direct the code generation performed by the annotation processor:

  • @LayerUnit indicates that this so called unit class is to be processed by the JLayer annotation processor.
  • @LayerField marks those fields of a unit class for which the dot notation is to be lifted to the array level.
  • @LayerMethod marks those methods of a unit class for which the dot notation is to be lifted to the array level.

Establishing Global Structure and Dynamics - Using the Generated Wrapper Class

As mentioned above, from unit class LifeCell the JLayer annotation processor automatically generates the wrapper class Layer_LifeCell_, which class LifeUniverse uses to model the dynamic aspects of the Game of Life. The parts of the code that depend on the JLayer framework are color coded.


  public class LifeUniverse {
	
    private LifeCell[][] lifeArray;
    private Layer_LifeCell_ lifeLayer; 
	
    public void createNet(int width, int heigth) {
      lifeArray = new LifeCell[width][heigth];
      for(int i = 0; i < width; i++) {
        for(int j = 0; j < heigth; j++) {
          lifeArray[i][j] = new LifeCell();
        }
      }
      lifeLayer = new Layer_LifeCell_(lifeArray);
      lifeLayer.vector.connect(lifeLayer, IndexTools.isNeighbour);
    }
	
    public void initNet(int initSeed) {
      Random r = new Random(initSeed);
      lifeLayer.initState(r);
    }
	
    public void updateNet() {
      lifeLayer.nextState();
      lifeLayer.updateState();
    }
	
  }
  

The whole modelling is done by three methods and is based on the two variables lifeArray (a two-dimensional array with element type LifeCell) and lifeLayer (the wrapped array):

  • method createNet() creates and initializes the array lifeArray, wraps it and binds the result to variable lifeLayer. After that, relation IndexTools.isNeighbour from the JLayer framework can be used to link the vector layer lifeLayer.vector with the based layer lifeLayer, so that the interaction described in the introductory section can be performed;
  • method initNet() randomly initializes the state of all cells;
  • method updateNet() computes the next state of all cells and then updates them.

More Information