rust/landgen/src/wavefront_collapse/wavefront_collapse.rs
changeset 16058 de01be16df95
parent 15925 b0e8cc72bfef
child 16059 2acea266d297
equal deleted inserted replaced
16057:106674bb21b1 16058:de01be16df95
     1 use integral_geometry::Size;
     1 use integral_geometry::Size;
       
     2 use rand::distributions::Distribution;
       
     3 use rand::distributions::WeightedIndex;
       
     4 use rand::Rng;
     2 use std::collections::HashSet;
     5 use std::collections::HashSet;
     3 use vec2d::Vec2D;
     6 use vec2d::Vec2D;
     4 
     7 
     5 #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
     8 #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
     6 pub enum Tile {
     9 pub enum Tile {
    15     }
    18     }
    16 }
    19 }
    17 
    20 
    18 #[derive(Debug)]
    21 #[derive(Debug)]
    19 pub struct CollapseRule {
    22 pub struct CollapseRule {
       
    23     pub weight: u32,
    20     pub tile: Tile,
    24     pub tile: Tile,
    21     pub right: HashSet<Tile>,
    25     pub right: HashSet<Tile>,
    22     pub bottom: HashSet<Tile>,
    26     pub bottom: HashSet<Tile>,
    23     pub left: HashSet<Tile>,
    27     pub left: HashSet<Tile>,
    24     pub top: HashSet<Tile>,
    28     pub top: HashSet<Tile>,
    47             grid: Vec2D::new(&Size::new(1, 1), Tile::Empty),
    51             grid: Vec2D::new(&Size::new(1, 1), Tile::Empty),
    48             wrap,
    52             wrap,
    49         }
    53         }
    50     }
    54     }
    51 
    55 
    52     pub fn generate_map<I: Iterator<Item = u32>, F: FnOnce(&mut Vec2D<Tile>)>(
    56     pub fn generate_map<F: FnOnce(&mut Vec2D<Tile>)>(
    53         &mut self,
    57         &mut self,
    54         map_size: &Size,
    58         map_size: &Size,
    55         seed_fn: F,
    59         seed_fn: F,
    56         random_numbers: &mut I,
    60         random_numbers: &mut impl Rng,
    57     ) {
    61     ) {
    58         self.grid = Vec2D::new(map_size, Tile::Empty);
    62         self.grid = Vec2D::new(map_size, Tile::Empty);
    59 
    63 
    60         seed_fn(&mut self.grid);
    64         seed_fn(&mut self.grid);
    61 
    65 
    80         };
    84         };
    81 
    85 
    82         self.grid.get(y, x).copied().unwrap_or_default()
    86         self.grid.get(y, x).copied().unwrap_or_default()
    83     }
    87     }
    84 
    88 
    85     fn collapse_step<I: Iterator<Item = u32>>(&mut self, random_numbers: &mut I) -> bool {
    89     fn collapse_step(&mut self, random_numbers: &mut impl Rng) -> bool {
    86         let mut tiles_to_collapse = (usize::max_value(), Vec::new());
    90         let mut tiles_to_collapse = (usize::max_value(), Vec::new());
    87 
    91 
    88         // Iterate through the tiles in the land
    92         // Iterate through the tiles in the land
    89         for x in 0..self.grid.width() {
    93         for x in 0..self.grid.width() {
    90             for y in 0..self.grid.height() {
    94             for y in 0..self.grid.height() {
    95                     let right_tile = self.get_tile(y, x + 1);
    99                     let right_tile = self.get_tile(y, x + 1);
    96                     let bottom_tile = self.get_tile(y + 1, x);
   100                     let bottom_tile = self.get_tile(y + 1, x);
    97                     let left_tile = self.get_tile(y, x.wrapping_sub(1));
   101                     let left_tile = self.get_tile(y, x.wrapping_sub(1));
    98                     let top_tile = self.get_tile(y.wrapping_sub(1), x);
   102                     let top_tile = self.get_tile(y.wrapping_sub(1), x);
    99 
   103 
   100                     let possibilities: Vec<Tile> = self
   104                     let possibilities: Vec<(u32, Tile)> = self
   101                         .rules
   105                         .rules
   102                         .iter()
   106                         .iter()
   103                         .filter_map(|rule| {
   107                         .filter_map(|rule| {
   104                             if rule.right.contains(&right_tile)
   108                             if rule.right.contains(&right_tile)
   105                                 && rule.bottom.contains(&bottom_tile)
   109                                 && rule.bottom.contains(&bottom_tile)
   106                                 && rule.left.contains(&left_tile)
   110                                 && rule.left.contains(&left_tile)
   107                                 && rule.top.contains(&top_tile)
   111                                 && rule.top.contains(&top_tile)
   108                             {
   112                             {
   109                                 Some(rule.tile)
   113                                 Some((rule.weight, rule.tile))
   110                             } else {
   114                             } else {
   111                                 None
   115                                 None
   112                             }
   116                             }
   113                         })
   117                         })
   114                         .collect();
   118                         .collect();
   115 
   119 
   116                     let entropy = possibilities.len();
   120                     let entropy = possibilities.len();
   117                     if entropy > 0 {
   121                     if entropy > 0 {
   118                         if entropy <= tiles_to_collapse.0 {
   122                         if entropy <= tiles_to_collapse.0 {
   119                             let entry = (
   123                             let weights = possibilities.iter().map(|(weight, _)| *weight);
   120                                 y,
   124                             let distribution = WeightedIndex::new(weights).unwrap();
   121                                 x,
   125 
   122                                 possibilities
   126                             let entry = (y, x, possibilities[distribution.sample(random_numbers)]);
   123                                     [random_numbers.next().unwrap_or_default() as usize % entropy],
       
   124                             );
       
   125 
   127 
   126                             if entropy < tiles_to_collapse.0 {
   128                             if entropy < tiles_to_collapse.0 {
   127                                 tiles_to_collapse = (entropy, vec![entry])
   129                                 tiles_to_collapse = (entropy, vec![entry])
   128                             } else {
   130                             } else {
   129                                 tiles_to_collapse.1.push(entry)
   131                                 tiles_to_collapse.1.push(entry)
   145 
   147 
   146         let tiles_to_collapse = tiles_to_collapse.1;
   148         let tiles_to_collapse = tiles_to_collapse.1;
   147         let possibilities_number = tiles_to_collapse.len();
   149         let possibilities_number = tiles_to_collapse.len();
   148 
   150 
   149         if possibilities_number > 0 {
   151         if possibilities_number > 0 {
   150             let (y, x, tile) = tiles_to_collapse
   152             let weights = tiles_to_collapse.iter().map(|(_, _, (weight, _))| *weight);
   151                 [random_numbers.next().unwrap_or_default() as usize % possibilities_number];
   153             let distribution = WeightedIndex::new(weights).unwrap();
       
   154 
       
   155             let (y, x, (_, tile)) = tiles_to_collapse[distribution.sample(random_numbers)];
   152 
   156 
   153             *self
   157             *self
   154                 .grid
   158                 .grid
   155                 .get_mut(y, x)
   159                 .get_mut(y, x)
   156                 .expect("correct iteration over grid") = tile;
   160                 .expect("correct iteration over grid") = tile;