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; |