392 lines
15 KiB
C#
392 lines
15 KiB
C#
/*
|
|
* Change History
|
|
* Date Developer Description
|
|
* 2020-01-16 marqusa File creation and initial implementation -- Charles Costarella
|
|
* 2020-01-28 marqusa Game Board Initialization --Charles Costarella
|
|
* Process game board, determine if cell is dead or alive
|
|
* Add main game rule logic & count neighbors.
|
|
* Add a StreamWriter to write out the gameboard per generation to a file.
|
|
*/
|
|
using System;
|
|
using System.IO;
|
|
|
|
namespace GLife
|
|
{
|
|
//ref: Chuck Costarella
|
|
public class Game
|
|
{
|
|
public StreamWriter outfile;
|
|
|
|
//const has to be assigned immediately and can never be changed.
|
|
//Readonly does not need to be assigned immediately.
|
|
// once readonly is assigned, can never be changed.
|
|
|
|
// size the game boards
|
|
public const int ROW_SIZE = 32;
|
|
public const int COL_SIZE = 120;
|
|
|
|
//Define characters for dead or alive cells.
|
|
public const char DEAD = '-';
|
|
public const char LIVE = 'o';
|
|
|
|
// game boards
|
|
//initialize board as two dimentional array
|
|
private char[,] gameBoard;
|
|
private char[,] tempBoard;
|
|
|
|
// Class constructor
|
|
// 1 - Create the object - chunk out memory on the heap for the actual storage.
|
|
// 2 - initialize all relevant variables to reasonable defaults.
|
|
public Game()
|
|
{
|
|
//Create a new file with a name.
|
|
//StreamWriter will make a text file.
|
|
outfile = new StreamWriter("OUTPUT.txt");
|
|
|
|
// construct the two gameboards.
|
|
gameBoard = new char[ROW_SIZE, COL_SIZE];
|
|
tempBoard = new char[ROW_SIZE, COL_SIZE];
|
|
|
|
// playing the game consists of
|
|
// step 1 - initializing the gameboard to some start pattern
|
|
// superimposed over the field
|
|
// the initial field will be all DEAD
|
|
InitializeBoards();
|
|
|
|
// superimpose some start patterns over the initial board.
|
|
InsertStartPatterns();
|
|
}
|
|
|
|
// main processing loop for the game
|
|
// plays the game by setting a number of generations to display
|
|
// and then looping for each generation
|
|
// each generation
|
|
// 1 - displays the results of the current generation
|
|
// 2 - processes the next gen to the results board (tempBoard)
|
|
// 3 - swaps the 2 boards
|
|
//Preconditions: not yet
|
|
//Inputs: no args
|
|
//Outputs: void return
|
|
//Postconditions: not yet
|
|
//ref: Chuck Costarella
|
|
public void PlayGame()
|
|
{
|
|
// get the number of generations to display from the user
|
|
Console.Write("Enter the number of generations: ");
|
|
int numberOfGenerations = int.Parse(Console.ReadLine());
|
|
|
|
for (int generation = 1; generation <= numberOfGenerations; generation++)
|
|
{
|
|
// 1 - displays the results of the current generation
|
|
DisplayBoard(generation);
|
|
|
|
// 2 - processes the next gen to the results board (tempBoard)
|
|
ProcessGameBoard();
|
|
|
|
// 3 - swaps the 2 boards
|
|
char[,] tempLocation = gameBoard;
|
|
gameBoard = tempBoard;
|
|
tempBoard = tempLocation;
|
|
}
|
|
|
|
//release resource
|
|
outfile.Close();
|
|
}
|
|
|
|
//Processes the game board according to
|
|
//Conway's Game of Life rules
|
|
//Iterate through the current gameboard and
|
|
//determine if that cell will live or die (in the next gen)
|
|
//store the results back into the results board (tempBoard)
|
|
//Preconditions: not yet
|
|
//Inputs: no args
|
|
//Outputs: void return
|
|
//Postconditions: not yet
|
|
//ref: Chuck Costarella
|
|
private void ProcessGameBoard()
|
|
{
|
|
// 2 - processes the next gen to the results board (tempBoard)
|
|
// Count the neighbors for each cell
|
|
for (int r = 0; r < ROW_SIZE; r++)
|
|
{
|
|
for (int c = 0; c < COL_SIZE; c++)
|
|
{
|
|
// determine if [r,c[ will live or die
|
|
//store the results back into the results board (tempBoard)
|
|
tempBoard[r, c] = DetermineDeadOrAlive(r,c);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Determine if a specific cell is dead or alive in the next gen by doing the following
|
|
//count the neighbors to get a total
|
|
//apply the rules of the game.
|
|
//Preconditions: not yet
|
|
//Inputs: int row, int column
|
|
//Outputs: char DEAD or char ALIVE.
|
|
//Postconditions: not yet
|
|
//ref: Chuck Costarella
|
|
private char DetermineDeadOrAlive(int r, int c)
|
|
{
|
|
//YOU SPIN ME RIGHT ROUND BABY
|
|
//RIGHT ROUND
|
|
//LIKE A RECORD BABY RIGHT ROUND ROUND ROUND
|
|
|
|
//count the neighbors to get a total
|
|
int count = CountNeighbors(r, c);
|
|
|
|
//apply the rules of the game
|
|
|
|
//Any live cell with fewer than two live neighbours dies, as if by underpopulation.
|
|
//Any live cell with two or three live neighbours lives on to the next generation.
|
|
//Any live cell with more than three live neighbours dies, as if by overpopulation.
|
|
//Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
|
|
|
|
if (count == 2) return gameBoard[r, c]; //status quo rule
|
|
else if (count == 3) return LIVE; //spontaneous birth
|
|
else return DEAD; //It's dead, Jim.
|
|
}
|
|
|
|
//Inserts start patterns to both game boards
|
|
//Preconditions: not yet
|
|
//Inputs: int row, int column
|
|
//Outputs: void return
|
|
//Postconditions: not yet
|
|
//ref: Chuck Costarella
|
|
private int CountNeighbors(int r, int c)
|
|
{
|
|
int count = 0;
|
|
|
|
if(r == 0 && c == 0)
|
|
{
|
|
//EXCEPTIONAL CASE -- Top Left corner
|
|
//if (gameBoard[r - 1, c - 1] == LIVE) count++;
|
|
//if (gameBoard[r - 1, c] == LIVE) count++;
|
|
//if (gameBoard[r - 1, c + 1] == LIVE) count++;
|
|
//if (gameBoard[r, c - 1] == LIVE) count++;
|
|
if (gameBoard[r, c + 1] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c] == LIVE) count++;
|
|
if (gameBoard[r + 1, c + 1] == LIVE) count++;
|
|
}
|
|
else if(r==0 && c==COL_SIZE-1)
|
|
{
|
|
//EXCEPTIONAL CASE -- Top Right corner
|
|
//if (gameBoard[r - 1, c - 1] == LIVE) count++;
|
|
//if (gameBoard[r - 1, c] == LIVE) count++;
|
|
//if (gameBoard[r - 1, c + 1] == LIVE) count++;
|
|
if (gameBoard[r, c - 1] == LIVE) count++;
|
|
//if (gameBoard[r, c + 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c + 1] == LIVE) count++;
|
|
}
|
|
else if (r==ROW_SIZE-1 && c==0)
|
|
{
|
|
//EXCEPTIONAL CASE -- Bottom Left corner
|
|
//if (gameBoard[r - 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r - 1, c] == LIVE) count++;
|
|
if (gameBoard[r - 1, c + 1] == LIVE) count++;
|
|
//if (gameBoard[r, c - 1] == LIVE) count++;
|
|
if (gameBoard[r, c + 1] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c - 1] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c + 1] == LIVE) count++;
|
|
}
|
|
else if (r==ROW_SIZE-1 && c==COL_SIZE-1)
|
|
{
|
|
//EXCEPTIONAL CASE -- Bottom Right corner
|
|
if (gameBoard[r - 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r - 1, c] == LIVE) count++;
|
|
//if (gameBoard[r - 1, c + 1] == LIVE) count++;
|
|
if (gameBoard[r, c - 1] == LIVE) count++;
|
|
//if (gameBoard[r, c + 1] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c - 1] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c + 1] == LIVE) count++;
|
|
}
|
|
else if (r==0)
|
|
{
|
|
//EXCEPTIONAL CASE -- Top Edge
|
|
//if (gameBoard[r - 1, c - 1] == LIVE) count++;
|
|
//if (gameBoard[r - 1, c] == LIVE) count++;
|
|
//if (gameBoard[r - 1, c + 1] == LIVE) count++;
|
|
if (gameBoard[r, c - 1] == LIVE) count++;
|
|
if (gameBoard[r, c + 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c] == LIVE) count++;
|
|
if (gameBoard[r + 1, c + 1] == LIVE) count++;
|
|
}
|
|
else if (c==COL_SIZE-1)
|
|
{
|
|
//EXCEPTIONAL CASE -- Right Edge
|
|
if (gameBoard[r - 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r - 1, c] == LIVE) count++;
|
|
//if (gameBoard[r - 1, c + 1] == LIVE) count++;
|
|
if (gameBoard[r, c - 1] == LIVE) count++;
|
|
//if (gameBoard[r, c + 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c + 1] == LIVE) count++;
|
|
}
|
|
else if (r==ROW_SIZE-1)
|
|
{
|
|
//EXCEPTIONAL CASE -- Bottom Edge
|
|
if (gameBoard[r - 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r - 1, c] == LIVE) count++;
|
|
if (gameBoard[r - 1, c + 1] == LIVE) count++;
|
|
if (gameBoard[r, c - 1] == LIVE) count++;
|
|
if (gameBoard[r, c + 1] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c - 1] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c + 1] == LIVE) count++;
|
|
}
|
|
else if (c==0)
|
|
{
|
|
//EXCEPTIONAL CASE -- Left Edge
|
|
//if (gameBoard[r - 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r - 1, c] == LIVE) count++;
|
|
if (gameBoard[r - 1, c + 1] == LIVE) count++;
|
|
//if (gameBoard[r, c - 1] == LIVE) count++;
|
|
if (gameBoard[r, c + 1] == LIVE) count++;
|
|
//if (gameBoard[r + 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c] == LIVE) count++;
|
|
if (gameBoard[r + 1, c + 1] == LIVE) count++;
|
|
}
|
|
else
|
|
{
|
|
//No edges, no corners.
|
|
if (gameBoard[r - 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r - 1, c] == LIVE) count++;
|
|
if (gameBoard[r - 1, c + 1] == LIVE) count++;
|
|
if (gameBoard[r, c - 1] == LIVE) count++;
|
|
if (gameBoard[r, c + 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c - 1] == LIVE) count++;
|
|
if (gameBoard[r + 1, c] == LIVE) count++;
|
|
if (gameBoard[r + 1, c + 1] == LIVE) count++;
|
|
}
|
|
return count;
|
|
} //end CountNeighbors
|
|
|
|
//Inserts start patterns to both game boards
|
|
//Preconditions: not yet
|
|
//Inputs: no args
|
|
//Outputs: void return
|
|
//Postconditions: not yet
|
|
//ref: Chuck Costarella
|
|
private void InsertStartPatterns()
|
|
{
|
|
//row, column
|
|
InsertStartPattern1(20,25);
|
|
}
|
|
|
|
//Initializes the game boards to the start state of raw storage
|
|
//Preconditions: not yet
|
|
//Inputs: no args
|
|
//Outputs: void return
|
|
//Postconditions: not yet
|
|
//ref: Chuck Costarella
|
|
private void InitializeBoards()
|
|
{
|
|
//iterate through each row
|
|
for (int r = 0; r < ROW_SIZE; r++)
|
|
{
|
|
//iterate through each column in an individual row
|
|
for (int c = 0; c < COL_SIZE; c++)
|
|
{
|
|
//init all cells in boath boards to the init start status - DEAD
|
|
gameBoard[r, c] = DEAD;
|
|
tempBoard[r, c] = DEAD;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Initializes the game boards to the start state of raw storage
|
|
//Preconditions: not yet
|
|
//Inputs: integer number of the current generation
|
|
//Outputs: void return
|
|
//Postconditions: not yet
|
|
//ref: Chuck Costarella
|
|
public void DisplayBoard(int generation)
|
|
{
|
|
//Move the cursor to overwrite the old gameboard.
|
|
Console.SetCursorPosition(0, Console.WindowTop + 1);
|
|
|
|
Console.WriteLine($"GENERATION #: {generation}");
|
|
outfile.WriteLine($"GENERATION #: {generation}");
|
|
|
|
//iterate through each row
|
|
for (int r = 0; r < ROW_SIZE; r++)
|
|
{
|
|
//iterate through each column in an individual row
|
|
for (int c = 0; c < COL_SIZE; c++)
|
|
{
|
|
//Write the cell [DEAD OR LIVE] to the console.
|
|
Console.Write(gameBoard[r, c]);
|
|
outfile.Write(gameBoard[r, c]);
|
|
}
|
|
|
|
//insert CRLF at the end of every row
|
|
Console.WriteLine();
|
|
outfile.WriteLine();
|
|
}
|
|
}
|
|
|
|
//Inserts start patterns to both game boards
|
|
//Preconditions: not yet
|
|
//Inputs: integer row, integer column
|
|
//Outputs: void return
|
|
//Postconditions: not yet
|
|
//ref: Chuck Costarella
|
|
private void InsertStartPattern1(int r, int c)
|
|
{
|
|
//8 LIVE cells
|
|
gameBoard[r, c + 1] = LIVE;
|
|
gameBoard[r, c + 2] = LIVE;
|
|
gameBoard[r, c + 3] = LIVE;
|
|
gameBoard[r, c + 4] = LIVE;
|
|
gameBoard[r, c + 5] = LIVE;
|
|
gameBoard[r, c + 6] = LIVE;
|
|
gameBoard[r, c + 7] = LIVE;
|
|
gameBoard[r, c + 8] = LIVE;
|
|
|
|
//1 DEAD SPACE - c == 9
|
|
|
|
//5 LIVE cells
|
|
gameBoard[r, c + 10] = LIVE;
|
|
gameBoard[r, c + 11] = LIVE;
|
|
gameBoard[r, c + 12] = LIVE;
|
|
gameBoard[r, c + 13] = LIVE;
|
|
gameBoard[r, c + 14] = LIVE;
|
|
|
|
//3 DEAD cells - c == 15,16,17
|
|
|
|
//3 LIVE cells
|
|
gameBoard[r, c + 18] = LIVE;
|
|
gameBoard[r, c + 19] = LIVE;
|
|
gameBoard[r, c + 20] = LIVE;
|
|
|
|
//6 DEAD cells - c == 21-26
|
|
|
|
//7 LIVE cells
|
|
gameBoard[r, c + 27] = LIVE;
|
|
gameBoard[r, c + 28] = LIVE;
|
|
gameBoard[r, c + 29] = LIVE;
|
|
gameBoard[r, c + 30] = LIVE;
|
|
gameBoard[r, c + 31] = LIVE;
|
|
gameBoard[r, c + 32] = LIVE;
|
|
gameBoard[r, c + 33] = LIVE;
|
|
|
|
//1 DEAD cell c==34
|
|
|
|
//5 LIVE cells
|
|
gameBoard[r, c + 35] = LIVE;
|
|
gameBoard[r, c + 36] = LIVE;
|
|
gameBoard[r, c + 37] = LIVE;
|
|
gameBoard[r, c + 38] = LIVE;
|
|
gameBoard[r, c + 39] = LIVE;
|
|
|
|
} //END InsertStartPattern1
|
|
|
|
} // END class
|
|
} // END namespace |