-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgallery_game_of_life.cpp
More file actions
286 lines (249 loc) 路 8.04 KB
/
Copy pathgallery_game_of_life.cpp
File metadata and controls
286 lines (249 loc) 路 8.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/**
* @file gallery_game_of_life.cpp
* @brief Conway's Game of Life - Cellular Automaton Simulation
* @author plotly.cpp contributors
* @date 2025
*
* @example gallery_game_of_life.cpp
*
* # Conway's Game of Life Simulation
*
* 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.
*
* ## What You'll Learn
* - Implementing cellular automaton algorithms and Conway's rules
* - Real-time animation using restyle() and relayout() for live updates
* - Object-oriented design with the GameOfLife class
* - Pattern recognition in cellular automata (gliders, oscillators, still
* lifes)
* - Threading and timing control for smooth animation
* - Heatmap visualization of grid-based simulations
* - Population dynamics tracking and extinction detection
*
* ## Sample Output
* The example creates a dynamic cellular automaton featuring:
* - 50x50 grid with 200 generations of evolution
* - Initial patterns: gliders, oscillators, and random sparse cells
* - Real-time animation at 100ms intervals per generation
* - Population counter showing live cell count in title
* - Black/white color scheme (live/dead cells)
* - Automatic termination when population dies out
*
* @image html game_of_life.gif "Conway's Game of Life Animation"
*
* @see plotly::Figure::restyle() For real-time data updates
* @see std::this_thread::sleep_for() For animation timing control
*/
#include "plotly/plotly.hpp"
#include <chrono>
#include <iostream>
#include <random>
#include <string>
#include <thread>
#include <utility>
#include <vector>
class GameOfLife {
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));
}
void randomize(double probability = 0.3) {
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;
}
}
}
void addGlider(int startX, int startY) {
// Classic glider pattern
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;
}
}
}
void addOscillator(int startX, int startY) {
// Blinker oscillator (3 cells in a row)
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;
}
}
}
auto countNeighbors(int x, int y) -> int {
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;
// Wrap around edges (toroidal topology)
nx = (nx + _width) % _width;
ny = (ny + _height) % _height;
count += _grid[ny][nx];
}
}
return count;
}
void step() {
// Apply Conway's rules
for (int y = 0; y < _height; y++) {
for (int x = 0; x < _width; x++) {
int neighbors = countNeighbors(x, y);
int cell = _grid[y][x];
if (cell == 1) {
// Live cell
if (neighbors < 2 || neighbors > 3) {
_nextGrid[y][x] = 0; // Dies
} else {
_nextGrid[y][x] = 1; // Survives
}
} else {
// Dead cell
if (neighbors == 3) {
_nextGrid[y][x] = 1; // Born
} else {
_nextGrid[y][x] = 0; // Stays dead
}
}
}
}
// Swap grids
_grid.swap(_nextGrid);
}
[[nodiscard]] auto getGrid() const -> const std::vector<std::vector<int>> & {
return _grid;
}
[[nodiscard]] auto countLiveCells() const -> int {
int count = 0;
for (const auto &row : _grid) {
for (int cell : row) {
count += cell;
}
}
return count;
}
};
auto main() -> int {
std::cout << "Starting Conway's Game of Life..." << '\n';
plotly::Figure fig;
fig.openBrowser();
// Game parameters
const int width = 50;
const int height = 50;
const int generations = 200;
const int stepDelay = 100; // milliseconds
GameOfLife game(width, height);
// Initialize with interesting patterns
game.randomize(0.15); // Sparse random cells
game.addGlider(5, 5); // Moving pattern
game.addGlider(15, 25); // Another glider
game.addOscillator(30, 10); // Blinking pattern
game.addOscillator(35, 35); // Another oscillator
// Create coordinate arrays
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);
}
// Create heatmap trace
plotly::Object trace = {
{"type", "heatmap"},
{"x", xCoords},
{"y", yCoords},
{"z", game.getGrid()},
{"colorscale",
{
{0.0, "white"}, // Dead cells
{1.0, "black"} // Live cells
}},
{"showscale", false},
{"hovertemplate", "Cell (%{x}, %{y})<br>State: %{z}<extra></extra>"}};
// Create layout
plotly::Object layout = {
{"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"} // Flip Y axis for better view
}},
{"width", 800},
{"height", 800},
{"margin", {{"l", 50}, {"r", 50}, {"t", 80}, {"b", 50}}}};
// Create initial plot
std::vector<plotly::Object> data = {trace};
fig.newPlot(data, layout);
std::cout << "Starting simulation with " << game.countLiveCells()
<< " initial live cells..." << '\n';
std::cout
<< "Patterns: Gliders (moving), Oscillators (blinking), Random cells"
<< '\n';
// Simulation loop
for (int generation = 1; generation <= generations && fig.isOpen();
generation++) {
game.step();
int liveCells = game.countLiveCells();
// Update the plot
fig.restyle({{"z", {game.getGrid()}}}, {0});
// Update title with generation info
plotly::Object newLayout = {
{"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}}}}}};
fig.relayout(newLayout);
std::this_thread::sleep_for(std::chrono::milliseconds(stepDelay));
if (generation % 25 == 0) {
std::cout << "Generation " << generation << ": " << liveCells
<< " live cells" << '\n';
}
// Stop if population dies out
if (liveCells == 0) {
std::cout << "Population died out at generation " << generation << '\n';
break;
}
}
// Final message
fig.relayout(
{{"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';
fig.waitClose();
return 0;
}