/*
Source file: GameOfLifeModel.java
Author: Markus Bautsch
Licence: public domain
Date: 13 October 2024
Version: 1.0
Programming language: Java
*/
/*
Model for the simulation of Conway's Game of Life
*/
public class GameOfLifeModel
{
// Class constants for state of fields
/**
* For dead cell state
*/
public final static boolean DEAD = false;
/**
* For live cell state
*/
public final static boolean LIVE = true;
// Class constants for mode of the borders
/**
* Border mode is always dead
*/
public final static boolean ALWAYS_DEAD = false;
/**
* Border mode is continued at opposite side of the board
*/
public final static boolean TORODIAL = true;
// Instance variables
private int sizeX = 200;
private int sizeY = 200;
private boolean borderMode = ALWAYS_DEAD;
private boolean [] [] currentBoard;
private boolean [] [] nextBoard;
/**
* Constructor for the initialisation of game board instances of the class GameOfLifeModel
* @param width: for the horizontal size
* @param height: for the vertical size
* @param borderMode: for the mode of the border, either ALWAYS_DEAD or TORODIAL
*/
public GameOfLifeModel (int width, int height, boolean borderMode)
{
this.sizeX = width;
this.sizeY = height;
this.borderMode = borderMode;
this.currentBoard = new boolean [width] [height];
this.nextBoard = new boolean [width] [height];
}
/**
* Returns the width of the game board
* @return width of the game board
*/
public int getWidth ()
{
return sizeX;
}
/**
* Returns the height of the game board
* @return height of the game board
*/
public int getHeight ()
{
return sizeY;
}
/**
* Returns the border mode of the game board
* @return border mode of the game board
*/
public boolean getBorderMode ()
{
return borderMode;
}
/**
* Checking the validity of the cell parameters
* @param x: horizontal position
* @param y: vertical position
* @return true, if all parameters are valid
*/
private boolean parametersValid (int x, int y)
{
boolean xValid = (x >= 0) && (x < this.sizeX);
boolean yValid = (y >= 0) && (y < this.sizeY);
return xValid && yValid;
}
/**
* Sets cell (x,y) to state
* @param x: horizontal position
* @param y: vertical position
* @param state: cell state
*/
public void setCellState (int x, int y, boolean state)
{
if (parametersValid (x, y))
{
currentBoard [x] [y] = state;
}
}
/**
* Gets state of cell (x,y)
* @param x: horizontal position
* @param y: vertical position
* @return state of the cell (x, y)
*/
public boolean getCellState (int x, int y)
{
boolean state = currentBoard [x] [y];
return state;
}
/**
* Checking cell (x, y) for upper left live neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if upper left neighbour is live
*/
private boolean upperLeftNeighbourIsLive (int x, int y)
{
boolean upperLeftNeighbourIsLive = false;
if ((x > 0) && (y > 0))
{
upperLeftNeighbourIsLive = (currentBoard [x - 1] [y - 1] == LIVE);
}
else if (borderMode == TORODIAL)
{
if ((x == 0) && (y == 0))
{
upperLeftNeighbourIsLive = (currentBoard [sizeX - 1] [sizeY - 1] == LIVE);
}
else if (x == 0)
{
upperLeftNeighbourIsLive = (currentBoard [sizeX - 1] [y - 1] == LIVE);
}
else if (y == 0)
{
upperLeftNeighbourIsLive = (currentBoard [x - 1] [sizeY - 1] == LIVE);
}
}
return upperLeftNeighbourIsLive;
}
/**
* Checking cell (x, y) for upper live neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if upper neighbour is live
*/
private boolean upperNeighbourIsLive (int x, int y)
{
boolean upperNeighbourIsLive = false;
if (y > 0)
{
upperNeighbourIsLive = (currentBoard [x] [y - 1] == LIVE);
}
else if (borderMode == TORODIAL)
{
upperNeighbourIsLive = (currentBoard [x] [sizeY - 1] == LIVE);
}
return upperNeighbourIsLive;
}
/**
* Checking cell (x, y) for upper right live neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if upper right neighbour is live
*/
private boolean upperRightNeighbourIsLive (int x, int y)
{
boolean upperRightNeighbourIsLive = false;
if ((x < (sizeX - 1)) && (y > 0))
{
upperRightNeighbourIsLive = (currentBoard [x + 1] [y - 1] == LIVE);
}
else if (borderMode == TORODIAL)
{
if ((x == (sizeX - 1)) && (y == 0))
{
upperRightNeighbourIsLive = (currentBoard [0] [sizeY - 1] == LIVE);
}
else if (x == (sizeX - 1))
{
upperRightNeighbourIsLive = (currentBoard [0] [y - 1] == LIVE);
}
else if (y == 0)
{
upperRightNeighbourIsLive = (currentBoard [x + 1] [sizeY - 1] == LIVE);
}
}
return upperRightNeighbourIsLive;
}
/**
* Checking cell (x, y) for left live neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if left neighbour is live
*/
private boolean leftNeighbourIsLive (int x, int y)
{
boolean leftNeighbourIsLive = false;
if (x > 0)
{
leftNeighbourIsLive = (currentBoard [x - 1] [y] == LIVE);
}
else if (borderMode == TORODIAL)
{
leftNeighbourIsLive = (currentBoard [sizeX - 1] [y] == LIVE);
}
return leftNeighbourIsLive;
}
/**
* Checking cell (x, y) for right live neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if right neighbour is live
*/
private boolean rightNeighbourIsLive (int x, int y)
{
boolean rightNeighbourIsLive = false;
if (x < (sizeX - 1))
{
rightNeighbourIsLive = (currentBoard [x + 1] [y] == LIVE);
}
else if (borderMode == TORODIAL)
{
rightNeighbourIsLive = (currentBoard [0] [y] == LIVE);
}
return rightNeighbourIsLive;
}
/**
* Checking cell (x, y) for lower left live neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if lower left neighbour is live
*/
private boolean lowerLeftNeighbourIsLive (int x, int y)
{
boolean lowerLeftNeighbourIsLive = false;
if ((x > 0) && (y < (sizeY - 1)))
{
lowerLeftNeighbourIsLive = (currentBoard [x - 1] [y + 1] == LIVE);
}
else if (borderMode == TORODIAL)
{
if ((x == 0) && (y == (sizeY - 1)))
{
lowerLeftNeighbourIsLive = (currentBoard [sizeX - 1] [0] == LIVE);
}
else if (x == 0)
{
lowerLeftNeighbourIsLive = (currentBoard [sizeX - 1] [y + 1] == LIVE);
}
else if (y == (sizeY - 1))
{
lowerLeftNeighbourIsLive = (currentBoard [x - 1] [0] == LIVE);
}
}
return lowerLeftNeighbourIsLive;
}
/**
* Checking cell (x, y) for lower live neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if lower neighbour is live
*/
private boolean lowerNeighbourIsLive (int x, int y)
{
boolean lowerNeighbourIsLive = false;
if (y < (sizeY - 1))
{
lowerNeighbourIsLive = (currentBoard [x] [y + 1] == LIVE);
}
else if (borderMode == TORODIAL)
{
lowerNeighbourIsLive = (currentBoard [x] [0] == LIVE);
}
return lowerNeighbourIsLive;
}
/**
* Checking cell (x, y) for lower right live neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if lower right neighbour is live
*/
private boolean lowerRightNeighbourIsLive (int x, int y)
{
boolean lowerRightNeighbourIsLive = false;
if ((x < (sizeX - 1)) && (y < (sizeY - 1)))
{
lowerRightNeighbourIsLive = (currentBoard [x + 1] [y + 1] == LIVE);
}
else if (borderMode == TORODIAL)
{
if ((x == (sizeX - 1)) && (y == (sizeY - 1)))
{
lowerRightNeighbourIsLive = (currentBoard [0] [0] == LIVE);
}
else if (x == (sizeX - 1))
{
lowerRightNeighbourIsLive = (currentBoard [0] [y + 1] == LIVE);
}
else if (y == (sizeY - 1))
{
lowerRightNeighbourIsLive = (currentBoard [x + 1] [0] == LIVE);
}
}
return lowerRightNeighbourIsLive;
}
/**
* Counting all live neighbours of cell (x, y)
* @param x: horizontal position
* @param y: vertical position
* @return number of live neighbours
*/
private long countLiveNeighbours (int x, int y)
{
long numberOfLiveNeighbours = 0;
if (upperLeftNeighbourIsLive (x, y)) {numberOfLiveNeighbours++;}
if (upperNeighbourIsLive (x, y)) {numberOfLiveNeighbours++;}
if (upperRightNeighbourIsLive (x, y)) {numberOfLiveNeighbours++;}
if (leftNeighbourIsLive (x, y)) {numberOfLiveNeighbours++;}
if (rightNeighbourIsLive (x, y)) {numberOfLiveNeighbours++;}
if (lowerLeftNeighbourIsLive (x, y)) {numberOfLiveNeighbours++;}
if (lowerNeighbourIsLive (x, y)) {numberOfLiveNeighbours++;}
if (lowerRightNeighbourIsLive (x, y)) {numberOfLiveNeighbours++;}
return numberOfLiveNeighbours;
}
/**
* Copies all cells of next board to current board
*/
private void copyNextToCurrentBoard ()
{
for (int y = 0; y < sizeY; y++)
{
for (int x = 0; x < sizeX; x++)
{
currentBoard [x] [y] = nextBoard [x] [y];
}
}
}
/**
* Computing next board of the game
*/
public void computeNextBoard ()
{
for (int y = 0; y < sizeY; y++)
{
for (int x = 0; x < sizeX; x++)
{
long numberOfLiveNeighbours = countLiveNeighbours (x, y);
if (numberOfLiveNeighbours == 2)
{
nextBoard [x] [y] = currentBoard [x] [y];
}
else if (numberOfLiveNeighbours == 3)
{
nextBoard [x] [y] = LIVE;
}
else
{
nextBoard [x] [y] = DEAD;
}
}
}
copyNextToCurrentBoard ();
}
}