Jack P. Oakley Portfolio Game Programmer/Designer

Conway’s Game of Life

This program uses multidimensional arrays to run through the algorithm for Conway’s Game of Life.

//Conway's Game of Life by Jack P. Oakley
//This program gives various options to play Conway's Game of Life as referenced
//from https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life

#include <iostream>
using namespace std;
#include <fstream>
#include <stdlib.h>
#include <time.h>

const int maxHeight = 25;
const int maxWidth = 45;

void title(void);
void newBoard(char board[][maxWidth], int &rows, int &columns);
void createBoard(char board[][maxWidth], int &numRows, int &numCols);
void loadFile(int &numRows, int &numCols, char board[][maxWidth]);
void useBoard(char board[][maxWidth], int &rows, int &columns);
void border(char board[][maxWidth], int rows, int columns);
void displayBoard(char board[][maxWidth], int numRows, int numCols);
void liveCount(char board[][maxWidth], int numRows, int numCols);
void holdScreen(void);
int menu(void);
int getTurns(void);
void playGame(char board[][maxWidth], int numRows, int numCols, int numTurns);
void restartGame(char board[][maxWidth], char savedBoard[][maxWidth],
	int numRows, int numCols);
void nextGeneration(char board[][maxWidth], int numRows, int numCols);
int neighbors(char board[][maxWidth], int rix, int cix);
void copyArray(char destArray[][maxWidth], char sourceArray[][maxWidth],
	int numRows, int numCols);
void animation(char board[][maxWidth], int numRows, int numCols, int numTurns);
void randomBoard(char board[][maxWidth], int &rows, int &columns);

/*******************************************************************************
* main function:                                                               *
* PURPOSE: Drives the program                                                  *
* IN: none                                                                     *
* OUT: none                                                                    *
*******************************************************************************/
int main()
{
	fstream infile;
	srand(time(NULL));
	int option, numRows, numCols, numTurns;
	char board[maxHeight][maxWidth];
	char savedBoard[maxHeight][maxWidth];

	title();
	newBoard(board, numRows, numCols);
	border(board, numRows, numCols);
	displayBoard(board, numRows, numCols);
	liveCount(board, numRows, numCols);
	holdScreen();
	copyArray(savedBoard, board, numRows, numCols);

	do
	{
		option = menu();

		if (option == 1)
		{
			numTurns = getTurns();
			playGame(board, numRows, numCols, numTurns);
		}
		else if (option == 2)
		{
			restartGame(board, savedBoard, numRows, numCols);
		}
		else if (option == 3)
		{
			displayBoard(board, numRows, numCols);
			liveCount(board, numRows, numCols);
			holdScreen();
		}
		else if (option == 4)
		{
			newBoard(board, numRows, numCols);
			border(board, numRows, numCols);
			displayBoard(board, numRows, numCols);
			liveCount(board, numRows, numCols);
			holdScreen();
			copyArray(savedBoard, board, numRows, numCols);
		}
		else if (option == 5)
		{
			numTurns = 100;
			animation(board, numRows, numCols, numTurns);
		}
	} while (option != 6);
} //End main()

/*******************************************************************************
* title function:                                                              *
* PURPOSE: Display title & description by keeping main() a little cleaner      *
* IN: none                                                                     *
* OUT: none                                                                    *
*******************************************************************************/
void title(void)
{
	cout << "Conway's Game of Life" << endl << endl;
	cout << "This program gives multiple options for Conway's Game of Life."
		<< endl << endl;
} //End title()

/*******************************************************************************
* newBoard function:                                                           *
* PURPOSE: Give user a choice for how to start a new board and select the      *
* 		associated method                                                      *
* IN: 2-D array to hold new board, 2 int containers to hold row size & col size*
* OUT: Reference Params: 2-D array, 2 ints                                     *
*******************************************************************************/
void newBoard(char board[][maxWidth], int &rows, int &columns)
{
	fstream infile;
	int choice;

	do
	{
		cout << "You have the following options:" << endl;
		cout << "[1] Enter New Board via Keyboard" << endl;
		cout << "[2] Load Board from Data File" << endl;
		cout << "[3] Use Predefined Board" << endl;
		cout << "[4] Generate Random Board" << endl;
		cin >> choice;
		if (choice < 1 || choice > 4)
		{
			cout << "Please enter an option from 1 to 4." << endl;
		}
	} while (choice < 1 || choice > 4);

	if (choice == 1)
	{
		createBoard(board, rows, columns);
	}
	else if (choice == 2)
	{
		loadFile(rows, columns, board);
	}
	else if (choice == 3)
	{
		useBoard(board, rows, columns);
	}
	else
	{
		randomBoard(board, rows, columns);
	}
} //End newBoard()

