/*
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
public final static boolean DEAD = false;
public final static boolean LIFE = true;
// Class constants for mode of the borders
public final static boolean ALWAYS_DEAD = false;
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 instances of the class GameOfLifeModel
* @param x: for the horizontal size
* @param y: for the vertical size
* @param borderMode: for the mode of the border, wither ALWAY_DEAD or TORODIAL
*/
public GameOfLifeModel (int x, int y, boolean borderMode)
{
this.sizeX = x;
this.sizeY = y;
this.borderMode = borderMode;
this.currentBoard = new boolean [x] [y];
this.nextBoard = new boolean [x] [y];
}
/**
* Returns the width of the game field
* @return height of the game field
*/
public int getWidth ()
{
return sizeX;
}
/**
* Returns the height of the game field
* @return height of the game field
*/
public int getHeight ()
{
return sizeY;
}
/**
* Returns the border mode of the game field
* @return border mode of the game field
*/
public boolean getBorderMode ()
{
return borderMode;
}
/**
* Checking the validity of the cell parameters
* @param x: horizontal position
* @param y: vertical position
* @param state: cell state
* @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
*/
public void setCellState (int x, int y, boolean state)
{
if (parametersValid (x, y))
{
currentBoard [x] [y] = state;
}
}
/**
* Gets state of cell (x,y)
* @return state of the cell (x, y)
*/
public boolean getCellState (int x, int y)
{
boolean state = currentBoard [x] [y];
return state;
}
/**
* Copies all cells of next field to current field
*/
private void copyNextToCurrentField ()
{
for (int y = 0; y < sizeY; y++)
{
for (int x = 0; x < sizeX; x++)
{
currentBoard [x] [y] = nextBoard [x] [y];
}
}
}
/**
* Checking cell (x, y) for upper left life neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if upper left neighbour is life
*/
private boolean upperLeftNeighbourIsLife (int x, int y)
{
boolean upperLeftNeighbourIsLife = false;
if ((x > 0) && (y > 0))
{
upperLeftNeighbourIsLife = (currentBoard [x - 1] [y - 1] == LIFE);
}
else if (borderMode == TORODIAL)
{
if ((x == 0) && (y == 0))
{
upperLeftNeighbourIsLife = (currentBoard [sizeX - 1] [sizeY - 1] == LIFE);
}
else if (x == 0)
{
upperLeftNeighbourIsLife = (currentBoard [sizeX - 1] [y - 1] == LIFE);
}
else if (y == 0)
{
upperLeftNeighbourIsLife = (currentBoard [x - 1] [sizeY - 1] == LIFE);
}
}
return upperLeftNeighbourIsLife;
}
/**
* Checking cell (x, y) for upper life neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if upper left neighbour is life
*/
private boolean upperNeighbourIsLife (int x, int y)
{
boolean upperNeighbourIsLife = false;
if (y > 0)
{
upperNeighbourIsLife = (currentBoard [x] [y - 1] == LIFE);
}
else if (borderMode == TORODIAL)
{
upperNeighbourIsLife = (currentBoard [x] [sizeY - 1] == LIFE);
}
return upperNeighbourIsLife;
}
/**
* Checking cell (x, y) for upper right life neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if upper right neighbour is life
*/
private boolean upperRightNeighbourIsLife (int x, int y)
{
boolean upperRightNeighbourIsLife = false;
if ((x < (sizeX - 1)) && (y > 0))
{
upperRightNeighbourIsLife = (currentBoard [x + 1] [y - 1] == LIFE);
}
else if (borderMode == TORODIAL)
{
if ((x == (sizeX - 1)) && (y == 0))
{
upperRightNeighbourIsLife = (currentBoard [0] [sizeY - 1] == LIFE);
}
else if (x == (sizeX - 1))
{
upperRightNeighbourIsLife = (currentBoard [0] [y - 1] == LIFE);
}
else if (y == 0)
{
upperRightNeighbourIsLife = (currentBoard [x + 1] [sizeY - 1] == LIFE);
}
}
return upperRightNeighbourIsLife;
}
/**
* Checking cell (x, y) for left life neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if left neighbour is life
*/
private boolean leftNeighbourIsLife (int x, int y)
{
boolean leftNeighbourIsLife = false;
if (x > 0)
{
leftNeighbourIsLife = (currentBoard [x - 1] [y] == LIFE);
}
else if (borderMode == TORODIAL)
{
leftNeighbourIsLife = (currentBoard [sizeX - 1] [y] == LIFE);
}
return leftNeighbourIsLife;
}
/**
* Checking cell (x, y) for right life neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if right neighbour is life
*/
private boolean rightNeighbourIsLife (int x, int y)
{
boolean rightNeighbourIsLife = false;
if (x < (sizeX - 1))
{
rightNeighbourIsLife = (currentBoard [x + 1] [y] == LIFE);
}
else if (borderMode == TORODIAL)
{
rightNeighbourIsLife = (currentBoard [0] [y] == LIFE);
}
return rightNeighbourIsLife;
}
/**
* Checking cell (x, y) for lower left life neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if lower left neighbour is life
*/
private boolean lowerLeftNeighbourIsLife (int x, int y)
{
boolean lowerLeftNeighbourIsLife = false;
if ((x > 0) && (y < (sizeY - 1)))
{
lowerLeftNeighbourIsLife = (currentBoard [x - 1] [y + 1] == LIFE);
}
else if (borderMode == TORODIAL)
{
if ((x == 0) && (y == (sizeY - 1)))
{
lowerLeftNeighbourIsLife = (currentBoard [sizeX - 1] [0] == LIFE);
}
else if (x == 0)
{
lowerLeftNeighbourIsLife = (currentBoard [sizeX - 1] [y + 1] == LIFE);
}
else if (y == (sizeY - 1))
{
lowerLeftNeighbourIsLife = (currentBoard [x - 1] [0] == LIFE);
}
}
return lowerLeftNeighbourIsLife;
}
/**
* Checking cell (x, y) for lower life neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if lower neighbour is life
*/
private boolean lowerNeighbourIsLife (int x, int y)
{
boolean lowerNeighbourIsLife = false;
if (y < (sizeY - 1))
{
lowerNeighbourIsLife = (currentBoard [x] [y + 1] == LIFE);
}
else if (borderMode == TORODIAL)
{
lowerNeighbourIsLife = (currentBoard [x] [0] == LIFE);
}
return lowerNeighbourIsLife;
}
/**
* Checking cell (x, y) for lower left life neighbour
* @param x: horizontal position
* @param y: vertical position
* @return true, if lower left neighbour is life
*/
private boolean lowerRightNeighbourIsLife (int x, int y)
{
boolean lowerRightNeighbourIsLife = false;
if ((x < (sizeX - 1)) && (y < (sizeY - 1)))
{
lowerRightNeighbourIsLife = (currentBoard [x + 1] [y + 1] == LIFE);
}
else if (borderMode == TORODIAL)
{
if ((x == (sizeX - 1)) && (y == (sizeY - 1)))
{
lowerRightNeighbourIsLife = (currentBoard [0] [0] == LIFE);
}
else if (x == (sizeX - 1))
{
lowerRightNeighbourIsLife = (currentBoard [0] [y + 1] == LIFE);
}
else if (y == (sizeY - 1))
{
lowerRightNeighbourIsLife = (currentBoard [x + 1] [0] == LIFE);
}
}
return lowerRightNeighbourIsLife;
}
/**
* Counting all life neighbours of cell (x, y)
* @param x: horizontal position
* @param y: vertical position
* @return number of life neighbours
*/
private long countLifeNeighbours (int x, int y)
{
long numberOfLifeNeighbours = 0;
if (upperLeftNeighbourIsLife (x, y)) {numberOfLifeNeighbours++;}
if (upperNeighbourIsLife (x, y)) {numberOfLifeNeighbours++;}
if (upperRightNeighbourIsLife (x, y)) {numberOfLifeNeighbours++;}
if (leftNeighbourIsLife (x, y)) {numberOfLifeNeighbours++;}
if (rightNeighbourIsLife (x, y)) {numberOfLifeNeighbours++;}
if (lowerLeftNeighbourIsLife (x, y)) {numberOfLifeNeighbours++;}
if (lowerNeighbourIsLife (x, y)) {numberOfLifeNeighbours++;}
if (lowerRightNeighbourIsLife (x, y)) {numberOfLifeNeighbours++;}
return numberOfLifeNeighbours;
}
/**
* Computing next board
*/
public void computeNextBoard ()
{
for (int y = 0; y < sizeY; y++)
{
for (int x = 0; x < sizeX; x++)
{
long numberOfLifeNeighbours = countLifeNeighbours (x, y);
if (numberOfLifeNeighbours == 2)
{
nextBoard [x] [y] = currentBoard [x] [y];
}
else if (numberOfLifeNeighbours == 3)
{
nextBoard [x] [y] = LIFE;
}
else
{
nextBoard [x] [y] = DEAD;
}
}
}
copyNextToCurrentField ();
}
}