Skip to main content

rill_patchbay/automaton/
cellular.rs

1//! # Клеточные автоматы
2//!
3//! Генерация сигналов на основе клеточных автоматов.
4//! Поддерживаются 1D и 2D клеточные автоматы с различными правилами.
5
6use crate::control::{Automaton, NoAction, Range, Time};
7
8/// Тип клеточного автомата
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub enum CellularType {
11    /// 1D клеточный автомат (правило Вольфрама)
12    OneDimensional,
13    /// 2D клеточный автомат (Game of Life)
14    TwoDimensional,
15    /// Циклический клеточный автомат
16    Cyclic,
17}
18
19/// Состояние клеточного автомата
20#[derive(Debug, Clone)]
21pub struct CellularState {
22    /// Текущее поколение
23    pub generation: Vec<u8>,
24    /// Следующее поколение
25    pub next_generation: Vec<u8>,
26    /// Ширина (для 2D)
27    pub width: usize,
28    /// Высота (для 2D)
29    pub height: usize,
30    /// Текущее значение (для вывода)
31    pub current_value: f64,
32    /// Счётчик шагов
33    pub step: usize,
34}
35
36/// Клеточный автомат
37#[derive(Debug, Clone)]
38pub struct CellularAutomaton {
39    /// Имя автомата
40    name: String,
41    /// Тип автомата
42    cell_type: CellularType,
43    /// Правило (для 1D: 0-255)
44    rule: u8,
45    /// Размер (количество клеток для 1D, ширина/высота для 2D)
46    size: usize,
47    /// Способ преобразования поколения в выходной сигнал
48    output_mode: OutputMode,
49    /// Диапазон выходных значений
50    range: Range,
51    /// Случайное зерно для инициализации
52    rng_state: u64,
53}
54
55/// Режим преобразования поколения в сигнал
56#[derive(Debug, Clone, Copy, PartialEq)]
57pub enum OutputMode {
58    /// Использовать центральную клетку
59    Center,
60    /// Использовать сумму всех клеток (нормализованную)
61    Sum,
62    /// Использовать плотность активных клеток
63    Density,
64    /// Использовать конкретный индекс
65    Index(usize),
66}
67
68impl CellularAutomaton {
69    /// Создать новый 1D клеточный автомат
70    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    /// Создать новый Game of Life
83    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    /// Установить режим вывода
96    pub fn with_output_mode(mut self, mode: OutputMode) -> Self {
97        self.output_mode = mode;
98        self
99    }
100
101    /// Установить диапазон
102    pub fn with_range(mut self, range: Range) -> Self {
103        self.range = range;
104        self
105    }
106
107    /// Инициализировать случайное состояние
108    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    /// Случайный бит
117    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    /// Применить правило Вольфрама (1D)
127    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    /// Применить правило Game of Life (2D)
152    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                // Считаем живых соседей (8 направлений)
161                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                // Правила Game of Life
179                next[idx] = match (cell, neighbors) {
180                    (1, 2) | (1, 3) => 1, // Выживание
181                    (0, 3) => 1,          // Рождение
182                    _ => 0,               // Смерть
183                };
184            }
185        }
186
187        next
188    }
189
190    /// Вычислить выходное значение
191    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
271/// Предустановленные правила для 1D клеточных автоматов
272pub mod rules {
273    /// Правило 30: хаотическое поведение
274    pub const RULE_30: u8 = 30;
275    /// Правило 90: фрактальное (треугольник Серпинского)
276    pub const RULE_90: u8 = 90;
277    /// Правило 110: универсальное (Тьюринг-полное)
278    pub const RULE_110: u8 = 110;
279    /// Правило 184: дорожное движение
280    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}