summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-11-11 20:46:17 -0500
committerFreya Murphy <freya@freyacat.org>2025-11-11 20:54:28 -0500
commit0bedd7991a7f2e3e35ae6b23d4f041c5a48454b9 (patch)
tree73175729fc202e0cda237871d88c3822c23bed8c
parentdungeon: rewrite const_pos! (diff)
downloadDungeonCrawl-0bedd7991a7f2e3e35ae6b23d4f041c5a48454b9.tar.gz
DungeonCrawl-0bedd7991a7f2e3e35ae6b23d4f041c5a48454b9.tar.bz2
DungeonCrawl-0bedd7991a7f2e3e35ae6b23d4f041c5a48454b9.zip
dungeon: bsp generate should return a floor to use same single rng
- `Floor::new` is now `Floor::from_parts` and takes in and rng - updated bps tests to operate on a floor - removed pos < MAP_SIZE checks, since `Pos` gurentees this - bsp::generate now returns a floor
-rw-r--r--dungeon/src/bsp.rs17
-rw-r--r--dungeon/src/lib.rs2
-rw-r--r--dungeon/src/map.rs26
-rw-r--r--dungeon/tests/bsp_tests.rs65
4 files changed, 56 insertions, 54 deletions
diff --git a/dungeon/src/bsp.rs b/dungeon/src/bsp.rs
index 48a00e1..e0ac001 100644
--- a/dungeon/src/bsp.rs
+++ b/dungeon/src/bsp.rs
@@ -6,6 +6,7 @@ use rand::prelude::IndexedRandom;
use rand::{Rng, SeedableRng};
use std::cmp; // for min/max
+use crate::Floor;
use crate::map::{MAP_SIZE, TILE_COUNT, Tile};
use crate::{const_pos, pos::Pos};
@@ -286,8 +287,8 @@ fn carve_v_corridor(tiles: &mut [Tile; TILE_COUNT], y1: u16, y2: u16, x: u16) {
}
/// Top-level generator function for the dungeon using BSP.
-/// Returns (tiles, player_start).
-pub fn generate(seed: u64) -> (Box<[Tile; TILE_COUNT]>, Pos) {
+/// Returns a `Floor`
+pub fn generate(seed: u64) -> Floor {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
// Initialize all tiles to walls
@@ -395,8 +396,8 @@ pub fn generate(seed: u64) -> (Box<[Tile; TILE_COUNT]>, Pos) {
let exit_idx = exit_pos.xy().0 + exit_pos.xy().1 * MAP_SIZE;
tiles_box[exit_idx as usize] = Tile::Stairs;
- // Return tiles and player_start
- (tiles_box, player_start)
+ // Return components turned into a `Floor`
+ Floor::from_parts(tiles_box, player_start, seed, rng)
}
/// BSP Unit Tests
@@ -467,12 +468,12 @@ mod tests {
#[test]
fn test_generate() {
let seed = 12345u64;
- let (tiles, player_start) = generate(seed);
+ let floor = generate(seed);
// Check that tiles contain some Room tiles
- let room_count = tiles.iter().filter(|&&t| t == Tile::Room).count();
+ let room_count = floor.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);
+ assert!(floor.player_start().xy().0 < MAP_SIZE);
+ assert!(floor.player_start().xy().1 < MAP_SIZE);
}
}
diff --git a/dungeon/src/lib.rs b/dungeon/src/lib.rs
index adc053e..4767942 100644
--- a/dungeon/src/lib.rs
+++ b/dungeon/src/lib.rs
@@ -121,7 +121,7 @@ impl From<Floor> for Dungeon {
// TODO: initalize rest of game state
// TODO: Randomize enemy positions/types
- let enemies = vec![Entity::zombie(floor.random_pos())];
+ let enemies = vec![Entity::zombie(floor.random_walkable_pos())];
Self {
floor,
diff --git a/dungeon/src/map.rs b/dungeon/src/map.rs
index a568950..7a6cc45 100644
--- a/dungeon/src/map.rs
+++ b/dungeon/src/map.rs
@@ -79,24 +79,28 @@ pub struct Floor {
player_start: Pos,
/// The seed used when generating the dungeon grid
seed: u64,
+ /// Seeded rng by `seed`
+ rng: StdRng,
/// The computed hash of the tile map
hash: RefCell<u64>,
/// If the tiles are dirty (hash needs to be recomputed)
dirty: RefCell<bool>,
- /// Custom rng
- rng: StdRng,
}
impl Floor {
- /// Internal constructor for `Floor`
- fn new(tiles: Box<[Tile; TILE_COUNT]>, player_start: Pos, seed: u64) -> Self {
- let rng = rand::rngs::StdRng::seed_from_u64(seed);
+ /// Construct a floor from its components
+ pub fn from_parts(
+ tiles: Box<[Tile; TILE_COUNT]>,
+ player_start: Pos,
+ seed: u64,
+ rng: StdRng,
+ ) -> Self {
Self {
tiles,
player_start,
seed,
+ rng,
hash: RefCell::new(0),
dirty: RefCell::new(true),
- rng,
}
}
@@ -134,9 +138,7 @@ impl Floor {
/// ```
#[must_use]
pub fn generate_seeded(seed: u64) -> Self {
- let (tiles, player_start) = bsp::generate(seed);
-
- Self::new(tiles, player_start, seed)
+ bsp::generate(seed)
}
/// Returns the start position of the player
@@ -210,7 +212,7 @@ impl Floor {
/// Returns a random open (no wall) position
#[must_use]
- pub fn random_pos(&mut self) -> Pos {
+ pub fn random_walkable_pos(&mut self) -> Pos {
loop {
let pos = self.rand().random();
if !self.get(pos).is_walkable() {
@@ -239,7 +241,9 @@ impl Default for Floor {
}
}
- Self::new(tiles, player_start, seed)
+ let rng = rand::rngs::StdRng::seed_from_u64(seed);
+
+ Self::from_parts(tiles, player_start, seed, rng)
}
}
impl Display for Floor {
diff --git a/dungeon/tests/bsp_tests.rs b/dungeon/tests/bsp_tests.rs
index 0a494b8..09d2d3a 100644
--- a/dungeon/tests/bsp_tests.rs
+++ b/dungeon/tests/bsp_tests.rs
@@ -17,11 +17,9 @@ mod tests {
fn test_bsp_integration() {
let test_seeds = generate_test_seeds(123456);
for seed in test_seeds {
- let (tiles, player_start) = bsp::generate(seed);
+ let floor = bsp::generate(seed);
// Basic integration test: ensure we get valid data
- assert!(!tiles.is_empty());
- assert!(player_start.x() < map::MAP_SIZE);
- assert!(player_start.y() < map::MAP_SIZE);
+ assert!(!floor.tiles().is_empty());
}
}
@@ -30,13 +28,10 @@ mod tests {
fn test_bsp_player_start() {
let test_seeds = generate_test_seeds(654321);
for seed in test_seeds {
- let (tiles, player_start) = bsp::generate(seed);
- // Ensure player start is within bounds
- assert!(player_start.x() < map::MAP_SIZE);
- assert!(player_start.y() < map::MAP_SIZE);
+ let floor = bsp::generate(seed);
// Ensure player start is a room tile
- let idx = player_start.idx();
- assert_eq!(tiles[idx], map::Tile::Room);
+ let start = floor.player_start();
+ assert_eq!(floor.get(start), map::Tile::Room);
}
}
@@ -45,9 +40,10 @@ mod tests {
fn test_bsp_2_or_more_rooms() {
let test_seeds = generate_test_seeds(111222);
for seed in test_seeds {
- let (tiles, _player_start) = bsp::generate(seed);
+ let floor = bsp::generate(seed);
// Ensure we have at least one room tile
- let room_count = tiles
+ let room_count = floor
+ .tiles()
.iter()
.filter(|&&tile| tile == map::Tile::Room)
.count();
@@ -63,19 +59,15 @@ mod tests {
fn test_bsp_walls_on_borders() {
let test_seeds = generate_test_seeds(777888);
for seed in test_seeds {
- let (tiles, _player_start) = bsp::generate(seed);
+ let floor = bsp::generate(seed);
// Go through all tiles, and ensure border tiles are walls
- for x in 0..map::MAP_SIZE {
- for y in 0..map::MAP_SIZE {
- let pos = Pos::new(x, y).unwrap_or(const_pos!(1, 1));
- if pos.is_border() {
- let idx = pos.idx();
- assert_eq!(
- tiles[idx],
- map::Tile::Wall,
- "Expected wall at border position {pos:?}"
- );
- }
+ for pos in Pos::values() {
+ if pos.is_border() {
+ assert_eq!(
+ floor.get(pos),
+ map::Tile::Wall,
+ "Expected wall at border position {pos:?}"
+ );
}
}
}
@@ -86,11 +78,16 @@ mod tests {
fn test_bsp_reproducibility() {
let test_seeds = generate_test_seeds(111111);
for seed in test_seeds {
- let (tiles1, player_start1) = bsp::generate(seed);
- let (tiles2, player_start2) = bsp::generate(seed);
- assert_eq!(tiles1, tiles2, "Tiles differ for same seed {seed}");
+ let floor1 = bsp::generate(seed);
+ let floor2 = bsp::generate(seed);
assert_eq!(
- player_start1, player_start2,
+ floor1.tiles(),
+ floor2.tiles(),
+ "Tiles differ for same seed {seed}"
+ );
+ assert_eq!(
+ floor1.player_start(),
+ floor2.player_start(),
"Player starts differ for same seed {seed}"
);
}
@@ -107,22 +104,22 @@ mod tests {
// Helper function to check that all air tiles are reachable from player start
fn check_air_tiles_reachable(seed: u64) {
- let (tiles, player_start) = bsp::generate(seed);
+ let floor = bsp::generate(seed);
// BFS to find all reachable air tiles
- let mut visited = vec![false; tiles.len()];
- let mut queue = vec![player_start];
- visited[player_start.idx()] = true;
+ let mut visited = vec![false; TILE_COUNT];
+ let mut queue = vec![floor.player_start()];
+ visited[floor.player_start().idx()] = true;
while let Some(pos) = queue.pop() {
for neighbor in pos.neighbors() {
let idx = neighbor.idx();
- if !visited[idx] && tiles[idx] != map::Tile::Wall {
+ if !visited[idx] && floor.get(neighbor) != map::Tile::Wall {
visited[idx] = true;
queue.push(neighbor);
}
}
}
- for (i, &tile) in tiles.iter().enumerate() {
+ for (i, &tile) in floor.tiles().iter().enumerate() {
if tile == map::Tile::Room || tile == map::Tile::Hallway {
assert!(visited[i], "Unreachable air tile at index {i}");
}