/*******************************************************************************
* createBoard function:                                                        *
* PURPOSE: Create a new board by way of keyboard entry                         *
* IN: 2-D array to hold new board, 2 int containers to hold row size & col size*
* OUT: Reference Params: 2-D array, 2 ints                                     *
*******************************************************************************/
void createBoard(char board[][maxWidth], int &numRows, int &numCols)
{
	int rix, cix, liveR, liveC;
	char another;
	const int minRows = 5;
	const int minCols = 5;
	const int maxRows = 20;
	const int maxCols = 40;

	do
	{
		cout << "Enter the number of rows (" << minRows << "-" << maxRows 
			<< "): ";
		cin >> numRows;
		if (numRows < minRows || numRows > maxRows)
		{
			cout << "Please enter a value from " << minRows << " to "
				<< maxRows << "." << endl;
		}
	} while (numRows < minRows || numRows > maxRows);

	do
	{
		cout << "Enter the number of columns (" << minCols << "-" << maxCols
			<< "): ";
		cin >> numCols;
		if (numCols < minCols || numCols > maxCols)
		{
			cout << "Please enter a value from " << minCols << " to "
				<< maxCols << "." << endl;
		}
	} while (numCols < minCols || numCols > maxCols);

	for (rix = 0; rix < numRows + 2; ++rix)
	{
		for (cix = 0; cix < numCols + 2; ++cix)
		{
			board[rix][cix] = ' ';
		}
	}

	do
	{
		do
		{
			cout << "Enter a row number for a live cell: ";
			cin >> liveR;
			if (liveR < 1 || liveR > numRows)
			{
				cout << "Please enter a row from 1 to " << numRows << "." 
					<< endl;
			}
		} while (liveR < 1 || liveR > numRows);

		do
		{
			cout << "Enter a column number for the cell: ";
			cin >> liveC;
			if (liveC < 1 || liveC > numCols)
			{
				cout << "Please enter a column from 1 to " << numCols << "."
					<< endl;
			}
		} while (liveC < 1 || liveC > numCols);

		board[liveR][liveC] = '*';

		do
		{
			cout << "Do you want to enter another live cell? (y/n) ";
			cin >> another;
			if (another != 'y' && another != 'Y' && another != 'n' &&
				another != 'N')
			{
				cout << "Please enter either a y or an n." << endl;
			}
		} while (another != 'y' && another != 'Y' && another != 'n' &&
			another != 'N');

	} while (another == 'y' || another == 'Y');
	cout << endl;
} //End createBoard()

/*******************************************************************************
* loadFile function:                                                           *
* PURPOSE: Create a new board by way of loading a specified file               *
* IN: 2-D array to hold new board, 2 int containers to hold row size & col size*
* OUT: Reference Params: 2-D array, 2 ints                                     *
* ADTNL NOTE: 2 Examples are provided in .\ConwaysGameGrids\, 5x10Grid.txt &   *
*		cat.txt                                                                *
*******************************************************************************/
void loadFile(int &numRows, int &numCols, char board[][maxWidth])
{
	fstream infile;
	char filePath[101];
	int rix, cix;

	cout << "Please enter the entire file path to the file including its "
		<< "name and extension: ";
	cin >> filePath;

	infile.open(filePath, ios::in);

	infile >> numRows >> numCols;

	for (rix = 1; rix <= numRows; ++rix)
	{
		for (cix = 1; cix <= numCols; ++cix)
		{
			infile >> board[rix][cix];
			if (board[rix][cix] == '-')
			{
				board[rix][cix] = ' ';
			}
		}
	}

	infile.close();
} //End loadFile()

