1use core::fmt;
2use std::collections::HashMap;
3
4use crate::{Point, Tile};
5use anyhow::bail;
6use rand::{rngs::StdRng, Rng};
7pub struct BSPMap {
12 size: Point,
13 tiles: HashMap<Point, Tile>,
14 rooms: Vec<Room>,
15 min_room_size: Point,
16 max_room_size: Point,
17}
18impl BSPMap {
19 pub fn new(
20 size: Point,
21 mut seed: StdRng,
22 min_room_size: Point,
23 max_room_size: Point,
24 ) -> Result<Self, anyhow::Error> {
25 if size.x < 20 || size.y < 20 {
26 bail!("Size of a BSP_Map needs to be greater than or equal x : 20, y : 20")
27 }
28 if min_room_size.x >= max_room_size.x {
29 bail!("Minimum room size (x) needs to be less than maximum room size (x).")
30 }
31 if min_room_size.y >= max_room_size.y {
32 bail!("Minimum room size (y) needs to be less than maximum room size (y).")
33 }
34 if max_room_size.x >= size.x {
35 bail!("Maximum room size (x) must be less than map size (x).")
36 }
37 if max_room_size.y >= size.y {
38 bail!("Maximum room size (y) must be less than map size (y).")
39 }
40 let mut map = BSPMap {
41 size: size,
42 tiles: HashMap::new(),
43 rooms: Vec::new(),
44 min_room_size,
45 max_room_size,
46 };
47 map.place_rooms(&mut seed, map.min_room_size, map.max_room_size);
48 for y in 0..size.y {
50 map.tiles.insert(Point::new(0, y), Tile::Wall);
51 map.tiles.insert(Point::new(map.size.x, y), Tile::Wall);
52
53 }
54 for x in 0..size.x {
56 map.tiles.insert(Point::new(x, 0), Tile::Wall);
57 map.tiles.insert(Point::new(x, map.size.y), Tile::Wall);
58 }
59 let mut walls : Vec<Point> = Vec::new();
60 for tile in map.tiles.iter() {
62 if map.tiles.get(&Point::new(tile.0.x + 1, tile.0.y)).is_none() {
64 walls.push(Point::new(tile.0.x + 1, tile.0.y))
65 }
66 if map.tiles.get(&Point::new(tile.0.x + 1, tile.0.y + 1)).is_none() {
67 walls.push(Point::new(tile.0.x + 1, tile.0.y + 1))
68 }
69 if map.tiles.get(&Point::new(tile.0.x, tile.0.y + 1)).is_none() {
70 walls.push(Point::new(tile.0.x, tile.0.y + 1))
71 }
72 if tile.0.x != 0 && map.tiles.get(&Point::new(tile.0.x - 1, tile.0.y + 1)).is_none() {
73 walls.push(Point::new(tile.0.x - 1, tile.0.y + 1))
74 }
75 if tile.0.x != 0 && map.tiles.get(&Point::new(tile.0.x - 1, tile.0.y)).is_none() {
76 walls.push(Point::new(tile.0.x - 1, tile.0.y))
77 }
78 if tile.0.x != 0 && tile.0.y != 0 && map.tiles.get(&Point::new(tile.0.x - 1, tile.0.y - 1)).is_none() {
79 walls.push(Point::new(tile.0.x - 1, tile.0.y - 1))
80 }
81 if tile.0.y != 0 && map.tiles.get(&Point::new(tile.0.x, tile.0.y - 1)).is_none() {
82 walls.push(Point::new(tile.0.x, tile.0.y - 1))
83 }
84 if tile.0.y != 0 && map.tiles.get(&Point::new(tile.0.x + 1, tile.0.y - 1)).is_none() {
85 walls.push(Point::new(tile.0.x + 1, tile.0.y - 1))
86 }
87 }
88 for wall in walls.iter(){
90 map.tiles.insert(wall.clone(), Tile::Wall);
91 }
92 Ok(map)
93 }
94
95 pub fn get_tiles(&self) -> &HashMap<Point, Tile> {
96 &self.tiles
97 }
98
99 pub fn get_size(&self) -> &Point {
100 &self.size
101 }
102
103 pub fn get_rooms(&self) -> &Vec<Room> {
104 &&self.rooms
105 }
106 fn place_rooms(&mut self, rng: &mut StdRng, min_room_size: Point, max_room_size: Point) {
107 let mut root = Leaf::new(Point { x: 0, y: 0 }, self.size);
108 root.generate(rng, &min_room_size, &max_room_size);
110 root.create_rooms(rng, &min_room_size);
112 for leaf in root.iter() {
114 if leaf.is_leaf() {
115 if let Some(room) = leaf.get_room() {
116 self.add_room(&room);
117 }
118 }
119
120 for corridor in &leaf.corridors {
121 self.add_room(&corridor);
122 }
123 }
124 }
125
126 pub fn add_room(&mut self, room: &Room) {
127 for x in 0..room.size.x {
128 for y in 0..room.size.y {
129 self.tiles.insert(
130 Point::new(room.position.x + x, room.position.y + y),
131 Tile::Floor,
132 );
133 }
134 }
135 self.rooms.push(room.clone());
136 }
137}
138
139impl fmt::Display for BSPMap {
140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141 for row in 0..=self.size.x {
148 for col in 0..=self.size.y {
149 match self.tiles.get(&Point::new(row, col)) {
150 Some(x) => write!(f, "{}", x)?,
151 None => write!(f, "x")?,
152 }
153 }
154 write!(f, "\n")?
155 }
156 Ok(())
157 }
158}
159
160#[derive(Clone, Copy)]
161pub struct Room {
162 position: Point,
163 size: Point,
164}
165
166impl Room {
167 pub fn new(position: Point, size: Point) -> Self {
168 Room { position, size }
169 }
170 pub fn intersects(&self, other: &Room) -> bool {
173 let x_intersect: bool = ((self.position.x + self.size.x) > other.position.x)
174 && (other.position.x + (other.size.x) > self.position.x);
175 let y_intersect: bool = ((self.position.y + self.size.y) > other.position.y)
176 && (other.position.y + (other.size.y) > self.position.y);
177 x_intersect && y_intersect
178 }
179}
180
181pub struct Leaf {
182 position: Point,
184 size: Point,
186 left_child: Option<Box<Leaf>>,
187 right_child: Option<Box<Leaf>>,
188 room: Option<Room>,
189 corridors: Vec<Room>,
190}
191
192impl Leaf {
193 pub fn new(position: Point, size: Point) -> Self {
194 Leaf {
195 position,
196 size,
197 left_child: None,
198 right_child: None,
199 room: None,
200 corridors: Vec::new(),
201 }
202 }
203 pub fn split(
204 &mut self,
205 rng: &mut StdRng,
206 min_room_size: &Point,
207 max_room_size: &Point,
208 ) -> bool {
209 if self.left_child.is_some() || self.right_child.is_some() {
210 return false;
211 }
212 let split_horizontal: bool;
214 if (self.size.x > self.size.y) && (self.size.x as f32 / self.size.y as f32) >= 1.25 {
216 split_horizontal = false;
217 } else if (self.size.y > self.size.x) && (self.size.y as f32 / self.size.x as f32) >= 1.25 {
218 split_horizontal = true;
219 } else {
220 split_horizontal = rng.gen_bool(0.5);
221 };
222
223 let split = if split_horizontal == true {
225 rng.gen_range(min_room_size.x..=max_room_size.y as usize)
226 } else {
227 rng.gen_range(min_room_size.y..=max_room_size.y as usize)
228 };
229 if split_horizontal {
231 self.left_child = Some(Box::new(Leaf::new(
232 Point::new(self.position.x, self.position.y),
233 Point::new(self.size.x, split),
234 )));
235 if split >= self.size.y {
236 return false;
237 } else {
238 self.right_child = Some(Box::new(Leaf::new(
239 Point::new(self.position.x, self.position.y + split),
240 Point::new(self.size.x, self.size.y - split),
241 )));
242 }
243 } else {
244 self.left_child = Some(Box::new(Leaf::new(
245 Point::new(self.position.x, self.position.y),
246 Point::new(split, self.size.y),
247 )));
248 if split >= self.size.x {
249 return false;
250 } else {
251 self.right_child = Some(Box::new(Leaf::new(
252 Point::new(self.position.x + split, self.position.y),
253 Point::new(self.size.x - split, self.size.y),
254 )));
255 }
256 }
257 true
258 }
259
260 fn is_leaf(&self) -> bool {
261 match self.left_child {
262 None => match self.right_child {
263 None => true,
264 Some(_) => false,
265 },
266 Some(_) => false,
267 }
268 }
269
270 fn generate(&mut self, rng: &mut StdRng, min_room_size: &Point, max_room_size: &Point) {
271 if self.is_leaf() {
272 if self.split(rng, min_room_size, max_room_size) {
273 self.left_child
274 .as_mut()
275 .unwrap()
276 .generate(rng, min_room_size, max_room_size);
277 self.right_child
278 .as_mut()
279 .unwrap()
280 .generate(rng, min_room_size, max_room_size);
281 }
282 }
283 }
284
285 fn create_rooms(&mut self, rng: &mut StdRng, min_room_size: &Point) {
286 if let Some(ref mut room) = self.left_child {
288 room.as_mut().create_rooms(rng, min_room_size);
289 };
290 if let Some(ref mut room) = self.right_child {
292 room.as_mut().create_rooms(rng, min_room_size);
293 };
294
295 if self.is_leaf() {
297 let width: usize;
298 if min_room_size.x >= self.size.x {
299 width = min_room_size.x;
300 } else {
301 width = rng.gen_range(min_room_size.x..=self.size.x);
302 }
303
304 let height: usize;
305 if min_room_size.y >= self.size.y {
306 height = min_room_size.y;
307 } else {
308 height = rng.gen_range(min_room_size.y..=self.size.y);
309 }
310 let x: usize;
311 if self.size.x as f32 - width as f32 <= 0.0 {
312 x = 0
313 } else {
314 x = rng.gen_range(0..=(self.size.x - width));
315 }
316
317 let y: usize;
318 if self.size.y as f32 - height as f32 <= 0.0 {
319 y = 0
320 } else {
321 y = rng.gen_range(0..=(self.size.y - height));
322 }
323 self.room = Some(Room::new(
324 Point::new(x + self.position.x, y + self.position.y),
325 Point::new(width, height),
326 ));
327 }
328 if let (Some(ref mut left), Some(ref mut right)) =
329 (&mut self.left_child, &mut self.right_child)
330 {
331 create_corridors(rng, left, right);
332 };
333 }
334
335 fn get_room(&self) -> Option<Room> {
336 if self.is_leaf() {
337 return self.room;
338 }
339
340 let mut left_room: Option<Room> = None;
341 let mut right_room: Option<Room> = None;
342
343 if let Some(ref room) = self.left_child {
344 left_room = room.get_room();
345 }
346
347 if let Some(ref room) = self.right_child {
348 right_room = room.get_room();
349 }
350 match (left_room, right_room) {
351 (None, None) => None,
352 (Some(room), _) => Some(room),
353 (_, Some(room)) => Some(room),
354 }
355 }
356
357 fn iter(&self) -> LeafIterator {
358 LeafIterator::new(&self)
359 }
360}
361
362fn create_corridors(rng: &mut StdRng, left: &mut Box<Leaf>, right: &mut Box<Leaf>) {
363 if let (Some(left_room), Some(right_room)) = (left.get_room(), right.get_room()) {
364 let left_point = Point::new(
366 rng.gen_range(left_room.position.x..=(left_room.position.x + left_room.size.x)),
367 rng.gen_range(left_room.position.y..=(left_room.position.y + left_room.size.y)),
368 );
369 let right_point = Point::new(
371 rng.gen_range(right_room.position.x..=(right_room.position.x + right_room.size.x)),
372 rng.gen_range(right_room.position.y..=(right_room.position.y + right_room.size.y)),
373 );
374
375 if left_point.y <= right_point.y {
376 left.corridors.push(vert_corridor(left_point.x, left_point.y, right_point.y));
377 } else {
378 left.corridors.push(vert_corridor(left_point.x, right_point.y, left_point.y));
379 }
380
381 if left_point.x <= right_point.x {
382 left.corridors.push(horz_corridor(left_point.x, right_point.y, right_point.x));
383 } else {
384 left.corridors.push(horz_corridor(right_point.x, right_point.y, left_point.x));
385 }
386 };
387}
388
389fn horz_corridor(start_x: usize, start_y: usize, end_x: usize) -> Room {
390 Room::new(Point { x: start_x, y: start_y }, Point { x : end_x - start_x + 1, y : 1})
391}
392
393fn vert_corridor(start_x: usize, start_y: usize, end_y: usize) -> Room {
394 Room::new(Point { x: start_x, y: start_y }, Point { x: 1, y : end_y - start_y})
395}
396
397struct LeafIterator<'a> {
398 current_node: Option<&'a Leaf>,
399 right_nodes: Vec<&'a Leaf>,
400}
401
402impl<'a> LeafIterator<'a> {
403 fn new(root: &'a Leaf) -> LeafIterator<'a> {
404 let mut iter = LeafIterator {
405 right_nodes: vec![],
406 current_node: None,
407 };
408
409 iter.add_left_subtree(root);
410 iter
411 }
412
413 fn add_left_subtree(&mut self, node: &'a Leaf) {
414 if let Some(ref left) = node.left_child {
415 self.right_nodes.push(&*left);
416 }
417 if let Some(ref right) = node.right_child {
418 self.right_nodes.push(&*right);
419 }
420
421 self.current_node = Some(node);
422 }
423}
424
425impl<'a> Iterator for LeafIterator<'a> {
426 type Item = &'a Leaf;
427
428 fn next(&mut self) -> Option<Self::Item> {
429 let result = self.current_node.take();
430 if let Some(rest) = self.right_nodes.pop() {
431 self.add_left_subtree(rest);
432 }
433
434 match result {
435 Some(leaf) => Some(&*leaf),
436 None => None,
437 }
438 }
439}
440
441#[cfg(test)]
442mod test {
443 use rand::SeedableRng;
444
445 use crate::Point;
446
447 use super::{Leaf, Room};
448
449 #[test]
451 fn non_intersect_touching_walls() {
452 let room_one = Room::new(Point { x: 0, y: 0 }, Point { x: 10, y: 10 });
453 let room_two = Room::new(Point { x: 10, y: 10 }, Point { x: 10, y: 10 });
454 assert_eq!(false, room_one.intersects(&room_two));
455 }
456
457 #[test]
458 fn non_intersect_nothing_touch() {
459 let room_one = Room::new(Point { x: 0, y: 0 }, Point { x: 10, y: 10 });
460 let room_two = Room::new(Point { x: 15, y: 15 }, Point { x: 10, y: 10 });
461 assert_eq!(false, room_one.intersects(&room_two));
462 }
463
464 #[test]
465 fn intersect_top_right_corner() {
466 let room_one = Room::new(Point { x: 0, y: 0 }, Point { x: 10, y: 10 });
467 let room_two = Room::new(Point { x: 9, y: 9 }, Point { x: 123, y: 321 });
468 assert_eq!(true, room_one.intersects(&room_two));
469 }
470
471 #[test]
472 fn full_intersect() {
473 let room_one = Room::new(Point { x: 0, y: 0 }, Point { x: 10, y: 10 });
474 let room_two = Room::new(Point { x: 0, y: 0 }, Point { x: 20, y: 20 });
475 assert_eq!(true, room_one.intersects(&room_two));
476 }
477
478 #[test]
479 fn intersect_up_shift() {
480 let room_one = Room::new(Point { x: 0, y: 0 }, Point { x: 10, y: 10 });
481 let room_two = Room::new(Point { x: 0, y: 5 }, Point { x: 10, y: 10 });
482 assert_eq!(true, room_one.intersects(&room_two));
483 }
484 #[test]
491 fn test_no_split_too_small_horz() {}
492
493 #[test]
494 fn test_no_split_too_small_vert() {}
495
496 #[test]
497 fn test_leaf_split_horz() {
498 let mut vert_room = Leaf::new(Point::new(0, 0), Point::new(20, 50));
500
501 let split = vert_room.split(
502 &mut SeedableRng::seed_from_u64(123),
503 &Point::new(10, 10),
504 &Point::new(15, 15),
505 );
506 assert_eq!(split, true);
507 let left_child = vert_room.left_child.unwrap();
508 let right_child = vert_room.right_child.unwrap();
509
510 assert_eq!(left_child.position, vert_room.position);
512 assert_eq!(
513 left_child.size.x + left_child.size.y <= vert_room.size.x + vert_room.size.y,
514 true
515 );
516
517 assert_eq!(right_child.position.y >= vert_room.position.y, true);
518 assert_eq!(
519 right_child.size.x + right_child.size.y <= vert_room.size.x + vert_room.size.y,
520 true
521 );
522 }
523 }