TINFO200/GLife/Game.cs
2022-03-15 21:16:45 -07:00

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