# HG changeset patch # User unC0Rr # Date 1675436358 -3600 # Node ID 60b5639cc3a5d4980bbc7eba99eedc037a9659c3 # Parent e82de0410da59ebb0634c9ec55437fda310cec6e Add WIP for generation of rules diff -r e82de0410da5 -r 60b5639cc3a5 rust/landgen/src/wavefront_collapse/generator.rs --- a/rust/landgen/src/wavefront_collapse/generator.rs Fri Feb 03 14:44:33 2023 +0100 +++ b/rust/landgen/src/wavefront_collapse/generator.rs Fri Feb 03 15:59:18 2023 +0100 @@ -1,20 +1,17 @@ use super::tile_image::{Edge, TileImage}; -use super::wavefront_collapse::WavefrontCollapse; +use super::wavefront_collapse::{CollapseRule, Tile, WavefrontCollapse}; use crate::{LandGenerationParameters, LandGenerator}; use integral_geometry::Size; use png::Decoder; +use std::collections::HashSet; use std::fs::File; use std::io::BufReader; -pub struct WavefrontCollapseLandGenerator { - wfc: WavefrontCollapse, -} +pub struct WavefrontCollapseLandGenerator {} impl WavefrontCollapseLandGenerator { pub fn new() -> Self { - Self { - wfc: WavefrontCollapse::default(), - } + Self {} } pub fn load_template( @@ -75,7 +72,62 @@ ) -> land2d::Land2D { let tiles = self.load_template(parameters); - todo!() + let mut rules = Vec::::new(); + + let default_connection = HashSet::from_iter(vec![Tile::Outside, 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(); + + for p in 0..i { + if tiles[p].left_edge() == tile.right_edge() { + rules[p].left.insert(Tile::Numbered(i)); + right.insert(Tile::Numbered(p)); + } + + if tiles[p].right_edge() == tile.left_edge() { + rules[p].right.insert(Tile::Numbered(i)); + left.insert(Tile::Numbered(p)); + } + + if tiles[p].top_edge() == tile.bottom_edge() { + rules[p].top.insert(Tile::Numbered(i)); + bottom.insert(Tile::Numbered(p)); + } + + if tiles[p].bottom_edge() == tile.top_edge() { + rules[p].bottom.insert(Tile::Numbered(i)); + top.insert(Tile::Numbered(p)); + } + } + + rules.push(CollapseRule { + tile: Tile::Numbered(i), + top, + right, + bottom, + left, + }); + } + + let mut wfc = WavefrontCollapse::default(); + wfc.set_rules(rules); + + wfc.generate_map(&Size::new(40, 20), |_| {}, random_numbers); + + let grid = wfc.grid(); + + for r in 0..grid.height() { + for c in 0..grid.width() { + print!("{:?}", grid.get(r, c)); + } + + println!(); + } + + todo!("build result") } } diff -r e82de0410da5 -r 60b5639cc3a5 rust/landgen/src/wavefront_collapse/tile_image.rs --- a/rust/landgen/src/wavefront_collapse/tile_image.rs Fri Feb 03 14:44:33 2023 +0100 +++ b/rust/landgen/src/wavefront_collapse/tile_image.rs Fri Feb 03 15:59:18 2023 +0100 @@ -2,7 +2,7 @@ use std::rc::Rc; use vec2d::Vec2D; -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] pub struct Edge { id: I, symmetrical: bool, @@ -109,7 +109,78 @@ left: self.bottom.clone(), } } + + pub fn right_edge(&self) -> &Edge { + &self.right + } + + pub fn bottom_edge(&self) -> &Edge { + &self.bottom + } + + pub fn left_edge(&self) -> &Edge { + &self.left + } + + pub fn top_edge(&self) -> &Edge { + &self.top + } } #[cfg(test)] -mod tests {} +mod tests { + use super::*; + + #[test] + fn test_edge_new() { + let edge = Edge::new(1, true); + assert_eq!(edge.id, 1); + assert_eq!(edge.symmetrical, true); + assert_eq!(edge.reverse, false); + } + + #[test] + fn test_edge_reversed() { + let edge = Edge::new(1, true); + let reversed = edge.reversed(); + assert_eq!(reversed.id, edge.id); + assert_eq!(reversed.symmetrical, edge.symmetrical); + assert_eq!(reversed.reverse, false); + + let edge = Edge::new(1, false); + let reversed = edge.reversed(); + assert_eq!(reversed.id, edge.id); + assert_eq!(reversed.symmetrical, edge.symmetrical); + assert_eq!(reversed.reverse, true); + } + + #[test] + fn test_edge_equality() { + let edge1 = Edge::new(1, true); + let edge2 = Edge::new(1, true); + assert_eq!(edge1, edge2); + + let edge1 = Edge::new(1, false); + let edge2 = Edge::new(1, false); + assert_eq!(edge1, edge2); + + let edge1 = Edge::new(1, false); + let edge2 = Edge::new(2, false); + assert_ne!(edge1, edge2); + } + + #[test] + fn test_edge_equality_with_reverse() { + let edge1 = Edge::new(1, true); + let edge2 = edge1.reversed(); + assert_eq!(edge1, edge2); + + let edge1 = Edge::new(1, false); + let edge2 = edge1.reversed(); + assert_ne!(edge1, edge2); + + let edge1 = Edge::new(1, true); + let edge2 = edge1.reversed().reversed(); + assert_eq!(edge1, edge2); + } +} diff -r e82de0410da5 -r 60b5639cc3a5 rust/landgen/src/wavefront_collapse/wavefront_collapse.rs --- a/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs Fri Feb 03 14:44:33 2023 +0100 +++ b/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs Fri Feb 03 15:59:18 2023 +0100 @@ -1,16 +1,16 @@ use integral_geometry::Size; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use vec2d::Vec2D; #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub enum Tile { Empty, Outside, - Numbered(u32), + Numbered(usize), } impl Tile { - fn is(&self, i: u32) -> bool { + fn is(&self, i: usize) -> bool { *self == Tile::Numbered(i) } @@ -22,7 +22,7 @@ } } - fn is_empty_or(&self, i: u32) -> bool { + fn is_empty_or(&self, i: usize) -> bool { match self { Tile::Numbered(n) => *n == i, Tile::Empty => true, @@ -30,7 +30,7 @@ } } - fn is_void_or(&self, i: u32) -> bool { + fn is_void_or(&self, i: usize) -> bool { match self { Tile::Numbered(n) => *n == i, _ => true, @@ -44,12 +44,13 @@ } } +#[derive(Debug)] pub struct CollapseRule { - tile: Tile, - right: HashSet, - bottom: HashSet, - left: HashSet, - top: HashSet, + pub tile: Tile, + pub right: HashSet, + pub bottom: HashSet, + pub left: HashSet, + pub top: HashSet, } pub struct WavefrontCollapse { @@ -80,7 +81,11 @@ while self.collapse_step(random_numbers) {} } - fn add_rule(&mut self, rule: CollapseRule) { + pub fn set_rules(&mut self, rules: Vec) { + self.rules = rules; + } + + pub fn add_rule(&mut self, rule: CollapseRule) { self.rules.push(rule); } @@ -120,21 +125,30 @@ .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 > 0 { + if 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) + if entropy < tiles_to_collapse.0 { + tiles_to_collapse = (entropy, vec![entry]) + } else { + tiles_to_collapse.1.push(entry) + } } } else { - todo!("no collapse possible") + println!("We're here: {}, {}", x, y); + println!( + "Neighbour tiles are: {:?} {:?} {:?} {:?}", + right_tile, bottom_tile, left_tile, top_tile + ); + println!("Rules are: {:?}", self.rules); + + todo!("no collapse possible - what to do?") } } } @@ -157,6 +171,10 @@ false } } + + pub fn grid(&self) -> &Vec2D { + &self.grid + } } #[cfg(test)] @@ -168,11 +186,13 @@ #[test] fn test_wavefront_collapse() { let size = Size::new(4, 4); - let mut rnd = [0u32; 64].into_iter(); + let mut rnd = [0u32; 64].into_iter().cycle(); let mut wfc = WavefrontCollapse::default(); + wfc.generate_map(&size, |_| {}, &mut rnd); + let empty_land = Vec2D::new(&size, Tile::Empty); - assert_eq!(empty_land.as_slice(), wfc.grid.as_slice()); + assert_eq!(empty_land.as_slice(), wfc.grid().as_slice()); } }