Allow defining compatible edges for grid, add few more templates transitional_engine
authorunC0Rr
Wed, 15 Feb 2023 12:36:24 +0100
branchtransitional_engine
changeset 15955 b0e8cc72bfef
parent 15954 9502611bffc1
child 15956 c273908218f3
Allow defining compatible edges for grid, add few more templates
rust/landgen/src/wavefront_collapse/generator.rs
rust/landgen/src/wavefront_collapse/wavefront_collapse.rs
rust/mapgen/src/lib.rs
rust/mapgen/src/template/wavefront_collapse.rs
share/hedgewars/Data/wfc_templates.yaml
--- a/rust/landgen/src/wavefront_collapse/generator.rs	Tue Feb 14 08:52:20 2023 +0100
+++ b/rust/landgen/src/wavefront_collapse/generator.rs	Wed Feb 15 12:36:24 2023 +0100
@@ -36,9 +36,18 @@
 }
 
 #[derive(Clone)]
+pub struct NonStrictEdgesDescription {
+    pub top: Option<EdgeDescription>,
+    pub right: Option<EdgeDescription>,
+    pub bottom: Option<EdgeDescription>,
+    pub left: Option<EdgeDescription>,
+}
+
+#[derive(Clone)]
 pub struct TemplateDescription {
     pub size: Size,
     pub tiles: Vec<TileDescription>,
+    pub edges: NonStrictEdgesDescription,
     pub wrap: bool,
 }
 
@@ -107,34 +116,19 @@
             }
         }
 
-        let edges: Vec<Edge<String>> = [
-            &tile_description.edges.top,
-            &tile_description.edges.right,
-            &tile_description.edges.bottom,
-            &tile_description.edges.left,
-        ]
-        .iter()
-        .map(|descr| {
-            let edge = Edge::new(descr.name.clone(), descr.symmetrical.unwrap_or_default());
-
-            if descr.reversed.unwrap_or_default() {
-                edge.reversed()
-            } else {
-                edge
-            }
-        })
-        .collect();
-
-        let [top_edge, right_edge, bottom_edge, left_edge] = edges.as_slice() else {
-            unreachable!()
-        };
+        let [top_edge, right_edge, bottom_edge, left_edge]: [Edge<String>; 4] = [
+            (&tile_description.edges.top).into(),
+            (&tile_description.edges.right).into(),
+            (&tile_description.edges.bottom).into(),
+            (&tile_description.edges.left).into(),
+        ];
 
         let tile = TileImage::<T, String>::new(
             tiles_image,
-            top_edge.clone(),
-            right_edge.clone(),
-            bottom_edge.clone(),
-            left_edge.clone(),
+            top_edge,
+            right_edge,
+            bottom_edge,
+            left_edge,
         );
 
         result.push(tile.clone());
@@ -178,25 +172,72 @@
 
         result
     }
-}
 
