summaryrefslogtreecommitdiff
path: root/dungeon/src/bsp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'dungeon/src/bsp.rs')
-rw-r--r--dungeon/src/bsp.rs68
1 files changed, 30 insertions, 38 deletions
diff --git a/dungeon/src/bsp.rs b/dungeon/src/bsp.rs
index 9b288a2..8b5fa3b 100644
--- a/dungeon/src/bsp.rs
+++ b/dungeon/src/bsp.rs
@@ -1,6 +1,7 @@
//! The `bsp` module implements a Binary-Space Partitioning based dungeon generator.
//! Produces a tile array and a player start position for a given seed.
+use core::panic;
use rand::prelude::IndexedRandom;
use rand::{Rng, SeedableRng};
use std::cmp; // for min/max
@@ -167,21 +168,13 @@ impl Node {
let room_w = rng.random_range(MIN_ROOM_SIZE..=max_w.min(MAX_ROOM_SIZE));
let room_h = rng.random_range(MIN_ROOM_SIZE..=max_h.min(MAX_ROOM_SIZE));
- // Choose position inside rect, leaving at least 1 tile to the border
+ // Define a valid range for room position, leaving at least 1 tile border
let x_range = (self.rect.x + 1)..=(self.rect.x + self.rect.w - 1 - room_w);
let y_range = (self.rect.y + 1)..=(self.rect.y + self.rect.h - 1 - room_h);
- // The ranges should be valid; if not, fallback to center
- let rx = if x_range.is_empty() {
- self.rect.x + (self.rect.w.saturating_sub(room_w)) / 2
- } else {
- rng.random_range(x_range)
- };
- let ry = if y_range.is_empty() {
- self.rect.y + (self.rect.h.saturating_sub(room_h)) / 2
- } else {
- rng.random_range(y_range)
- };
+ // Choose room position randomly within valid range
+ let rx = rng.random_range(x_range);
+ let ry = rng.random_range(y_range);
self.room = Some(Rect::new(rx, ry, room_w, room_h));
}
@@ -236,32 +229,33 @@ impl Node {
/// Connect rooms of child nodes recursively and collect corridors to carve.
///
- /// `corridors` is appended with pairs of points (x1,y1,x2,y2) describing corridors to carve.
- fn connect_children(
- &self,
- corridors: &mut Vec<(Pos, Pos)>,
- rng: &mut rand::rngs::StdRng,
- ) {
+ /// returns corridors: output vector of (Pos, Pos) pairs to connect
+ fn connect_children(&self, rng: &mut rand::rngs::StdRng) -> Vec<(Pos, Pos)> {
+ let mut corridors = Vec::new();
+
if let (Some(left), Some(right)) = (&self.left, &self.right) {
- // Connect a room (or corridor endpoint) from left subtree to right subtree
- // Select a random point from each side
- let left_point = left.random_point_in_room(rng);
- let right_point = right.random_point_in_room(rng);
+ // Get room centers for left and right children
+ let left_point = left.room_center();
+ let right_point = right.room_center();
corridors.push((left_point, right_point));
- left.connect_children(corridors, rng);
- right.connect_children(corridors, rng);
+ // Recursively connect children
+ let mut left_corridors = left.connect_children(rng);
+ let mut right_corridors = right.connect_children(rng);
+ corridors.append(&mut left_corridors);
+ corridors.append(&mut right_corridors);
}
- // leaf: nothing to connect!
+
+ corridors
}
}
-/// Carve a room rectangle into the map (tile array) by setting tiles inside to Air.
+/// Carve a room rectangle into the map (tile array) by setting tiles inside to Room.
fn carve_room(tiles: &mut [Tile; TILE_COUNT], room: &Rect) {
for y in room.y..(room.y + room.h) {
for x in room.x..(room.x + room.w) {
let idx = x + y * MAP_SIZE;
- tiles[idx as usize] = Tile::Air;
+ tiles[idx as usize] = Tile::Room;
}
}
}
@@ -271,16 +265,16 @@ fn carve_h_corridor(tiles: &mut [Tile; TILE_COUNT], x1: u16, x2: u16, y: u16) {
let (sx, ex) = if x1 <= x2 { (x1, x2) } else { (x2, x1) };
for x in sx..=ex {
let idx = x + y * MAP_SIZE;
- tiles[idx as usize] = Tile::Air;
+ tiles[idx as usize] = Tile::Hallway;
}
}
-/// Carve a vertical corridor from y1..=y2 at x
+/// Carve a vertical corridor from y1..=y2 at x (inclusive)
fn carve_v_corridor(tiles: &mut [Tile; TILE_COUNT], y1: u16, y2: u16, x: u16) {
let (sy, ey) = if y1 <= y2 { (y1, y2) } else { (y2, y1) };
for y in sy..=ey {
let idx = x + y * MAP_SIZE;
- tiles[idx as usize] = Tile::Air;
+ tiles[idx as usize] = Tile::Hallway;
}
}
@@ -356,8 +350,7 @@ pub fn generate(seed: u64) -> (Box<[Tile; TILE_COUNT]>, Pos) {
}
// Collect corridors (pairs of centers) by connecting children bottom-up
- let mut corridors = Vec::new();
- root.connect_children(&mut corridors, &mut rng);
+ let corridors = root.connect_children(&mut rng);
// Carve corridors. For each corridor (x1,y1,x2,y2), carve straight or L-shape.
for (left_point, right_point) in corridors {
@@ -440,7 +433,7 @@ mod tests {
assert!(room.x + room.w < MAP_SIZE);
assert!(room.y + room.h < MAP_SIZE);
}
- None => panic!("Room should be created"),
+ None => self::panic!("Room should exist"),
}
}
@@ -461,8 +454,7 @@ mod tests {
let mut node = Node::new(rect);
let mut rng = rand::rngs::StdRng::seed_from_u64(12345);
node.split(&mut rng);
- let mut corridors = Vec::new();
- node.connect_children(&mut corridors, &mut rng);
+ let corridors = node.connect_children(&mut rng);
assert!(!corridors.is_empty());
}
@@ -470,9 +462,9 @@ mod tests {
fn test_generate() {
let seed = 12345u64;
let (tiles, player_start) = generate(seed);
- // Check that tiles contain some Air tiles
- let air_count = tiles.iter().filter(|&&t| t == Tile::Air).count();
- assert!(air_count > 0);
+ // Check that tiles contain some Room tiles
+ let room_count = tiles.iter().filter(|&&t| t == Tile::Room).count();
+ assert!(room_count > 0);
// Check that player_start is within bounds
assert!(player_start.xy().0 < MAP_SIZE);
assert!(player_start.xy().1 < MAP_SIZE);