/*******************************************************************************
* useBoard function:                                                           *
* PURPOSE: Create a new board by way of precoded entry                         *
* IN: 2-D array to hold new board, 2 int containers to hold row size & col size*
* OUT: Reference Params: 2-D array, 2 ints                                     *
*******************************************************************************/
void useBoard(char board[][maxWidth], int &rows, int &columns)
{
	int rix, cix;

	rows = 15;
	columns = 20;

	board[1][1] = '*';
	board[1][2] = '*';
	board[1][4] = '*';
	board[1][10] = '*';
	board[1][11] = '*';
	board[1][12] = '*';
	board[1][13] = '*';
	board[1][14] = '*';
	board[1][15] = '*';
	board[1][18] = '*';
	board[1][19] = '*';
	board[1][20] = '*';
	board[2][1] = '*';
	board[2][4] = '*';
	board[2][19] = '*';
	board[3][4] = '*';
	board[3][12] = '*';
	board[3][13] = '*';
	board[3][14] = '*';
	board[3][15] = '*';
	board[3][20] = '*';
	board[4][20] = '*';
	board[5][20] = '*';
	board[6][4] = '*';
	board[6][12] = '*';
	board[6][13] = '*';
	board[6][14] = '*';
	board[6][15] = '*';
	board[7][13] = '*';
	board[7][14] = '*';
	board[7][20] = '*';
	board[9][1] = '*';
	board[9][2] = '*';
	board[9][3] = '*';
	board[9][4] = '*';
	board[10][1] = '*';
	board[10][4] = '*';
	board[11][1] = '*';
	board[11][2] = '*';
	board[11][3] = '*';
	board[11][4] = '*';
	board[11][7] = '*';
	board[11][8] = '*';
	board[11][9] = '*';
	board[11][10] = '*';
	board[12][7] = '*';
	board[12][8] = '*';
	board[12][9] = '*';
	board[12][10] = '*';
	board[13][7] = '*';
	board[13][8] = '*';
	board[13][9] = '*';
	board[13][10] = '*';
	board[12][15] = '*';
	board[13][15] = '*';
	board[12][16] = '*';
	board[13][16] = '*';
	board[12][19] = '*';
	board[13][19] = '*';
	board[12][20] = '*';
	board[13][20] = '*';
	board[10][17] = '*';
	board[10][18] = '*';
	board[11][17] = '*';
	board[11][18] = '*';
	board[14][17] = '*';
	board[14][18] = '*';
	board[15][17] = '*';
	board[15][18] = '*';
	board[14][1] = '*';
	board[14][2] = '*';
	board[15][2] = '*';

	for (rix = 1; rix < rows + 1; ++rix)
	{
		for (cix = 1; cix < columns + 1; ++cix)
		{
			if (board[rix][cix] != '*')
			{
				board[rix][cix] = ' ';
			}
		}
	}
} //End useBoard()

/*******************************************************************************
* border function:                                                             *
* PURPOSE: Inserts a border around game board                                  *
* IN: 2-D array of game board, 2 ints: row size & col size                     *
* OUT: Reference Param: 2-D array                                              *
*******************************************************************************/
void border(char board[][maxWidth], int rows, int columns)
{
	int ix;
	const int borderTop = 0;
	const int borderLeft = 0;
	const int borderRight = columns + 1;
	const int borderBottom = rows + 1;

	for (ix = 0; ix < borderRight; ++ix)
	{
		board[borderTop][ix] = '|';
		board[borderBottom][ix] = '|';
	}
	for (ix = 0; ix < borderBottom + 1; ++ix)
	{
		board[ix][borderRight] = '|';
		board[ix][borderLeft] = '|';
	}
} //End border()

/*******************************************************************************
* menu function:                                                               *
* PURPOSE: Display user options and obtain user's choice                       *
* IN: none                                                                     *
* OUT: Returns: int of user's chosen menu option                               *
*******************************************************************************/
int menu(void)
{
	int choice;

	do
	{
		cout << "Please select an option:" << endl;
		cout << "[1] Play Conway's Game of Life" << endl;
		cout << "[2] Restart the Game" << endl;
		cout << "[3] Display the Current Board" << endl;
		cout << "[4] Enter a New Board" << endl;
		cout << "[5] Animate Conway's Game of Life" << endl;
		cout << "[6] Stop the Program" << endl;
		cin >> choice;
		if (choice < 1 || choice > 6)
		{
			cout << "Please enter an option from 1 to 6." << endl;
		}
	} while (choice < 1 || choice > 6);

	return choice;
} //End menu()