-impl LandGenerator for WavefrontCollapseLandGenerator {
-    fn generate_land<T: Copy + PartialEq + Default, I: Iterator<Item = u32>>(
+    pub fn build_rules<T: Copy + PartialEq + Default>(
         &self,
-        parameters: &LandGenerationParameters<T>,
-        random_numbers: &mut I,
-    ) -> land2d::Land2D<T> {
-        let tiles = self.load_template(parameters);
+        tiles: &[TileImage<T, String>],
+    ) -> Vec<CollapseRule> {
+        let [grid_top_edge, grid_right_edge, grid_bottom_edge, grid_left_edge]: [Option<
+            Edge<String>,
+        >; 4] = [
+            self.template.edges.top.as_ref(),
+            self.template.edges.right.as_ref(),
+            self.template.edges.bottom.as_ref(),
+            self.template.edges.left.as_ref(),
+        ]
+        .map(|opt| opt.map(|d| d.into()));
 
         let mut rules = Vec::<CollapseRule>::new();
 
-        let default_connection = HashSet::from_iter(vec![Tile::Outside, Tile::Empty].into_iter());
+        let default_connection = HashSet::from_iter(vec![Tile::Empty].into_iter());
         for (i, tile) in tiles.iter().enumerate() {
             let mut right = default_connection.clone();
             let mut bottom = default_connection.clone();
             let mut left = default_connection.clone();
             let mut top = default_connection.clone();
 
+            // compatibility with grid edges
+            if grid_top_edge
+                .as_ref()
+                .map(|e| e.is_compatible(tile.top_edge()))
+                .unwrap_or(true)
+            {
+                top.insert(Tile::Outside);
+            }
+            if grid_right_edge
+                .as_ref()
+                .map(|e| e.is_compatible(tile.right_edge()))
+                .unwrap_or(true)
+            {
+                right.insert(Tile::Outside);
+            }
+            if grid_bottom_edge
+                .as_ref()
+                .map(|e| e.is_compatible(tile.bottom_edge()))
+                .unwrap_or(true)
+            {
+                bottom.insert(Tile::Outside);
+            }
+            if grid_left_edge
+                .as_ref()
+                .map(|e| e.is_compatible(tile.left_edge()))
+                .unwrap_or(true)
+            {
+                left.insert(Tile::Outside);
+            }
+
+            // compatibility with itself
+            if tile.left_edge().is_compatible(tile.right_edge()) {
+                left.insert(Tile::Numbered(i));
+                right.insert(Tile::Numbered(i));
+            }
+
+            if tile.top_edge().is_compatible(tile.bottom_edge()) {
+                top.insert(Tile::Numbered(i));
+                bottom.insert(Tile::Numbered(i));
+            }
+
+            // compatibility with previously defined tiles
             for p in 0..i {
                 if tiles[p].left_edge().is_compatible(tile.right_edge()) {
                     rules[p].left.insert(Tile::Numbered(i));
@@ -228,6 +269,19 @@
             });
         }
 
+        rules
+    }
+}
+
+impl LandGenerator for WavefrontCollapseLandGenerator {
+    fn generate_land<T: Copy + PartialEq + Default, I: Iterator<Item = u32>>(
+        &self,
+        parameters: &LandGenerationParameters<T>,
+        random_numbers: &mut I,
+    ) -> land2d::Land2D<T> {
+        let tiles = self.load_template(parameters);
+        let rules = self.build_rules(&tiles);
+
         let mut wfc = WavefrontCollapse::new(self.template.wrap);
         wfc.set_rules(rules);
 
@@ -244,6 +298,7 @@
 
         wfc.generate_map(&wfc_size, |_| {}, random_numbers);
 
+        // render tiles into resulting land array
         let mut result = land2d::Land2D::new(&self.template.size, parameters.zero);
         let offset_y = result.height() - result.play_height();
         let offset_x = (result.width() - result.play_width()) / 2;
@@ -272,3 +327,15 @@
         result
     }
 }
+
+impl From<&EdgeDescription> for Edge<String> {
+    fn from(val: &EdgeDescription) -> Self {
+        let edge = Edge::new(val.name.clone(), val.symmetrical.unwrap_or_default());
+
+        if val.reversed.unwrap_or_default() {
+            edge.reversed()
+        } else {
+            edge
+        }
+    }
+}
--- a/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs	Tue Feb 14 08:52:20 2023 +0100
+++ b/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs	Wed Feb 15 12:36:24 2023 +0100
@@ -70,17 +70,15 @@
         let x = if self.wrap {
             if x == usize::MAX {
                 self.grid.width() - 1
+            } else if x == self.grid.width() {
+                0
             } else {
-                if x == self.grid.width() {
-                    0
-                } else {
-                    x
-                }
+                x
             }
         } else {
             x
         };
-        
+
         self.grid.get(y, x).copied().unwrap_or_default()
     }
 
