Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca_hashlife_example.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
56#include <chrono>
57#include <cstdint>
58#include <iomanip>
59#include <iostream>
60#include <string>
61#include <vector>
62
63#include <tpl_ca_hashlife.H>
64
65using namespace Aleph::CA;
66
67namespace
68{
69 // ------------------------------------------------------------------
70 // Pretty-printing helpers
71 // ------------------------------------------------------------------
72
73 void print_section(const std::string &title)
74 {
75 std::cout << '\n';
76 std::cout << "═══════════════════════════════════════════════════════════════\n";
77 std::cout << " " << title << '\n';
78 std::cout << "═══════════════════════════════════════════════════════════════\n";
79 }
80
84 std::int64_t x_min, std::int64_t y_min,
85 std::int64_t x_max, std::int64_t y_max,
86 const std::string &caption)
87 {
88 const std::int64_t w = x_max - x_min;
89 const std::int64_t h = y_max - y_min;
90 std::vector<std::string> rows(static_cast<std::size_t>(h),
91 std::string(static_cast<std::size_t>(w), '.'));
92 engine.for_each_alive([&](std::int64_t x, std::int64_t y)
93 {
94 if (x >= x_min and x < x_max and y >= y_min and y < y_max)
95 rows[static_cast<std::size_t>(y - y_min)]
96 [static_cast<std::size_t>(x - x_min)] = 'O';
97 });
98 std::cout << " ┌";
99 for (std::int64_t i = 0; i < w; ++i) std::cout << "─";
100 std::cout << "┐ " << caption << '\n';
101 for (const auto &r : rows)
102 std::cout << " │" << r << "│\n";
103 std::cout << " └";
104 for (std::int64_t i = 0; i < w; ++i) std::cout << "─";
105 std::cout << "┘\n";
106 }
107
108 // ------------------------------------------------------------------
109 // Canonical patterns expressed in local (top-left = 0,0) coordinates.
110 // ------------------------------------------------------------------
111
112 void seed_glider(Hashlife_Engine &e, std::int64_t ox = 0, std::int64_t oy = 0)
113 {
114 e.set_alive(ox + 1, oy + 0);
115 e.set_alive(ox + 2, oy + 1);
116 e.set_alive(ox + 0, oy + 2);
117 e.set_alive(ox + 1, oy + 2);
118 e.set_alive(ox + 2, oy + 2);
119 }
120
121 void seed_r_pentomino(Hashlife_Engine &e, std::int64_t ox = 0, std::int64_t oy = 0)
122 {
123 e.set_alive(ox + 1, oy + 0);
124 e.set_alive(ox + 2, oy + 0);
125 e.set_alive(ox + 0, oy + 1);
126 e.set_alive(ox + 1, oy + 1);
127 e.set_alive(ox + 1, oy + 2);
128 }
129
133 std::int64_t ox = 0, std::int64_t oy = 0)
134 {
135 static constexpr int xy[][2] = {
136 { 1, 5 }, { 1, 6 }, { 2, 5 }, { 2, 6 },
137 { 11, 5 }, { 11, 6 }, { 11, 7 }, { 12, 4 }, { 12, 8 },
138 { 13, 3 }, { 13, 9 }, { 14, 3 }, { 14, 9 },
139 { 15, 6 }, { 16, 4 }, { 16, 8 },
140 { 17, 5 }, { 17, 6 }, { 17, 7 }, { 18, 6 },
141 { 21, 3 }, { 21, 4 }, { 21, 5 },
142 { 22, 3 }, { 22, 4 }, { 22, 5 },
143 { 23, 2 }, { 23, 6 },
144 { 25, 1 }, { 25, 2 }, { 25, 6 }, { 25, 7 },
145 { 35, 3 }, { 35, 4 }, { 36, 3 }, { 36, 4 },
146 };
147 for (const auto &p : xy)
148 e.set_alive(ox + p[0], oy + p[1]);
149 }
150
151 // ------------------------------------------------------------------
152 // Demo 1 — watch a glider travel diagonally.
153 // ------------------------------------------------------------------
154
155 void demo_glider()
156 {
157 print_section("Demo 1 — Glider: 5 cells, period 4, moves (+1,+1) per period");
158
160 seed_glider(e);
161
162 // Show 5 frames spaced by 4 generations: glider returns to the same
163 // shape but shifted one cell south-east each time.
164 for (int frame = 0; frame < 5; ++frame)
165 {
166 const auto bb = e.bbox();
167 std::cout << " step " << std::setw(4) << e.generation()
168 << " pop=" << e.population()
169 << " bbox=(" << bb.x_min << ',' << bb.y_min
170 << ")…(" << bb.x_max << ',' << bb.y_max << ")\n";
171 render_window(e, -1, -1, 8, 8, "");
172 e.run(4);
173 }
174 }
175
176 // ------------------------------------------------------------------
177 // Demo 2 — R-pentomino: chaotic stabilisation around step 1103.
178 // ------------------------------------------------------------------
179
180 void demo_r_pentomino()
181 {
182 print_section("Demo 2 — R-pentomino: 5 chaotic cells stabilising at step 1103");
183
186 std::cout << " Initial seed (5 cells):\n";
187 render_window(e, -1, -1, 8, 8, "step 0");
188
189 // Hashlife always advances by powers of two, so the actual generation
190 // count usually overshoots the requested milestone. We poll instead
191 // of subtracting, which keeps the walker moving forward monotonically.
192 constexpr std::uint64_t milestones[] = {
193 10u, 100u, 500u, 1000u, 1103u, 2000u, 5000u
194 };
195 for (const std::uint64_t target : milestones)
196 {
197 const auto current = static_cast<std::uint64_t>(e.generation());
198 if (target > current)
199 e.run(target - current);
200 const auto bb = e.bbox();
201 std::cout << " step " << std::setw(5) << e.generation()
202 << " pop=" << std::setw(4) << e.population()
203 << " bbox=" << std::setw(4) << bb.width()
204 << " x " << std::setw(4) << bb.height() << '\n';
205 }
206 std::cout << " → the methuselah settles into still-lifes, blinkers and"
207 " 6 escaping gliders.\n";
208 }
209
210 // ------------------------------------------------------------------
211 // Demo 3 — Gosper gun at exponential generation counts.
212 // ------------------------------------------------------------------
213
214 void demo_gosper_gun()
215 {
216 print_section("Demo 3 — Gosper gun: exponential advances up to step 2²⁰");
217
219 seed_gosper_gun(e, /*ox=*/0, /*oy=*/0);
220 std::cout << " Seed (36 cells, classic Gosper gun configuration):\n";
221 render_window(e, -1, -1, 41, 13, "step 0");
222
223 using clock = std::chrono::steady_clock;
224 std::cout << "\n Calling advance(k) for k = 4, 8, 12, 16, 20:\n";
225 std::cout << " k | 2^k generations | pop | cache nodes | wall time\n";
226 std::cout << " ----+-----------------+-----------+----------------+--------------\n";
227 for (unsigned k : { 4u, 8u, 12u, 16u, 20u })
228 {
229 const auto t0 = clock::now();
230 const std::uint64_t advanced = e.advance(k);
231 const auto dt = std::chrono::duration<double, std::milli>(
232 clock::now() - t0).count();
233 const auto stats = e.stats();
234 std::cout << " " << std::setw(2) << k
235 << " | " << std::setw(15) << advanced
236 << " | " << std::setw(9) << e.population()
237 << " | " << std::setw(14) << stats.canonical_nodes
238 << " | " << std::fixed << std::setprecision(2)
239 << std::setw(8) << dt << " ms\n";
240 }
241
242 const auto bb = e.bbox();
243 std::cout << "\n Final state: " << e.population() << " alive cells across a "
244 << bb.width() << " × " << bb.height() << " bounding box.\n";
245 std::cout << " Generation: " << e.generation() << '\n';
246
247 const auto stats = e.stats();
248 std::cout << " Cache: " << stats.canonical_nodes << " canonical nodes, "
249 << stats.result_hits << " result hits, "
250 << stats.result_misses << " result misses, "
251 << stats.result_cache_clears << " evictions.\n";
252 std::cout << " Hit ratio: " << std::fixed << std::setprecision(2)
253 << (100.0 * stats.result_hits
254 / std::max<std::size_t>(1, stats.result_hits + stats.result_misses))
255 << " % (high hit ratio = lots of repeated subtree work avoided)\n";
256 }
257
258 // ------------------------------------------------------------------
259 // Demo 4 — RLE round-trip through HighLife.
260 // ------------------------------------------------------------------
261
263 {
264 print_section("Demo 4 — RLE I/O and a pinch of HighLife");
265
267 seed_glider(src);
268
269 const std::string serialised = src.save_rle_string("glider, evolved by HighLife");
270 std::cout << " RLE serialisation of the seed:\n";
271 for (const char c : serialised)
272 std::cout << ((c == '\n') ? std::string("\n ") : std::string(1, c));
273 std::cout << '\n';
274
277 std::cout << "\n After parsing, dst.rule = " << format_rule(dst.rule())
278 << " population = " << dst.population() << '\n';
279 }
280
281} // namespace
282
283
284int main()
285{
286 std::cout << "Aleph::CA::Hashlife_Engine — guided tour\n";
287 std::cout << "Outer-totalistic binary cellular automata at exponential scale.\n";
288
289 demo_glider();
293
294 std::cout << "\nDone.\n";
295 return 0;
296}
void print_section(const string &title)
long double h
Definition btreepic.C:154
long double w
Definition btreepic.C:153
size_t * rows
Definition ca-c-api.h:112
int main()
Hashlife engine for outer-totalistic binary cellular automata.
void load_rle_string(const std::string &s)
Convenience overload that reads the pattern from a string.
BBox bbox() const
Tight bounding box of the alive cells (empty if population() == 0).
std::uint64_t run(std::uint64_t generations)
Advance by exactly generations steps.
Stats stats() const noexcept
Diagnostic counters and current root level.
void set_alive(const std::int64_t x, const std::int64_t y, const bool alive=true)
Set or clear the cell at world coordinates (x, y).
std::uint64_t advance(const unsigned k)
Advance the universe by 2^k generations.
std::int64_t generation() const noexcept
Number of generations elapsed since construction (or the last clear).
std::uint64_t population() const noexcept
Number of alive cells.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
static mpfr_t y
Definition mpfr_mul_d.c:3
std::string format_rule(const Outer_Totalistic_Binary_Rule &r)
Format a rule as a Conway-style Bxxx/Sxxx string.
constexpr Outer_Totalistic_Binary_Rule HighLife
Nathan Thompson's HighLife: B36/S23 (replicators).
and
Check uniqueness with explicit hash + equality functors.
STL namespace.
long double y_max
Definition ntreepic.C:1147
long double x_max
Definition ntreepic.C:1146
std::size_t canonical_nodes
number of distinct canonical nodes
static int * k
static mt19937 engine
gsl_rng * r
Hashlife engine for outer-totalistic binary cellular automata.