terrain_forge/algorithms/
cellular.rs

1use crate::{Algorithm, Grid, Rng, Tile};
2
3#[derive(Debug, Clone)]
4pub struct CellularConfig {
5    pub initial_floor_chance: f64,
6    pub iterations: usize,
7    pub birth_limit: usize,
8    pub death_limit: usize,
9}
10
11impl Default for CellularConfig {
12    fn default() -> Self {
13        Self { initial_floor_chance: 0.45, iterations: 4, birth_limit: 5, death_limit: 4 }
14    }
15}
16
17pub struct CellularAutomata {
18    config: CellularConfig,
19}
20
21impl CellularAutomata {
22    pub fn new(config: CellularConfig) -> Self { Self { config } }
23}
24
25impl Default for CellularAutomata {
26    fn default() -> Self { Self::new(CellularConfig::default()) }
27}
28
29impl Algorithm<Tile> for CellularAutomata {
30    fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
31        let mut rng = Rng::new(seed);
32        let (w, h) = (grid.width(), grid.height());
33
34        for y in 1..h - 1 {
35            for x in 1..w - 1 {
36                if rng.chance(self.config.initial_floor_chance) {
37                    grid.set(x as i32, y as i32, Tile::Floor);
38                }
39            }
40        }
41
42        for _ in 0..self.config.iterations {
43            let snapshot: Vec<bool> = (0..w * h)
44                .map(|i| grid[(i % w, i / w)].is_floor())
45                .collect();
46
47            for y in 1..h - 1 {
48                for x in 1..w - 1 {
49                    let neighbors = count_neighbors(&snapshot, x, y, w);
50                    let is_floor = snapshot[y * w + x];
51                    let new_floor = if is_floor {
52                        neighbors >= self.config.death_limit
53                    } else {
54                        neighbors >= self.config.birth_limit
55                    };
56                    grid.set(x as i32, y as i32, if new_floor { Tile::Floor } else { Tile::Wall });
57                }
58            }
59        }
60    }
61
62    fn name(&self) -> &'static str { "CellularAutomata" }
63}
64
65fn count_neighbors(cells: &[bool], x: usize, y: usize, w: usize) -> usize {
66    let mut count = 0;
67    for dy in -1i32..=1 {
68        for dx in -1i32..=1 {
69            if dx == 0 && dy == 0 { continue; }
70            let nx = (x as i32 + dx) as usize;
71            let ny = (y as i32 + dy) as usize;
72            if cells[ny * w + nx] { count += 1; }
73        }
74    }
75    count
76}