rust/landgen/src/wavefront_collapse/wavefront_collapse.rs
branchtransitional_engine
changeset 15916 e82de0410da5
parent 15915 8f093b1b18bc
child 15917 60b5639cc3a5
--- a/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs	Thu Feb 02 08:41:31 2023 +0100
+++ b/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs	Fri Feb 03 14:44:33 2023 +0100
@@ -1,5 +1,5 @@
 use integral_geometry::Size;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use vec2d::Vec2D;
 
 #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
@@ -44,14 +44,26 @@
     }
 }
 
-struct CollapseRule {
-    to_tile: Tile,
-    condition: fn([Tile; 4]) -> bool,
+pub struct CollapseRule {
+    tile: Tile,
+    right: HashSet<Tile>,
+    bottom: HashSet<Tile>,
+    left: HashSet<Tile>,
+    top: HashSet<Tile>,
 }
 
-#[derive(Default)]
 pub struct WavefrontCollapse {
-    rules: HashMap<Tile, Vec<CollapseRule>>,
+    rules: Vec<CollapseRule>,
+    grid: Vec2D<Tile>,
+}
+
+impl Default for WavefrontCollapse {
+    fn default() -> Self {
+        Self {
+            rules: Vec::new(),
+            grid: Vec2D::new(&Size::new(1, 1), Tile::Empty),
+        }
+    }
 }
 
 impl WavefrontCollapse {
@@ -60,69 +72,90 @@
         map_size: &Size,
         seed_fn: F,
         random_numbers: &mut I,
-    ) -> Vec2D<Tile> {
-        let mut land = Vec2D::new(&map_size, Tile::Empty);
+    ) {
+        self.grid = Vec2D::new(&map_size, Tile::Empty);
 
-        seed_fn(&mut land);
+        seed_fn(&mut self.grid);
 
-        while self.collapse_step(&mut land, random_numbers) {}
-
-        land
+        while self.collapse_step(random_numbers) {}
     }
 
-    fn add_rule(&mut self, from_tile: Tile, to_tile: Tile, condition: fn([Tile; 4]) -> bool) {
-        let rule = CollapseRule { to_tile, condition };
-        self.rules
-            .entry(from_tile)
-            .or_insert_with(Vec::new)
-            .push(rule);
+    fn add_rule(&mut self, rule: CollapseRule) {
+        self.rules.push(rule);
+    }
+
+    fn get_tile(&self, y: usize, x: usize) -> Tile {
+        self.grid.get(y, x).map(|p| *p).unwrap_or_default()
     }
 
-    fn collapse_step<I: Iterator<Item = u32>>(
-        &self,
-        land: &mut Vec2D<Tile>,
-        random_numbers: &mut I,
-    ) -> bool {
-        let mut collapse_occurred = false;
-        for x in 0..land.width() {
-            for y in 0..land.height() {
-                let current_tile = land.get(y, x).expect("valid iteration range");
+    fn collapse_step<I: Iterator<Item = u32>>(&mut self, random_numbers: &mut I) -> bool {
+        let mut tiles_to_collapse = (usize::max_value(), Vec::new());
+
+        // Iterate through the tiles in the land
+        for x in 0..self.grid.width() {
+            for y in 0..self.grid.height() {
+                let current_tile = self.get_tile(y, x);
 
-                if let Some(rules) = self.rules.get(&current_tile) {
-                    for rule in rules
+                if let Tile::Empty = current_tile {
+                    // calc entropy
+                    let right_tile = self.get_tile(y, x + 1);
+                    let bottom_tile = self.get_tile(y + 1, x);
+                    let left_tile = self.get_tile(y, x.wrapping_sub(1));
+                    let top_tile = self.get_tile(y.wrapping_sub(1), x);
+
+                    let possibilities: Vec<Tile> = self
+                        .rules
                         .iter()
-                        .cycle()
-                        .skip(
-                            random_numbers.next().unwrap_or_default() as usize % (rules.len() + 1),
-                        )
-                        .take(rules.len())
-                    {
-                        let neighbors = self.get_neighbors(&land, x, y);
-                        let have_neighbors = neighbors.iter().any(|t| !t.is_empty());
-                        if have_neighbors && (rule.condition)(neighbors) {
-                            *land.get_mut(y, x).expect("valid iteration range") = rule.to_tile;
-                            collapse_occurred = true;
-                            break;
+                        .filter_map(|rule| {
+                            if rule.right.contains(&right_tile)
+                                && rule.bottom.contains(&bottom_tile)
+                                && rule.left.contains(&left_tile)
+                                && rule.top.contains(&top_tile)
+                            {
+                                Some(rule.tile)
+                            } else {
+                                None
+                            }
+                        })
+                        .collect();
+
+                    let entropy = possibilities.len();
+                    if entropy > 0 && entropy <= tiles_to_collapse.0 {
+                        let entry = (
+                            y,
+                            x,
+                            possibilities
+                                [random_numbers.next().unwrap_or_default() as usize % entropy],
+                        );
+
+                        if entropy < tiles_to_collapse.0 {
+                            tiles_to_collapse = (entropy, vec![entry])
+                        } else {
+                            tiles_to_collapse.1.push(entry)
                         }
+                    } else {
+                        todo!("no collapse possible")
                     }
                 }
             }
         }
 
-        collapse_occurred
-    }
+        let tiles_to_collapse = tiles_to_collapse.1;
+        let possibilities_number = tiles_to_collapse.len();
+
+        if possibilities_number > 0 {
+            let (y, x, tile) = tiles_to_collapse
+                [random_numbers.next().unwrap_or_default() as usize % possibilities_number];
 
-    fn get_neighbors(&self, land: &Vec2D<Tile>, x: usize, y: usize) -> [Tile; 4] {
-        [
-            land.get(y, x + 1).map(|p| *p).unwrap_or_default(),
-            land.get(y + 1, x).map(|p| *p).unwrap_or_default(),
-            land.get(y, x.wrapping_sub(1))
-                .map(|p| *p)
-                .unwrap_or_default(),
-            land.get(y.wrapping_sub(1), x)
-                .map(|p| *p)
-                .unwrap_or_default(),
-        ]
+            *self
+                .grid
+                .get_mut(y, x)
+                .expect("correct iteration over grid") = tile;
+
+            true
+        } else {
+            false
+        }
     }
 }
 
@@ -139,15 +172,7 @@
         let mut wfc = WavefrontCollapse::default();
 
         let empty_land = Vec2D::new(&size, Tile::Empty);
-        let no_rules_land = wfc.generate_map(&size, |_| {}, &mut rnd);
 
-        assert_eq!(empty_land.as_slice(), no_rules_land.as_slice());
-
-        wfc.add_rule(Tile::Empty, Tile::Numbered(0), |neighbors| {
-            neighbors.iter().filter(|&n| *n == Tile::Empty).count() >= 2
-        });
-        let ruled_map = wfc.generate_map(&size, |_| {}, &mut rnd);
-
-        assert_eq!(ruled_map.as_slice(), empty_land.as_slice());
+        assert_eq!(empty_land.as_slice(), wfc.grid.as_slice());
     }
 }