terrain_forge/algorithms/
wfc.rs1use crate::{Algorithm, Grid, Rng, Tile};
2
3#[derive(Debug, Clone)]
4pub struct WfcConfig {
5 pub floor_weight: f64,
6}
7
8impl Default for WfcConfig {
9 fn default() -> Self { Self { floor_weight: 0.4 } }
10}
11
12pub struct Wfc {
13 config: WfcConfig,
14}
15
16impl Wfc {
17 pub fn new(config: WfcConfig) -> Self { Self { config } }
18}
19
20impl Default for Wfc {
21 fn default() -> Self { Self::new(WfcConfig::default()) }
22}
23
24impl Algorithm<Tile> for Wfc {
25 fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
26 let mut rng = Rng::new(seed);
27 let (w, h) = (grid.width(), grid.height());
28
29 let mut possibilities: Vec<Vec<[bool; 2]>> = vec![vec![[true, true]; w]; h];
31
32 for cell in possibilities[0].iter_mut() {
34 *cell = [true, false];
35 }
36 for cell in possibilities[h - 1].iter_mut() {
37 *cell = [true, false];
38 }
39 for row in possibilities.iter_mut() {
40 row[0] = [true, false];
41 row[w - 1] = [true, false];
42 }
43
44 loop {
46 let mut min_entropy = 3;
48 let mut candidates = Vec::new();
49
50 for (y, row) in possibilities.iter().enumerate() {
51 for (x, cell) in row.iter().enumerate() {
52 let entropy = cell.iter().filter(|&&b| b).count();
53 if entropy > 1 {
54 if entropy < min_entropy {
55 min_entropy = entropy;
56 candidates.clear();
57 }
58 if entropy == min_entropy {
59 candidates.push((x, y));
60 }
61 }
62 }
63 }
64
65 if candidates.is_empty() { break; }
66
67 let &(cx, cy) = rng.pick(&candidates).unwrap();
68 let choose_floor = rng.chance(self.config.floor_weight) && possibilities[cy][cx][1];
69 possibilities[cy][cx] = if choose_floor { [false, true] } else { [true, false] };
70
71 propagate(&mut possibilities);
72 }
73
74 for (y, row) in possibilities.iter().enumerate() {
76 for (x, cell) in row.iter().enumerate() {
77 if cell[1] {
78 grid.set(x as i32, y as i32, Tile::Floor);
79 }
80 }
81 }
82 }
83
84 fn name(&self) -> &'static str { "WFC" }
85}
86
87fn propagate(poss: &mut [Vec<[bool; 2]>]) {
88 let mut changed = true;
89 while changed {
90 changed = false;
91 for row in poss.iter_mut() {
92 for cell in row.iter_mut() {
93 if cell.iter().filter(|&&b| b).count() != 1 { continue; }
94 }
96 }
97 }
98}