/*******************************************************************************
* displayBoard function:                                                       *
* PURPOSE: Print board to screen in user friendly format                       *
* IN: 2-D array of game board, 2 ints: row size & col size                     *
* OUT: none                                                                    *
*******************************************************************************/
void displayBoard(char board[][maxWidth], int numRows, int numCols)
{
	int row, column;

	for (row = 0; row < numRows + 2; ++row)
	{
		for (column = 0; column < numCols + 2; ++column)
		{
			cout << board[row][column];
		}
		cout << endl;
	}
	cout << endl;
} //End displayBoard()

/*******************************************************************************
* liveCount function:                                                          *
* PURPOSE: Count number of cells that are alive & display total as well as %   *
* IN: 2-D array of game board, 2 ints: row size & col size                     *
* OUT: none                                                                    *
*******************************************************************************/
void liveCount(char board[][maxWidth], int numRows, int numCols)
{
	int rix, cix, totalSquares, numLive = 0;
	double percentLive;

	for (rix = 1; rix <= numRows; ++rix)
	{
		for (cix = 1; cix <= numCols; ++cix)
		{
			if (board[rix][cix] == '*')
				++numLive;
		}
	}

	totalSquares = numRows * numCols;
	percentLive = numLive / (double)totalSquares;

	cout << "Rows: " << numRows << ",\tColumns: " << numCols << ",\tLive: "
		<< numLive << ",\tPercent: " << percentLive << endl;
} //End liveCount()

/*******************************************************************************
* holdScreen function:                                                         *
* PURPOSE: Holds the screen from scrolling so user can see board               *
* IN: none                                                                     *
* OUT: none                                                                    *
*******************************************************************************/
void holdScreen(void)
{
	char pause;

	cout << "Enter any letter then press enter to continue: ";
	cin >> pause;
	cout << endl;
} //End holdScreen()

/*******************************************************************************
* getTurns function:                                                           *
* PURPOSE: Obtain number of iterations user wants to go through                *
* IN: none                                                                     *
* OUT: Returns: int of number of iterations user chose                         *
* ADTNL NOTE: Prompts user to confirm if 50 or more iterations chosen          *
*******************************************************************************/
int getTurns(void)
{
	int numTurns;
	char confirm;

	do
	{
		cout << "How many turns do you want to play for? ";
		cin >> numTurns;
		if (numTurns <= 0)
		{
			cout << "Please enter a positive value." << endl;
		}
		else if (numTurns >= 50)
		{
			do
			{
				cout << "Are you sure? (y/n) It may take awhile to go through "
					<< numTurns << " turns. ";
				cin >> confirm;
				if (confirm != 'y' && confirm != 'Y' && confirm != 'n' &&
					confirm != 'N')
				{
					cout << "Please enter a y or n." << endl;
				}
			} while (confirm != 'y' && confirm != 'Y' && confirm != 'n' && 
				confirm != 'N');
		}
	} while (numTurns <= 0);

	return numTurns;
} //End getTurns()

/*******************************************************************************
* playGame function:                                                           *
* PURPOSE: Run through specified number of iterations, holding screen for each *
*		display                                                                *
* IN: 2-D array of game board, 3 ints: row size, col size, & num iterations    *
* OUT: none                                                                    *
*******************************************************************************/
void playGame(char board[][maxWidth], int numRows, int numCols, int numTurns)
{
	int ix;

	for (ix = 0; ix < numTurns; ++ix)
	{
		displayBoard(board, numRows, numCols);
		liveCount(board, numRows, numCols);
		holdScreen();
		nextGeneration(board, numRows, numCols);
	}
} //End playGame()

/*******************************************************************************
* nextGeneration function:                                                     *
* PURPOSE: Determines which cells live, die, & birth, then updates board       *
* IN: 2-D array of game board, 2 ints row size & col size                      *
* OUT: Reference Param: 2-D array                                              *
*******************************************************************************/
void nextGeneration(char board[][maxWidth], int numRows, int numCols)
{
	int rix, cix, adjacent;
	char newGenBoard[maxHeight][maxWidth];

	copyArray(newGenBoard, board, numRows, numCols);

	for (rix = 1; rix <= numRows; ++rix)
	{
		for (cix = 1; cix <= numCols; ++cix)
		{
			adjacent = neighbors(board, rix, cix);

			if (board[rix][cix] == '*')
			{
				if (adjacent < 2 || adjacent > 3)
					newGenBoard[rix][cix] = ' ';
			}
			else if (board[rix][cix] == ' ' && adjacent == 3)
				newGenBoard[rix][cix] = '*';
		}
	}
	border(newGenBoard, numRows, numCols);
	copyArray(board, newGenBoard, numRows, numCols);
} //End nextGeneration()

