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 {
10 Self { floor_weight: 0.4 }
11 }
12}
13
14pub struct Wfc {
15 config: WfcConfig,
16}
17
18impl Wfc {
19 pub fn new(config: WfcConfig) -> Self {
20 Self { config }
21 }
22}
23
24impl Default for Wfc {
25 fn default() -> Self {
26 Self::new(WfcConfig::default())
27 }
28}
29
30impl Algorithm<Tile> for Wfc {
31 fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
32 let mut rng = Rng::new(seed);
33 let (w, h) = (grid.width(), grid.height());
34
35 let mut possibilities: Vec<Vec<[bool; 2]>> = vec![vec![[true, true]; w]; h];
37
38 for cell in possibilities[0].iter_mut() {
40 *cell = [true, false];
41 }
42 for cell in possibilities[h - 1].iter_mut() {
43 *cell = [true, false];
44 }
45 for row in possibilities.iter_mut() {
46 row[0] = [true, false];
47 row[w - 1] = [true, false];
48 }
49
50 loop {
52 let mut min_entropy = 3;
54 let mut candidates = Vec::new();
55
56 for (y, row) in possibilities.iter().enumerate() {
57 for (x, cell) in row.iter().enumerate() {
58 let entropy = cell.iter().filter(|&&b| b).count();
59 if entropy > 1 {
60 if entropy < min_entropy {
61 min_entropy = entropy;
62 candidates.clear();
63 }
64 if entropy == min_entropy {
65 candidates.push((x, y));
66 }
67 }
68 }
69 }
70
71 if candidates.is_empty() {
72 break;
73 }
74
75 let &(cx, cy) = rng.pick(&candidates).unwrap();
76 let choose_floor = rng.chance(self.config.floor_weight) && possibilities[cy][cx][1];
77 possibilities[cy][cx] = if choose_floor {
78 [false, true]
79 } else {
80 [true, false]
81 };
82
83 propagate(&mut possibilities);
84 }
85
86 for (y, row) in possibilities.iter().enumerate() {
88 for (x, cell) in row.iter().enumerate() {
89 if cell[1] {
90 grid.set(x as i32, y as i32, Tile::Floor);
91 }
92 }
93 }
94 }
95
96 fn name(&self) -> &'static str {
97 "WFC"
98 }
99}
100
101fn propagate(poss: &mut [Vec<[bool; 2]>]) {
102 let mut changed = true;
103 while changed {
104 changed = false;
105 for row in poss.iter_mut() {
106 for cell in row.iter_mut() {
107 if cell.iter().filter(|&&b| b).count() != 1 {
108 continue;
109 }
110 }
112 }
113 }
114}