Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca_sandpile_example.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
31#include <array>
32#include <cstddef>
33#include <cstdint>
34#include <iostream>
35#include <stdexcept>
36#include <string>
37
38#include <ca-traits.H>
39#include <tpl_ca_async_engine.H>
40#include <tpl_ca_lattice.H>
41#include <tpl_ca_neighborhood.H>
42#include <tpl_ca_storage.H>
44
45using namespace Aleph;
46using namespace Aleph::CA;
47
48namespace
49{
50
52
56struct Sandpile_Rule
57{
58 template <typename State>
59 State operator()(const State &s, Neighbor_View<State> nh) const noexcept
60 {
61 constexpr State threshold = 4;
62 State result = s;
63 if (result >= threshold)
64 result -= threshold;
65 for (const auto &n : nh)
66 if (n >= threshold)
67 result += 1;
68 return result;
69 }
70};
71
73void render(const Grid &g, std::ostream &os)
74{
75 static const std::array<const char *, 5> glyphs = {" .", " :", " +", " #", "##"};
76 for (ca_size_t i = 0; i < g.size(0); ++i)
77 {
78 for (ca_size_t j = 0; j < g.size(1); ++j)
79 {
80 const auto v = g.at({static_cast<ca_index_t>(i),
81 static_cast<ca_index_t>(j)});
82 const std::size_t idx = v < 0 ? 0 : (v > 4 ? 4 : static_cast<std::size_t>(v));
83 os << glyphs[idx];
84 }
85 os << '\n';
86 }
87}
88
90bool stabilised(const Grid &g)
91{
92 for (ca_size_t i = 0; i < g.size(0); ++i)
93 for (ca_size_t j = 0; j < g.size(1); ++j)
94 if (g.at({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)}) >= 4)
95 return false;
96 return true;
97}
98
100std::size_t cells_changed(const Grid &before, const Grid &after)
101{
102 std::size_t n = 0;
103 for (ca_size_t i = 0; i < before.size(0); ++i)
104 for (ca_size_t j = 0; j < before.size(1); ++j)
105 {
106 const Coord_Vec<2> c{static_cast<ca_index_t>(i),
107 static_cast<ca_index_t>(j)};
108 if (before.at(c) != after.at(c))
109 ++n;
110 }
111 return n;
112}
113
114} // namespace
115
116int main(int argc, char **argv)
117{
118 std::size_t drops = 80;
119 ca_size_t side = 16;
120 try
121 {
122 if (argc >= 2)
123 drops = static_cast<std::size_t>(std::stoul(argv[1]));
124 if (argc >= 3)
125 side = static_cast<ca_size_t>(std::stoul(argv[2]));
126 }
127 catch (const std::invalid_argument &)
128 {
129 std::cerr << "Invalid argument: expected non-negative integers for [drops] [side]\n";
130 return 1;
131 }
132 catch (const std::out_of_range &)
133 {
134 std::cerr << "Argument out of range for [drops] [side]\n";
135 return 1;
136 }
137 if (side == 0)
138 {
139 std::cerr << "Invalid side: must be a positive integer\n";
140 return 1;
141 }
142
143 Grid grid({side, side}, 0);
146
147 std::cout << "BTW Sandpile — " << side << "×" << side << ", " << drops
148 << " drops at the centre\n\n";
149
150 std::size_t total_avalanche_cells = 0;
151 for (std::size_t k = 0; k < drops; ++k)
152 {
153 // Drop a grain at the centre.
154 const auto cr = static_cast<ca_index_t>(side / 2);
155 const auto cc = static_cast<ca_index_t>(side / 2);
156 grid.set({cr, cc}, grid.at({cr, cc}) + 1);
157
158 // Relax until stable using the Sequential_Update strategy
159 // (single-buffer, in-place sweep).
160 Engine engine(std::move(grid), Sandpile_Rule{}, Von_Neumann<2, 1>{});
161 Grid pre = engine.frame();
162 std::size_t sweeps = 0;
163 while (not stabilised(engine.frame()) and sweeps < 4 * side * side)
164 {
165 engine.step();
166 ++sweeps;
167 }
168 const std::size_t aval = cells_changed(pre, engine.frame());
170 grid = engine.frame();
171
172 if (k % (drops < 20 ? 1 : 10) == 0 or k + 1 == drops)
173 std::cout << "drop " << k + 1 << "/" << drops
174 << " — avalanche size " << aval
175 << " (cumulative " << total_avalanche_cells
176 << ", " << sweeps << " sweeps)\n";
177 }
178
179 std::cout << "\nFinal frame:\n\n";
180 render(grid, std::cout);
181 std::cout << "\nAverage avalanche size: "
182 << static_cast<double>(total_avalanche_cells) / static_cast<double>(drops)
183 << "\n";
184 return 0;
185}
int main()
Common typedefs and tag types for the Cellular Automata module.
Update-scheme aware engine.
Lattice that adds boundary-aware access on top of a storage.
Von Neumann (L1) neighborhood of radius R in N dimensions.
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
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
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
and
Check uniqueness with explicit hash + equality functors.
Out-of-range neighbours behave as if the lattice ended.
Definition ca-traits.H:119
In-place sequential update (no double buffer).
static int * k
static mt19937 engine
Phase 13 update-scheme aware engine.
Cellular automata lattice with pluggable boundary policies.
Neighborhoods catalogue for Aleph::CA.
Dense, contiguous storage for cellular automata cells (1D/2D/3D).
Phase 13 update-scheme strategies for Aleph::CA.