Skip to main content

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