/*******************************************************************************
* restartGame function:                                                        *
* PURPOSE: Reset to original chosen board                                      *
* IN: 2 2-D arrays: game board & original board, 2 ints: row size & col size   *
* OUT: Reference Param: 2-D array                                              *
*******************************************************************************/
void restartGame(char board[][maxWidth], char savedBoard[][maxWidth], 
	int numRows, int numCols)
{
	copyArray(board, savedBoard, numRows, numCols);
} //End restartGame()

/*******************************************************************************
* neighbors function:                                                          *
* PURPOSE: Count number of living neighbors to a given cell (iteratively)      *
* IN: 2-D array of game board, 2 ints: row & col of current cell               *
* OUT: Returns: int of neighbor count                                          *
*******************************************************************************/
int neighbors(char board[][maxWidth], int rix, int cix)
{
	int numAdjacent = 0;

	if (board[rix - 1][cix - 1] == '*')
		++numAdjacent;
	if (board[rix - 1][cix] == '*')
		++numAdjacent;
	if (board[rix - 1][cix + 1] == '*')
		++numAdjacent;
	if (board[rix][cix - 1] == '*')
		++numAdjacent;
	if (board[rix][cix + 1] == '*')
		++numAdjacent;
	if (board[rix + 1][cix - 1] == '*')
		++numAdjacent;
	if (board[rix + 1][cix] == '*')
		++numAdjacent;
	if (board[rix + 1][cix + 1] == '*')
		++numAdjacent;

	return numAdjacent;
} //End neighbors()

/*******************************************************************************
* copyArray function:                                                          *
* PURPOSE: Make an exact copy of an array                                      *
* IN: 2 2-D arrays: source & destination, 2 ints: row size & col size          *
* OUT: Reference Param: 2-D array                                              *
*******************************************************************************/
void copyArray(char destArray[][maxWidth], char sourceArray[][maxWidth], 
	int numRows, int numCols)
{
	int rix, cix;

	for (rix = 0; rix < numRows + 2; ++rix)
	{
		for (cix = 0; cix < numCols + 2; ++cix)
		{
			destArray[rix][cix] = sourceArray[rix][cix];
		}
	}
} //End copyArray()

/*******************************************************************************
* animation function:                                                          *
* PURPOSE: Simulate an animation                                               *
* IN: 2-D array of game board, 3 ints: row size, col size, & num iterations    *
* OUT: none                                                                    *
* ADTNL NOTE: Animation is simulated by doing an additional loop enough times  *
*		to slow down the console window scrolling enough to see each generation*
*		of board go by without the need to hold screen. The faster the         *
*		processor this is run on, the faster the animation.  If animation is   *
*		too fast to see, increase 'timerStart' to a larger value.              *
*******************************************************************************/
void animation(char board[][maxWidth], int numRows, int numCols, int numTurns)
{
	int ix;
	double timer;
	const double timerStart = 50000000; 
	const int timerEnd = 0;

	for (ix = 0; ix < numTurns; ++ix)
	{
		displayBoard(board, numRows, numCols);
		nextGeneration(board, numRows, numCols);
		cout << endl << endl << endl;

		timer = timerStart;
		do
		{
			--timer;
		} while (timer > timerEnd);
	}
} //End animation()

/*******************************************************************************
* randomBoard function:                                                        *
* PURPOSE: Create a new board by way of random num generation                  *
* IN: 2-D array to hold new board, 2 int containers to hold row size & col size*
* OUT: Reference Params: 2-D array, 2 ints                                     *
* ADTNL NOTE: Row size & col size are chosen randomly, then each cell has 50%  *
*		chance of being alive/empty                                            *
*******************************************************************************/
void randomBoard(char board[][maxWidth], int &rows, int &columns)
{
	int rix, cix, deadAlive;
	const int minHW = 5;
	
	rows = rand() % (maxHeight - minHW - 5) + minHW;
	columns = rand() % (maxWidth - minHW - 5) + minHW;

	for (rix = 1; rix <= rows; ++rix)
	{
		for (cix = 1; cix <= columns; ++cix)
		{
			deadAlive = rand() % 2;
			if (deadAlive == 0)
				board[rix][cix] = ' ';
			else if (deadAlive == 1)
				board[rix][cix] = '*';
		}
	}
} //End randomBoard()