Skip to main content

terrain_forge/algorithms/
drunkard.rs

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