This example implements the famous Conway's Game of Life cellular automaton, demonstrating emergent complexity from simple rules. The simulation shows how patterns evolve over time, including gliders (moving patterns), oscillators (repeating patterns), and random cellular interactions.
#include <chrono>
#include <iostream>
#include <random>
#include <string>
#include <thread>
#include <utility>
#include <vector>
private:
int _width, _height;
std::vector<std::vector<int>> _grid;
std::vector<std::vector<int>> _nextGrid;
public:
GameOfLife(
int w,
int h) : _width(w), _height(h) {
_grid = std::vector<std::vector<int>>(_height, std::vector<int>(_width, 0));
_nextGrid =
std::vector<std::vector<int>>(_height, std::vector<int>(_width, 0));
}
std::random_device rd;
std::mt19937 gen(rd());
std::bernoulli_distribution dist(probability);
for (int y = 0; y < _height; y++) {
for (int x = 0; x < _width; x++) {
_grid[y][x] = dist(gen) ? 1 : 0;
}
}
}
std::vector<std::pair<int, int>> glider = {
{1, 0}, {2, 1}, {0, 2}, {1, 2}, {2, 2}};
for (const auto &[dx, dy] : glider) {
int x = startX + dx;
int y = startY + dy;
if (x >= 0 && x < _width && y >= 0 && y < _height) {
_grid[y][x] = 1;
}
}
}
for (int i = 0; i < 3; i++) {
int x = startX + i;
int y = startY;
if (x >= 0 && x < _width && y >= 0 && y < _height) {
_grid[y][x] = 1;
}
}
}
int count = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if (dx == 0 && dy == 0)
continue;
int nx = x + dx;
int ny = y + dy;
nx = (nx + _width) % _width;
ny = (ny + _height) % _height;
count += _grid[ny][nx];
}
}
return count;
}
for (int y = 0; y < _height; y++) {
for (int x = 0; x < _width; x++) {
int cell = _grid[y][x];
if (cell == 1) {
if (neighbors < 2 || neighbors > 3) {
_nextGrid[y][x] = 0;
} else {
_nextGrid[y][x] = 1;
}
} else {
if (neighbors == 3) {
_nextGrid[y][x] = 1;
} else {
_nextGrid[y][x] = 0;
}
}
}
}
_grid.swap(_nextGrid);
}
[[nodiscard]]
auto getGrid()
const ->
const std::vector<std::vector<int>> & {
return _grid;
}
int count = 0;
for (const auto &row : _grid) {
for (int cell : row) {
count += cell;
}
}
return count;
}
};
std::cout << "Starting Conway's Game of Life..." << '\n';
const int width = 50;
const int height = 50;
const int generations = 200;
const int stepDelay = 100;
game.randomize(0.15);
game.addGlider(5, 5);
game.addGlider(15, 25);
game.addOscillator(30, 10);
game.addOscillator(35, 35);
std::vector<double> xCoords, yCoords;
for (int x = 0; x < width; x++) {
xCoords.push_back(x);
}
for (int y = 0; y < height; y++) {
yCoords.push_back(y);
}
{"type", "heatmap"},
{"x", xCoords},
{"y", yCoords},
{"z", game.getGrid()},
{"colorscale",
{
{0.0, "white"},
{1.0, "black"}
}},
{"showscale", false},
{"hovertemplate", "Cell (%{x}, %{y})<br>State: %{z}<extra></extra>"}};
{"title",
{{"text", "Conway's Game of Life<br>" +
std::string("<sub>Generation 0 - Live Cells: ") +
std::to_string(game.countLiveCells()) + "</sub>"},
{"font", {{"size", 16}}}}},
{"xaxis",
{{"title", "X"}, {"showgrid", false}, {"showticklabels", false}}},
{"yaxis",
{
{"title", "Y"},
{"showgrid", false},
{"showticklabels", false},
{"scaleanchor", "x"},
{"autorange", "reversed"}
}},
{"width", 800},
{"height", 800},
{"margin", {{"l", 50}, {"r", 50}, {"t", 80}, {"b", 50}}}};
std::vector<plotly::Object> data = {trace};
std::cout << "Starting simulation with " << game.countLiveCells()
<< " initial live cells..." << '\n';
std::cout
<< "Patterns: Gliders (moving), Oscillators (blinking), Random cells"
<< '\n';
for (
int generation = 1; generation <= generations && fig.
isOpen();
generation++) {
game.step();
int liveCells = game.countLiveCells();
fig.
restyle({{
"z", {game.getGrid()}}}, {0});
{"title",
{{"text",
"Conway's Game of Life<br>" + std::string("<sub>Generation ") +
std::to_string(generation) +
" - Live Cells: " + std::to_string(liveCells) + "</sub>"},
{"font", {{"size", 16}}}}}};
std::this_thread::sleep_for(std::chrono::milliseconds(stepDelay));
if (generation % 25 == 0) {
std::cout << "Generation " << generation << ": " << liveCells
<< " live cells" << '\n';
}
if (liveCells == 0) {
std::cout << "Population died out at generation " << generation << '\n';
break;
}
}
{{"title",
{{"text", "Conway's Game of Life - SIMULATION COMPLETE<br>" +
std::string("<sub>Final Population: ") +
std::to_string(game.countLiveCells()) + " cells</sub>"},
{"font", {{"size", 16}, {"color", "red"}}}}}});
std::cout << "Game of Life simulation completed. Close browser to exit."
<< '\n';
return 0;
}
Definition gallery_game_of_life.cpp:51
auto getGrid() const -> const std::vector< std::vector< int > > &
Definition gallery_game_of_life.cpp:150
auto countNeighbors(int x, int y) -> int
Definition gallery_game_of_life.cpp:101
GameOfLife(int w, int h)
Definition gallery_game_of_life.cpp:58
void randomize(double probability=0.3)
Definition gallery_game_of_life.cpp:64
auto countLiveCells() const -> int
Definition gallery_game_of_life.cpp:154
void step()
Definition gallery_game_of_life.cpp:121
void addGlider(int startX, int startY)
Definition gallery_game_of_life.cpp:76
void addOscillator(int startX, int startY)
Definition gallery_game_of_life.cpp:90
auto main() -> int
Definition gallery_animate_sin_wave.cpp:48
nlohmann::json Object
Definition plotly.hpp:26
Public Plotly C++ API header.