import java.awt.*;
import java.util.*;

/**************************************************************
* ConwaysLife: A class to play Conway's Game of Life. In
* brief the game simulates a 2D grid of cells, each of which
* are either empty or occupied by an organism. Organisms live
* or die on a generational basis based on the number of 
* occupied neighbour cells: too many (4+) and they are over
* crowded to death. Too few (1-) and they freeze. An empty cell
* may generate life it has exactly the right number of
* occupied neighbours (3).
* @author Spike Barlow
* @date 24/8/1999
**************************************************************/
public class ConwaysLife {

  private boolean[][]	board;
  private int		generation;
  private int		columns,
			rows;
  private Color		lifeColour = Color.yellow;
  private int		interColumnGap = 0;
  private int		interRowGap = 0;
  private static final int	BIRTH = 3;
  private static final int	FREEZE = 1;
  private static final int	OVERCROWD = 4;


/*********************************************************
* The single constructor. Accepts a width and height
* specifying how many cells the life board will have.
* Also accepts a population density: a value between 0
* and 1. That value refelcts the likelihood that a cell
* will start occupied and is used to populate the board
* with an initial distribution.
*********************************************************/
ConwaysLife(int width, int height, double density) {

  Random generator = new Random();

  ///////////////////////////////////////////
  // Create the array then populate each cell
  ///////////////////////////////////////////
  board = new boolean[width][height];
  for (int i=0;i<width;i++)
    for (int j=0;j<height;j++) {
      if (generator.nextDouble()<=density)
	board[i][j] = true;
      else
	board[i][j] = false;
    }

  generation = 0;
  columns = width;
  rows = height;
}



/***********************************************************
* Apply the rules for the Game of Life to take the entire
* board from one generation to the next. These rules concern
* the survival of an organism or spontaneous creation of one
* based on the number of neighbours it has. The class 
* description includes a brief statement of those rules.
***********************************************************/
public void advanceGeneration() {
  boolean[][]  nextGeneration;
  int		i;
  int		j;
  int		numLivingNeighbours;

  ///////////////////////////////////////////////////////////
  // Need a 2nd array to temporarily hold the next generation
  ///////////////////////////////////////////////////////////
  nextGeneration = new boolean[columns][rows];
  generation++;
  /////////////////////////////////////////////////
  // for every cell (each column and then each row)
  //   determine its contents in the new generation
  /////////////////////////////////////////////////
  for (i=0;i<columns;i++)
    for (j=0;j<rows;j++) {
      numLivingNeighbours = countNeighbours(i,j);
      if (!board[i][j]) {
	if (numLivingNeighbours==BIRTH)
	  nextGeneration[i][j] = true;
        else
	  nextGeneration[i][j] = false;
      }
      else {
	if (numLivingNeighbours>=OVERCROWD || 
		numLivingNeighbours<=FREEZE)
	  nextGeneration[i][j] = false;
        else
	  nextGeneration[i][j] = true;
      }
    }
  board = nextGeneration;
}


/********************************************************
* Determine the number of occupied neighbouring cells for
* a given cell location (column and row). An internal
* (private) method utilised only by the class.
********************************************************/
private int countNeighbours(int col, int row) {
  int count = 0;
  int		i, j;

  /////////////////////////////////////////////////////////
  // for the 3x3 grid centred on the cell. However cells
  // along edges have less neighbours hence the use of min
  // and max. If efficiency was sought for this class then
  // this method should be rewritten.
  /////////////////////////////////////////////////////////
  for (i=Math.max(0,col-1);i<=Math.min(columns-1,col+1);i++)
    for (j=Math.max(0,row-1);j<=Math.min(rows-1,row+1);j++)
      if (board[i][j])
	count++;

  if (board[col][row])
    return count-1;
  else
    return count;
}



/**********************************************************
* draw(): Cause the life board to be drawn on a Graphics
* object, starting at a particular location (startX,
* startY), and drawing each cell as a rectangle
* width x height.
***********************************************************/
public void draw(Graphics g, int startX, int startY,
		int width, int height) {
  int		i, j;

  Color preserve = g.getColor();
  g.setColor(lifeColour);
  for (i=0;i<columns;i++) 
    for (j=0;j<rows;j++)
      if (board[i][j])
	g.fillRect(startX + i*(width+interColumnGap),
		startY + j*(height+interRowGap),
		width, height);
  g.setColor(preserve);
}



/*********************************************************
* Set the horizontal gap (in pixels) between cells for
* when they are drawn.
*********************************************************/
public void setColumnGap(int gap) {

  interColumnGap = gap;
}



/**********************************************************
* Set the vertical gap (in pixels) between cells for
* when they are drawn.
**********************************************************/
public void setRowGap(int gap) {

  interRowGap = gap;
}


/*********************************************************
* Get the current generation number (starts at zero).
*********************************************************/
public int getGeneration() {

  return generation;
}
}


