Skip to main content

terrain_forge/algorithms/
dla.rs

1use crate::{Algorithm, Grid, Rng, Tile};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5/// Configuration for diffusion-limited aggregation.
6pub struct DlaConfig {
7    /// Number of particles to release. Default: 500.
8    pub num_particles: usize,
9    /// Maximum random walk steps per particle. Default: 1000.
10    pub max_walk_steps: usize,
11}
12
13impl Default for DlaConfig {
14    fn default() -> Self {
15        Self {
16            num_particles: 500,
17            max_walk_steps: 1000,
18        }
19    }
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23/// Diffusion-limited aggregation generator.
24pub struct Dla {
25    config: DlaConfig,
26}
27
28impl Dla {
29    /// Creates a new DLA generator with the given config.
30    pub fn new(config: DlaConfig) -> Self {
31        Self { config }
32    }
33}
34
35impl Default for Dla {
36    fn default() -> Self {
37        Self::new(DlaConfig::default())
38    }
39}
40
41impl Algorithm<Tile> for Dla {
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 dirs: [(i32, i32); 4] = [(0, -1), (1, 0), (0, 1), (-1, 0)];
46
47        // Seed in center
48        grid.set(w as i32 / 2, h as i32 / 2, Tile::Floor);
49
50        for _ in 0..self.config.num_particles {
51            let mut x = rng.range(1, w as i32 - 1);
52            let mut y = rng.range(1, h as i32 - 1);
53
54            for _ in 0..self.config.max_walk_steps {
55                let has_neighbor = dirs.iter().any(|&(dx, dy)| {
56                    grid.get(x + dx, y + dy)
57                        .map(|t| t.is_floor())
58                        .unwrap_or(false)
59                });
60
61                if has_neighbor {
62                    grid.set(x, y, Tile::Floor);
63                    break;
64                }
65
66                let (dx, dy) = dirs[rng.range_usize(0, 4)];
67                let (nx, ny) = (x + dx, y + dy);
68                if nx > 0 && nx < w as i32 - 1 && ny > 0 && ny < h as i32 - 1 {
69                    x = nx;
70                    y = ny;
71                }
72            }
73        }
74    }
75
76    fn name(&self) -> &'static str {
77        "DLA"
78    }
79}