summaryrefslogtreecommitdiff
path: root/dungeon
diff options
context:
space:
mode:
authoralf9310 <alf9310@rit.edu>2025-11-09 16:43:04 -0500
committeralf9310 <alf9310@rit.edu>2025-11-09 16:43:04 -0500
commit6266b64d7c7b0862db2b058021b81e7fb316330e (patch)
tree94f74baca427773e0cfad42fe1751aa6982b69b8 /dungeon
parentgraphics: refactor string formatting to use single common scratch buffer (diff)
downloadDungeonCrawl-6266b64d7c7b0862db2b058021b81e7fb316330e.tar.gz
DungeonCrawl-6266b64d7c7b0862db2b058021b81e7fb316330e.tar.bz2
DungeonCrawl-6266b64d7c7b0862db2b058021b81e7fb316330e.zip
dungeon_generation: variables changed from usize to u16 for pos compatability
Diffstat (limited to 'dungeon')
-rw-r--r--dungeon/src/bsp.rs125
-rw-r--r--dungeon/src/map.rs2
2 files changed, 57 insertions, 70 deletions
diff --git a/dungeon/src/bsp.rs b/dungeon/src/bsp.rs
index 5800d8d..f996405 100644
--- a/dungeon/src/bsp.rs
+++ b/dungeon/src/bsp.rs
@@ -1,39 +1,40 @@
//! 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 rand::prelude::IndexedRandom;
use rand::{Rng, SeedableRng};
use std::cmp; // for min/max
-use crate::map::{MAP_SIZE_USIZE, TILE_COUNT, Tile};
+use crate::map::{MAP_SIZE, TILE_COUNT, Tile};
use crate::{const_pos, pos::Pos};
/// `MIN_LEAF_SIZE` is the minimum width/height for a leaf to be considered "splittable".
-const MIN_LEAF_SIZE: usize = 8;
+const MIN_LEAF_SIZE: u16 = 8;
/// `MIN_ROOM_SIZE` is the minimum room size (w/h).
-const MIN_ROOM_SIZE: usize = 3;
+const MIN_ROOM_SIZE: u16 = 3;
/// `MAX_ROOM_SIZE` is the maximum room size (w/h).
-const MAX_ROOM_SIZE: usize = 12;
+const MAX_ROOM_SIZE: u16 = 12;
/// The `Rect` type represents a rectangle inside the map (x,y,width,height).
/// (x,y) is the top-left corner.
#[derive(Debug, Clone, Copy)]
struct Rect {
- x: usize,
- y: usize,
- w: usize,
- h: usize,
+ x: u16,
+ y: u16,
+ w: u16,
+ h: u16,
}
// since this is all "internal", you likely have to use unit tests and not doctests
impl Rect {
- fn new(x: usize, y: usize, w: usize, h: usize) -> Self {
+ fn new(x: u16, y: u16, w: u16, h: u16) -> Self {
Self { x, y, w, h }
}
- /// Returns the center point (cx, cy) of the rectangle.
- fn center(&self) -> (usize, usize) {
+ /// Returns the center point (cx, cy) of the rectangle as a Pos.
+ fn center(&self) -> Pos {
let cx = self.x + self.w / 2;
let cy = self.y + self.h / 2;
- (cx, cy)
+ Pos::new(cx as u16, cy as u16).unwrap_or(const_pos!(1, 1))
}
}
@@ -194,29 +195,27 @@ impl Node {
/// Return the "room center" for this subtree: if node has a room, return its center,
/// else try left then right.
- fn room_center(&self) -> Option<(usize, usize)> {
+ fn room_center(&self) -> Pos {
+ // Base case: if this node has a room, return its center
if let Some(room) = &self.room {
- return Some(room.center());
+ return room.center();
}
- if let Some(left) = &self.left
- && let Some(center) = left.room_center()
- {
- return Some(center);
+ if let Some(left) = &self.left {
+ return left.room_center();
}
- if let Some(right) = &self.right
- && let Some(center) = right.room_center()
- {
- return Some(center);
+ if let Some(right) = &self.right {
+ return right.room_center();
}
- None
+ // Fallback (should not happen)
+ const_pos!(1, 1)
}
/// Return a point (x,y) on the border of the room in this node.
- fn random_point_in_room<R: Rng>(&self, rng: &mut R) -> (usize, usize) {
+ fn random_point_in_room<R: Rng>(&self, rng: &mut R) -> Pos {
if let Some(room) = &self.room {
let rx = rng.random_range(room.x..(room.x + room.w));
let ry = rng.random_range(room.y..(room.y + room.h));
- (rx, ry)
+ Pos::new(rx, ry).unwrap_or(self.rect.center())
} else {
// No room: return center of rect
self.rect.center()
@@ -228,15 +227,15 @@ impl Node {
/// `corridors` is appended with pairs of points (x1,y1,x2,y2) describing corridors to carve.
fn connect_children(
&self,
- corridors: &mut Vec<(usize, usize, usize, usize)>,
+ corridors: &mut Vec<(Pos, Pos)>,
rng: &mut rand::rngs::StdRng,
) {
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 (x1, y1) = left.random_point_in_room(rng);
- let (x2, y2) = right.random_point_in_room(rng);
- corridors.push((x1, y1, x2, y2));
+ let left_point = left.random_point_in_room(rng);
+ let right_point = right.random_point_in_room(rng);
+ corridors.push((left_point, right_point));
left.connect_children(corridors, rng);
right.connect_children(corridors, rng);
@@ -247,29 +246,29 @@ impl Node {
/// Carve a room rectangle into the map (tile array) by setting tiles inside to Air.
fn carve_room(tiles: &mut [Tile; TILE_COUNT], room: &Rect) {
- for yy in room.y..(room.y + room.h) {
- for xx in room.x..(room.x + room.w) {
- let idx = xx + yy * MAP_SIZE_USIZE;
- tiles[idx] = Tile::Air;
+ 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;
}
}
}
/// Carve a horizontal corridor from x1..=x2 at y (inclusive)
-fn carve_h_corridor(tiles: &mut [Tile; TILE_COUNT], x1: usize, x2: usize, y: usize) {
+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_USIZE;
- tiles[idx] = Tile::Air;
+ let idx = x + y * MAP_SIZE;
+ tiles[idx as usize] = Tile::Air;
}
}
/// Carve a vertical corridor from y1..=y2 at x
-fn carve_v_corridor(tiles: &mut [Tile; TILE_COUNT], y1: usize, y2: usize, x: usize) {
+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_USIZE;
- tiles[idx] = Tile::Air;
+ let idx = x + y * MAP_SIZE;
+ tiles[idx as usize] = Tile::Air;
}
}
@@ -283,7 +282,7 @@ pub fn generate(seed: u64) -> (Box<[Tile; TILE_COUNT]>, Pos) {
// Rooms and coorridors will be carved as `Air` into the tile array.
// Build root rect (whole map)
- let root_rect = Rect::new(0, 0, MAP_SIZE_USIZE, MAP_SIZE_USIZE);
+ let root_rect = Rect::new(0, 0, MAP_SIZE, MAP_SIZE);
let mut root = Node::new(root_rect);
// Repeatedly try splitting nodes to produce the tree.
@@ -349,7 +348,9 @@ pub fn generate(seed: u64) -> (Box<[Tile; TILE_COUNT]>, Pos) {
root.connect_children(&mut corridors, &mut rng);
// Carve corridors. For each corridor (x1,y1,x2,y2), carve straight or L-shape.
- for (x1, y1, x2, y2) in corridors {
+ for (left_point, right_point) in corridors {
+ let (x1, y1) = (left_point.xy().0, left_point.xy().1);
+ let (x2, y2) = (right_point.xy().0, right_point.xy().1);
// If aligned horizontally or vertically, carve straight
if x1 == x2 {
carve_v_corridor(&mut tiles_box, y1, y2, x1);
@@ -367,31 +368,15 @@ pub fn generate(seed: u64) -> (Box<[Tile; TILE_COUNT]>, Pos) {
}
}
- // Choose player start randomly in the center of one of the rooms
- // TODO: Better Pos creation
- #[expect(clippy::cast_possible_truncation)]
- let player_start = root
- .room_center()
- .map(|(cx, cy)| Pos::new(cx as u16, cy as u16).unwrap_or(const_pos!(1, 1)))
- .unwrap_or(const_pos!(1, 1));
+ // Choose player start randomly in the center of one of the rooms (leaf nodes)
- // Choose random location among air tiles
- /*
- let mut player_start = Pos::new_unchecked(1, 1);
- let mut found = false;
- for pos in Pos::values() {
- if tiles_box[pos.idx()] == Tile::Air {
- player_start = pos;
- found = true;
- break;
- }
- }
- // If we didn't find any air (extremely unlikely), put start at (1,1) and carve it
- if !found {
- player_start = Pos::new_unchecked(1, 1);
- let idx = player_start.idx();
- tiles_box[idx] = Tile::Air;
- }*/
+ // Choose a random leaf node (room) for the player start
+ let mut leaves = vec![];
+ root.collect_leaves(&mut leaves);
+ let player_room = leaves.choose(&mut rng).unwrap_or(&leaves[0]);
+ let player_start = player_room.room_center();
+
+ // Set one tile to Stairs (exit) in a random room different from player start
// Return tiles and player_start
(tiles_box, player_start)
@@ -406,9 +391,9 @@ mod tests {
#[test]
fn test_rect_center() {
let rect = Rect::new(2, 2, 4, 4);
- let (cx, cy) = rect.center();
- assert_eq!(cx, 4);
- assert_eq!(cy, 4);
+ let center = rect.center();
+ assert_eq!(center.xy().0, 4);
+ assert_eq!(center.xy().1, 4);
}
#[test]
@@ -433,8 +418,8 @@ mod tests {
Some(room) => {
assert!(room.w >= MIN_ROOM_SIZE && room.h >= MIN_ROOM_SIZE);
assert!(room.x >= 1 && room.y >= 1);
- assert!(room.x + room.w < MAP_SIZE_USIZE);
- assert!(room.y + room.h < MAP_SIZE_USIZE);
+ assert!(room.x + room.w < MAP_SIZE);
+ assert!(room.y + room.h < MAP_SIZE);
}
None => panic!("Room should be created"),
}
diff --git a/dungeon/src/map.rs b/dungeon/src/map.rs
index eda467c..9e43faf 100644
--- a/dungeon/src/map.rs
+++ b/dungeon/src/map.rs
@@ -43,6 +43,8 @@ impl Tile {
pub fn is_wall(self) -> bool {
self == Self::Wall
}
+
+ // Index by u16
}
impl Default for Tile {
fn default() -> Self {