terrain_forge/algorithms/
drunkard.rs

1use crate::{Algorithm, Grid, Rng, Tile};
2
3#[derive(Debug, Clone)]
4pub struct DrunkardConfig {
5    pub floor_percent: f64,
6    pub max_iterations: usize,
7}
8
9impl Default for DrunkardConfig {
10    fn default() -> Self {
11        Self {
12            floor_percent: 0.4,
13            max_iterations: 50000,
14        }
15    }
16}
17
18pub struct DrunkardWalk {
19    config: DrunkardConfig,
20}
21
22impl DrunkardWalk {
23    pub fn new(config: DrunkardConfig) -> Self {
24        Self { config }
25    }
26}
27
28impl Default for DrunkardWalk {
29    fn default() -> Self {
30        Self::new(DrunkardConfig::default())
31    }
32}
33
34impl Algorithm<Tile> for DrunkardWalk {
35    fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
36        let mut rng = Rng::new(seed);
37        let (w, h) = (grid.width(), grid.height());
38        let target = ((w * h) as f64 * self.config.floor_percent) as usize;
39        let dirs: [(i32, i32); 4] = [(0, -1), (1, 0), (0, 1), (-1, 0)];
40
41        let mut x = w as i32 / 2;
42        let mut y = h as i32 / 2;
43        let mut floor_count = 0;
44
45        for _ in 0..self.config.max_iterations {
46            if floor_count >= target {
47                break;
48            }
49
50            if !grid.get(x, y).map(|t| t.is_floor()).unwrap_or(true) {
51                grid.set(x, y, Tile::Floor);
52                floor_count += 1;
53            }
54
55            let (dx, dy) = dirs[rng.range_usize(0, 4)];
56            let (nx, ny) = (x + dx, y + dy);
57            if nx > 0 && nx < w as i32 - 1 && ny > 0 && ny < h as i32 - 1 {
58                x = nx;
59                y = ny;
60            }
61        }
62    }
63
64    fn name(&self) -> &'static str {
65        "DrunkardWalk"
66    }
67}