diff options
| author | alf9310 <alf9310@rit.edu> | 2025-11-09 16:43:04 -0500 |
|---|---|---|
| committer | alf9310 <alf9310@rit.edu> | 2025-11-09 16:43:04 -0500 |
| commit | 6266b64d7c7b0862db2b058021b81e7fb316330e (patch) | |
| tree | 94f74baca427773e0cfad42fe1751aa6982b69b8 /dungeon | |
| parent | graphics: refactor string formatting to use single common scratch buffer (diff) | |
| download | DungeonCrawl-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.rs | 125 | ||||
| -rw-r--r-- | dungeon/src/map.rs | 2 |
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 { |