--- a/rust/mapgen/src/lib.rs	Tue Feb 14 08:52:20 2023 +0100
+++ b/rust/mapgen/src/lib.rs	Wed Feb 15 12:36:24 2023 +0100
@@ -248,7 +248,7 @@
 
 #[cfg(test)]
 mod tests {
-    use crate::{MapGenerator, TemplateType, OutlineTemplate};
+    use crate::{MapGenerator, OutlineTemplate, TemplateType};
     use rand::thread_rng;
 
     #[test]
--- a/rust/mapgen/src/template/wavefront_collapse.rs	Tue Feb 14 08:52:20 2023 +0100
+++ b/rust/mapgen/src/template/wavefront_collapse.rs	Wed Feb 15 12:36:24 2023 +0100
@@ -42,6 +42,16 @@
 
 #[derive(Deserialize)]
 pub struct TileDescriptionHelper(#[serde(with = "TileDesc")] TileDescription);
+#[derive(Deserialize)]
+pub struct EdgeDescriptionHelper(#[serde(with = "EdgeDesc")] EdgeDescription);
+
+#[derive(Deserialize)]
+pub struct NonStrictEdgesDesc {
+    pub top: Option<EdgeDescriptionHelper>,
+    pub right: Option<EdgeDescriptionHelper>,
+    pub bottom: Option<EdgeDescriptionHelper>,
+    pub left: Option<EdgeDescriptionHelper>,
+}
 
 #[derive(Deserialize)]
 pub struct TemplateDesc {
@@ -52,6 +62,7 @@
     pub put_girders: bool,
     pub max_hedgehogs: u8,
     pub wrap: bool,
+    pub edges: Option<NonStrictEdgesDesc>,
     pub tiles: Vec<TileDescriptionHelper>,
 }
 
@@ -63,6 +74,18 @@
 
 impl From<&TemplateDesc> for TemplateDescription {
     fn from(desc: &TemplateDesc) -> Self {
+        let [top, right, bottom, left] = if let Some(edges) = &desc.edges {
+            [
+                edges.top.as_ref(),
+                edges.right.as_ref(),
+                edges.bottom.as_ref(),
+                edges.left.as_ref(),
+            ]
+            .map(|e| e.map(|EdgeDescriptionHelper(e)| e.clone()))
+        } else {
+            [None, None, None, None]
+        };
+
         Self {
             size: Size::new(desc.width, desc.height),
             tiles: desc
@@ -71,6 +94,12 @@
                 .map(|TileDescriptionHelper(t)| t.clone())
                 .collect(),
             wrap: desc.wrap,
+            edges: NonStrictEdgesDescription {
+                top,
+                right,
+                bottom,
+                left,
+            },
         }
     }
 }
--- a/share/hedgewars/Data/wfc_templates.yaml	Tue Feb 14 08:52:20 2023 +0100
+++ b/share/hedgewars/Data/wfc_templates.yaml	Wed Feb 15 12:36:24 2023 +0100
@@ -1,8 +1,8 @@
+---
 # Templates for wavefront collapse map generator in hedgewars
 
 templates:
-  # 00
-  -
+  - &template_00
     width: 3960
     height: 1920
     can_invert: false
@@ -10,7 +10,11 @@
     put_girders: true
     max_hedgehogs: 40
     wrap: true
-    tiles:
+    edges:
+      bottom:
+        name: "ff"
+        symmetrical: true
+    tiles: &template_00_tiles
       - name: "120_bar.png"
         edges:
           top:
@@ -127,12 +131,54 @@
         can_rotate180: false
         can_rotate270: false
 
+  - &template_01
+    width: 3960
+    height: 1920
+    can_invert: false
+    is_negative: false
+    put_girders: true
+    max_hedgehogs: 40
+    wrap: false
+    edges: &open_edges
+      top:
+        name: "ee"
+        symmetrical: true
+      right:
+        name: "ee"
+        symmetrical: true
+      bottom:
+        name: "ff"
+        symmetrical: true
+      left:
+        name: "ee"
+        symmetrical: true
+    tiles: *template_00_tiles
 
+  - &template_02
+    width: 1200
+    height: 600
+    can_invert: false
+    is_negative: false
+    put_girders: true
+    max_hedgehogs: 24
+    wrap: false
+    edges: *open_edges
+    tiles: *template_00_tiles
 
+  - &template_03
+    width: 720
+    height: 7920
+    can_invert: false
+    is_negative: false
+    put_girders: true
+    max_hedgehogs: 64
+    wrap: false
+    edges: *open_edges
+    tiles: *template_00_tiles
 
 template_types:
-  small: [0]
-  medium: [0]
-  large: [0]
+  small: [2]
+  medium: [1]
+  large: [1]
   cavern: [0]
-  wacky: [0]
+  wacky: [3]