Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca_engine_sync_test.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
36# include <array>
37# include <initializer_list>
38# include <set>
39# include <span>
40# include <utility>
41# include <vector>
42
43# include <gtest/gtest.h>
44
45# include <ca-traits.H>
46# include <tpl_ca_storage.H>
47# include <tpl_ca_lattice.H>
48# include <tpl_ca_neighborhood.H>
49# include <tpl_ca_rule.H>
50# include <tpl_ca_engine.H>
51# include <ca-engine-utils.H>
52
53using namespace Aleph;
54using namespace Aleph::CA;
55
56namespace
57{
60 using Cell = std::pair<ca_index_t, ca_index_t>;
61
62 std::set<Cell> to_set(std::initializer_list<Cell> cells)
63 {
64 return std::set<Cell>(cells.begin(), cells.end());
65 }
66
68 std::initializer_list<Cell> alive)
69 {
70 Grid g({ rows, cols }, 0);
71 for (const auto & c : alive)
72 g.set({ c.first, c.second }, 1);
73 return g;
74 }
75
77 const std::vector<Cell> & alive)
78 {
79 Grid g({ rows, cols }, 0);
80 for (const auto & c : alive)
81 g.set({ c.first, c.second }, 1);
82 return g;
83 }
84
85 std::set<Cell> alive_cells(const Grid & g)
86 {
87 std::set<Cell> out;
88 const auto rows = static_cast<ca_index_t>(g.size(0));
89 const auto cols = static_cast<ca_index_t>(g.size(1));
90 for (ca_index_t i = 0; i < rows; ++i)
91 for (ca_index_t j = 0; j < cols; ++j)
92 if (g.at({ i, j }) != 0)
93 out.insert({ i, j });
94 return out;
95 }
96
97 Grid manual_life_step(const Grid & cur)
98 {
99 Grid nxt(cur.extents(), 0);
100 auto rule = make_game_of_life_rule();
102 std::array<int, Moore<2, 1>::size_v> buf { };
103
104 const auto rows = static_cast<ca_index_t>(cur.size(0));
105 const auto cols = static_cast<ca_index_t>(cur.size(1));
106 for (ca_index_t i = 0; i < rows; ++i)
107 for (ca_index_t j = 0; j < cols; ++j)
108 {
110 std::span<int>(buf.data(), buf.size()));
111 nxt.set({ i, j },
112 rule(cur.at({ i, j }),
113 Neighbor_View<int>(buf.data(), buf.size())));
114 }
115 return nxt;
116 }
117
119 {
120 return Engine(std::move(initial), make_game_of_life_rule(), Moore<2, 1>{});
121 }
122
124 const char * name)
125 {
126 Grid ref = initial;
127 auto engine = make_engine(std::move(initial));
128 for (std::size_t t = 0; t < 20; ++t)
129 {
131 << name << " before step " << t;
132 engine.step();
133 ref = manual_life_step(ref);
134 }
136 << name << " after 20 steps";
137 EXPECT_EQ(engine.steps_run(), 20u);
138 }
139
140 Grid make_pulsar()
141 {
142 Grid g({ 17, 17 }, 0);
143 constexpr int offset = 2;
144 const std::array<int, 4> sparse_rows = {{ 0, 5, 7, 12 }};
145 const std::array<int, 6> dense_cols = {{ 2, 3, 4, 8, 9, 10 }};
146 for (const int r : sparse_rows)
147 for (const int c : dense_cols)
148 g.set({ offset + r, offset + c }, 1);
149
150 const std::array<int, 6> dense_rows = {{ 2, 3, 4, 8, 9, 10 }};
151 const std::array<int, 4> sparse_cols = {{ 0, 5, 7, 12 }};
152 for (const int r : dense_rows)
154 g.set({ offset + r, offset + c }, 1);
155 return g;
156 }
157
158 std::vector<Cell> glider_cells()
159 {
160 return { { 1, 2 }, { 2, 3 }, { 3, 1 }, { 3, 2 }, { 3, 3 } };
161 }
162
163 std::set<Cell> shifted(const std::vector<Cell> & cells,
164 ca_index_t dr, ca_index_t dc)
165 {
166 std::set<Cell> out;
167 for (const auto & c : cells)
168 out.insert({ c.first + dr, c.second + dc });
169 return out;
170 }
171}
172
174{
176 make_grid(5, 5, { { 2, 1 }, { 2, 2 }, { 2, 3 } }),
177 "blinker");
179 make_grid(6, 6, { { 2, 2 }, { 2, 3 }, { 2, 4 },
180 { 3, 1 }, { 3, 2 }, { 3, 3 } }),
181 "toad");
183 make_grid(6, 6, { { 1, 1 }, { 1, 2 }, { 2, 1 }, { 2, 2 },
184 { 3, 3 }, { 3, 4 }, { 4, 3 }, { 4, 4 } }),
185 "beacon");
187 make_grid(10, 10, glider_cells()),
188 "glider");
190}
191
193{
194 auto blinker = make_engine(
195 make_grid(5, 5, { { 2, 1 }, { 2, 2 }, { 2, 3 } }));
196 auto toad = make_engine(
197 make_grid(6, 6, { { 2, 2 }, { 2, 3 }, { 2, 4 },
198 { 3, 1 }, { 3, 2 }, { 3, 3 } }));
199 auto beacon = make_engine(
200 make_grid(6, 6, { { 1, 1 }, { 1, 2 }, { 2, 1 }, { 2, 2 },
201 { 3, 3 }, { 3, 4 }, { 4, 3 }, { 4, 4 } }));
202
203 const auto blinker0 = to_set({ { 2, 1 }, { 2, 2 }, { 2, 3 } });
204 const auto blinker1 = to_set({ { 1, 2 }, { 2, 2 }, { 3, 2 } });
205 const auto toad0 = to_set({ { 2, 2 }, { 2, 3 }, { 2, 4 },
206 { 3, 1 }, { 3, 2 }, { 3, 3 } });
207 const auto toad1 = to_set({ { 1, 3 }, { 2, 1 }, { 2, 4 },
208 { 3, 1 }, { 3, 4 }, { 4, 2 } });
209 const auto beacon0 = to_set({ { 1, 1 }, { 1, 2 }, { 2, 1 }, { 2, 2 },
210 { 3, 3 }, { 3, 4 }, { 4, 3 }, { 4, 4 } });
211 const auto beacon1 = to_set({ { 1, 1 }, { 1, 2 }, { 2, 1 },
212 { 3, 4 }, { 4, 3 }, { 4, 4 } });
213
214 for (std::size_t t = 0; t <= 20; ++t)
215 {
216 EXPECT_EQ(alive_cells(blinker.frame()), t % 2 == 0 ? blinker0 : blinker1)
217 << "blinker t=" << t;
218 EXPECT_EQ(alive_cells(toad.frame()), t % 2 == 0 ? toad0 : toad1)
219 << "toad t=" << t;
220 EXPECT_EQ(alive_cells(beacon.frame()), t % 2 == 0 ? beacon0 : beacon1)
221 << "beacon t=" << t;
222 if (t < 20)
223 {
224 blinker.step();
225 toad.step();
226 beacon.step();
227 }
228 }
229}
230
232{
233 const auto cells = glider_cells();
234 auto engine = make_engine(make_grid(10, 10, cells));
235
236 for (std::size_t t = 0; t <= 20; ++t)
237 {
238 if (t % 4 == 0)
239 {
240 const auto d = static_cast<ca_index_t>(t / 4);
241 EXPECT_EQ(alive_cells(engine.frame()), shifted(cells, d, d))
242 << "glider t=" << t;
243 }
244 if (t < 20)
245 engine.step();
246 }
247}
248
250{
251 const auto initial = alive_cells(make_pulsar());
253
254 for (std::size_t t = 1; t <= 20; ++t)
255 {
256 engine.step();
257 if (t % 3 == 0)
258 EXPECT_EQ(alive_cells(engine.frame()), initial) << "pulsar t=" << t;
259 }
260 EXPECT_EQ(engine.steps_run(), 20u);
261}
262
264{
265 auto engine = make_engine(
266 make_grid(5, 5, { { 2, 1 }, { 2, 2 }, { 2, 3 } }));
267 std::vector<std::size_t> pre_steps;
268 std::vector<std::size_t> post_steps;
269 std::vector<std::size_t> pre_alive;
270 std::vector<std::size_t> post_alive;
271
272 engine.on_pre_step([&](std::size_t step, const Grid & frame)
273 {
274 pre_steps.push_back(step);
275 pre_alive.push_back(alive_cells(frame).size());
276 });
277 engine.on_post_step([&](std::size_t step, const Grid & frame)
278 {
279 post_steps.push_back(step);
280 post_alive.push_back(alive_cells(frame).size());
281 });
282
283 engine.run(2);
284
285 EXPECT_EQ(pre_steps, (std::vector<std::size_t>{ 0, 1 }));
286 EXPECT_EQ(post_steps, (std::vector<std::size_t>{ 1, 2 }));
287 EXPECT_EQ(pre_alive, (std::vector<std::size_t>{ 3, 3 }));
288 EXPECT_EQ(post_alive, (std::vector<std::size_t>{ 3, 3 }));
289 EXPECT_EQ(engine.steps_run(), 2u);
290}
291
293{
294 using Tiled_Engine =
296 using Column_Engine =
298
299 Grid initial = make_pulsar();
302 Column_Engine column(std::move(initial), make_game_of_life_rule(),
303 Moore<2, 1>{});
304
305 for (std::size_t t = 0; t <= 20; ++t)
306 {
307 const auto expected = alive_cells(row_major.frame());
308 EXPECT_EQ(alive_cells(tiled.frame()), expected) << "tiled t=" << t;
309 EXPECT_EQ(alive_cells(column.frame()), expected) << "column t=" << t;
310 if (t < 20)
311 {
312 row_major.step();
313 tiled.step();
314 column.step();
315 }
316 }
317}
318
320{
322 row.set({ 3 }, 1);
323 auto engine = make_wolfram_engine(90, std::move(row));
324
325 engine.step();
326
327 EXPECT_EQ(engine.frame().at({ 0 }), 0);
328 EXPECT_EQ(engine.frame().at({ 1 }), 0);
329 EXPECT_EQ(engine.frame().at({ 2 }), 1);
330 EXPECT_EQ(engine.frame().at({ 3 }), 0);
331 EXPECT_EQ(engine.frame().at({ 4 }), 1);
332 EXPECT_EQ(engine.frame().at({ 5 }), 0);
333 EXPECT_EQ(engine.frame().at({ 6 }), 0);
334 EXPECT_EQ(engine.steps_run(), 1u);
335}
size_t size_t int32_t * out
Definition ca-c-api.h:120
size_t row
Definition ca-c-api.h:115
size_t * rows
Definition ca-c-api.h:112
size_t cols
Definition ca-c-api.h:105
Convenience builders for the Phase 3 synchronous engine.
Common typedefs and tag types for the Cellular Automata module.
Lattice that adds boundary-aware access on top of a storage.
Moore (Chebyshev) neighborhood of radius R in N dimensions.
Synchronous double-buffered engine.
Minimal std::expected-style result type for C++20.
#define TEST(name)
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
const long double offset[]
Offset values indexed by symbol string length (bounded by MAX_OFFSET_INDEX)
Wolfram_1D_Engine make_wolfram_engine(std::uint8_t rule_no, ca_size_t width)
Build a 1D elementary Wolfram engine of the given width.
constexpr Game_Of_Life_Rule make_game_of_life_rule() noexcept
Build the canonical Game of Life rule.
std::span< const T > Neighbor_View
Read-only view over a contiguous range of neighbour values.
Definition ca-traits.H:90
std::ptrdiff_t ca_index_t
Signed coordinate component used by lattices and neighborhoods.
Definition ca-traits.H:60
std::array< ca_index_t, N > Coord_Vec
Default coordinate vector.
Definition ca-traits.H:69
void gather_neighbors(const Nbh &nh, const L &lat, const typename L::coord_type &center, std::span< T > out)
Populate out[0..nh.size()) with neighbour values of center.
std::size_t ca_size_t
Unsigned size component used for extents and counts.
Definition ca-traits.H:63
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
size_t size(Node *root) noexcept
std::set< typename C::Item_Type > to_set(const C &c)
Convert a container to a std::set.
Definition ah-convert.H:719
Iterate the lattice in column-major (Fortran) order.
Definition ca-traits.H:183
Out-of-range neighbours behave as if the lattice ended.
Definition ca-traits.H:119
Iterate a 2D lattice in W x H tiles.
Definition ca-traits.H:200
static mt19937 engine
gsl_rng * r
Synchronous double-buffered engine for cellular automata.
Cellular automata lattice with pluggable boundary policies.
Neighborhoods catalogue for Aleph::CA.
Rule mechanisms for Aleph::CA.
Dense, contiguous storage for cellular automata cells (1D/2D/3D).