1use crate::control::{Automaton, NoAction, Range, Time};
7
8#[derive(Debug, Clone, Copy, PartialEq)]
10pub enum CellularType {
11 OneDimensional,
13 TwoDimensional,
15 Cyclic,
17}
18
19#[derive(Debug, Clone)]
21pub struct CellularState {
22 pub generation: Vec<u8>,
24 pub next_generation: Vec<u8>,
26 pub width: usize,
28 pub height: usize,
30 pub current_value: f64,
32 pub step: usize,
34}
35
36#[derive(Debug, Clone)]
38pub struct CellularAutomaton {
39 name: String,
41 cell_type: CellularType,
43 rule: u8,
45 size: usize,
47 output_mode: OutputMode,
49 range: Range,
51 rng_state: u64,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq)]
57pub enum OutputMode {
58 Center,
60 Sum,
62 Density,
64 Index(usize),
66}
67
68impl CellularAutomaton {
69 pub fn one_dimensional(name: &str, rule: u8, size: usize) -> Self {
71 Self {
72 name: name.to_string(),
73 cell_type: CellularType::OneDimensional,
74 rule,
75 size,
76 output_mode: OutputMode::Center,
77 range: Range::unipolar(),
78 rng_state: 123456789,
79 }
80 }
81
82 pub fn game_of_life(name: &str, width: usize, height: usize) -> Self {
84 Self {
85 name: name.to_string(),
86 cell_type: CellularType::TwoDimensional,
87 rule: 0,
88 size: width * height,
89 output_mode: OutputMode::Density,
90 range: Range::unipolar(),
91 rng_state: 123456789,
92 }
93 }
94
95 pub fn with_output_mode(mut self, mode: OutputMode) -> Self {
97 self.output_mode = mode;
98 self
99 }
100
101 pub fn with_range(mut self, range: Range) -> Self {
103 self.range = range;
104 self
105 }
106
107 fn random_initial(&self, _width: usize, _height: usize, rng: &mut u64) -> Vec<u8> {
109 let mut gen = Vec::with_capacity(self.size);
110 for _ in 0..self.size {
111 gen.push(if self.random_bit(rng) { 1 } else { 0 });
112 }
113 gen
114 }
115
116 fn random_bit(&self, rng: &mut u64) -> bool {
118 let mut x = *rng;
119 x ^= x << 13;
120 x ^= x >> 7;
121 x ^= x << 17;
122 *rng = x;
123 (x & 1) == 1
124 }
125
126 fn apply_rule_1d(&self, generation: &[u8]) -> Vec<u8> {
128 let mut next = vec![0; generation.len()];
129
130 for i in 0..generation.len() {
131 let left = if i > 0 {
132 generation[i - 1]
133 } else {
134 generation[generation.len() - 1]
135 };
136 let center = generation[i];
137 let right = if i < generation.len() - 1 {
138 generation[i + 1]
139 } else {
140 generation[0]
141 };
142
143 let pattern = (left << 2) | (center << 1) | right;
144 let bit = (self.rule >> pattern) & 1;
145 next[i] = bit;
146 }
147
148 next
149 }
150
151 fn apply_rule_gol(&self, generation: &[u8], width: usize, height: usize) -> Vec<u8> {
153 let mut next = vec![0; generation.len()];
154
155 for y in 0..height {
156 for x in 0..width {
157 let idx = y * width + x;
158 let cell = generation[idx];
159
160 let mut neighbors = 0;
162 for dy in -1..=1 {
163 for dx in -1..=1 {
164 if dx == 0 && dy == 0 {
165 continue;
166 }
167
168 let nx = (x as i32 + dx + width as i32) % width as i32;
169 let ny = (y as i32 + dy + height as i32) % height as i32;
170 let nidx = (ny * width as i32 + nx) as usize;
171
172 if generation[nidx] == 1 {
173 neighbors += 1;
174 }
175 }
176 }
177
178 next[idx] = match (cell, neighbors) {
180 (1, 2) | (1, 3) => 1, (0, 3) => 1, _ => 0, };
184 }
185 }
186
187 next
188 }
189
190 fn compute_output(&self, generation: &[u8], _state: &CellularState) -> f64 {
192 match self.output_mode {
193 OutputMode::Center => {
194 let idx = self.size / 2;
195 generation[idx] as f64
196 }
197
198 OutputMode::Sum => {
199 let sum: usize = generation.iter().map(|&c| c as usize).sum();
200 sum as f64 / self.size as f64
201 }
202
203 OutputMode::Density => {
204 let sum: usize = generation.iter().map(|&c| c as usize).sum();
205 sum as f64 / self.size as f64
206 }
207
208 OutputMode::Index(idx) => {
209 if idx < generation.len() {
210 generation[idx] as f64
211 } else {
212 0.0
213 }
214 }
215 }
216 }
217}
218
219impl Automaton for CellularAutomaton {
220 type State = CellularState;
221 type Action = NoAction;
222
223 fn step(
224 &self,
225 _time: Time,
226 _action: &Self::Action,
227 state: &Self::State,
228 ) -> (Self::State, Option<f64>) {
229 let mut new_state = state.clone();
230
231 new_state.next_generation = match self.cell_type {
232 CellularType::OneDimensional => self.apply_rule_1d(&state.generation),
233 CellularType::TwoDimensional => {
234 self.apply_rule_gol(&state.generation, state.width, state.height)
235 }
236 CellularType::Cyclic => state.generation.clone(),
237 };
238
239 std::mem::swap(&mut new_state.generation, &mut new_state.next_generation);
240 new_state.step += 1;
241
242 let raw = self.compute_output(&new_state.generation, &new_state);
243 let value = self.range.clamp(raw);
244
245 (new_state, Some(value))
246 }
247
248 fn initial_state(&self) -> Self::State {
249 let mut rng = self.rng_state;
250 let generation = self.random_initial(self.size, 1, &mut rng);
251
252 CellularState {
253 generation,
254 next_generation: vec![0; self.size],
255 width: self.size,
256 height: 1,
257 current_value: 0.0,
258 step: 0,
259 }
260 }
261
262 fn name(&self) -> &str {
263 &self.name
264 }
265
266 fn extract_value(&self, state: &Self::State) -> f64 {
267 self.compute_output(&state.generation, state)
268 }
269}
270
271pub mod rules {
273 pub const RULE_30: u8 = 30;
275 pub const RULE_90: u8 = 90;
277 pub const RULE_110: u8 = 110;
279 pub const RULE_184: u8 = 184;
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn test_rule_30() {
289 let ca = CellularAutomaton::one_dimensional("Rule 30", rules::RULE_30, 31);
290 let state = ca.initial_state();
291
292 let (_state, value) = ca.step(0.0, &NoAction, &state);
293 assert!(value.is_some());
294 }
295}