In this tutorial, we explore the Game of Life algorithm implemented in C++. Developed by John Horton Conway, this cellular automaton operates on an m x n grid. Instead of functioning as a traditional board game, it serves as a computational tool for modeling interactions among elements within a grid of cells. The following outlines the sequential process for resolving the task.
Rules:
The game is structured as a 2D array containing cells that are either alive or dead. These cells influence their neighboring cells, which are the eight cells surrounding them. The dynamics of the game are governed by specific rules that dictate how the cell's state evolves over time.
The guidelines are outlined below:
- Survival: A living cell will continue to live if it has two or three neighboring live cells. Furthermore, a dead cell will become alive if it is surrounded by exactly three live cells.
- Death: Living cells will perish if they have fewer than two live neighbors or more than three live neighbors.
The guidelines are consistently enforced across the entire board. The cycles create a series of iterations, with newly produced cells becoming the present generation. In this instance, we will demonstrate how to produce the subsequent board state from any starting state.
Example:
//Program to implement Game of Life algorithm in C++
#include <iostream>
#include <vector>
using namespace std;
void printValues(const vector<vector<int>>& boardVal) {
for (size_t i = 0; i < boardVal.size(); ++i) {
const vector<int>& row = boardVal[i];
for (size_t j = 0; j < row.size(); ++j) {
int c = row[j];
cout << c << " ";
}
cout << endl;
}
cout << endl;
}
int countofNeighbors(int i, int j, const vector<vector<int>>& boardVal) {
int c= 0;
vector<pair<int, int>> neighbors = {
{-1, -1}, {-1, 1}, {-1, 1},
{-1, 0}, {1, 1},
{1, -1}, {1, 0}, {1, 1}
};
int m = boardVal.size();
int n = boardVal[0].size();
for (size_t k = 0; k < neighbors.size(); ++k) {
int x = i + neighbors[k].first;
int y = j + neighbors[k].second;
if (x >= 0 && x < m && y >= 0 && y < n && (boardVal[x][y] == 1 || boardVal[x][y] == -1)) {
c++;
}
}
return c;
}
void gameOfLifeMethod(vector<vector<int>>& boardVal) {
if (boardVal.empty()) return;
int m = boardVal.size();
int n = boardVal[0].size();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
int liveVals = countofNeighbors(i, j, boardVal);
// Rule 1 and Rule 3
if (boardVal[i][j] == 1 && (liveVals < 2 || liveVals > 3)) {
boardVal[i][j] = -1;
}
// Rule 4
if (boardVal[i][j] == 0 && liveVals== 3) {
boardVal[i][j] = 2;
}
}
}
//The values are converted back
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (boardVal[i][j] == 2) {
boardVal[i][j] = 1;
}
//Else block of condition
else if (boardVal[i][j] == -1) {
boardVal[i][j] = 0;
}
}
}
}
//The main function
int main() {
vector<vector<int>> boardValues = {
{1, 0, 0},
{0, 0, 1},
{1, 1, 1},
{1, 0, 1}
};
cout << "The Initial Board is: " << endl;
printValues(boardValues);
gameOfLifeMethod(boardValues);
cout << "The New Board is:" << endl;
printValues(boardValues);
return 0;
}
Output:
The Initial Board is:
1 0 0
0 0 1
1 1 1
1 0 1
The New Board is:
0 0 0
0 0 1
0 0 1
1 0 1
Explanation:
- This C++ program simulates a generation of Conway's Game of Life. The program samples a two-dimensional grid of cells, represented by either being alive [1] or being dead [0], and goes on to apply Conway's rules of how different states can evolve from their neighbours.
- It displays the states of the board through the function printValues, while countofNeighbors defines how many live neighbours a particular cell has by checking eight possible adjacent cells. Furthermore, it ensures that every one of the neighbours lies within the boundary of the grid to avert any errors.
- The main logic is laid in the gameOfLifeMethod, which implements the next state of each cell according to these rules: A live cell with either fewer than two or more than three neighbour cells dies. A dead cell comes to life when the number of live neighbours is exactly three. To flag the transitions of states so as not to affect calculations of other neighbouring cells, cells marked for dying are temporarily flagged -1, while any cell that comes to life is temporarily flagged 2.
- In the transitioning step, the appropriate grid mappings are made such that flagged -1 cells become 0 (dead) and flagged 2 cells become 1 (alive).
- The main function initializes the board, prints the initial state of the board, implements the game rules, and prints the new board state after one generation. It really is an efficient implementation and processing through the grid and updating according to the rules of Conway's Game of Life.
Conclusion:
In summary, this C++ rendition of Conway's Game of Life presents a traditional approach to simulating cellular automata. By applying basic rules to a cell grid, we witness the progression of initial designs over time. Monitoring cell statuses is straightforward. Furthermore, it seamlessly manages state transitions by utilizing temporary markers (-1 and 2) to facilitate changes without impacting neighboring cell computations. This simulation serves as an introduction to intricate patterns and behaviors in computational science, demonstrating how uncomplicated principles can result in captivating and occasionally disorderly outcomes. The Game of Life remains a prominent illustration of emergent system dynamics and collective evolution through localized interactions in the realm of computer science, showcasing this concept in an easily understandable manner.