Game of Life/ GameOfLifeModel

/*
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 ();
	}
}