summaryrefslogtreecommitdiff
path: root/dungeon
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-11-09 20:53:36 -0500
committerFreya Murphy <freya@freyacat.org>2025-11-09 20:53:36 -0500
commit2f7c963c8ffb3bceef08f760cdfac758ddae3758 (patch)
treefef94830f2f87586e27ced85d9a9ca965fe32398 /dungeon
parentdungeon: fix astar neighbor generation (diff)
downloadDungeonCrawl-2f7c963c8ffb3bceef08f760cdfac758ddae3758.tar.gz
DungeonCrawl-2f7c963c8ffb3bceef08f760cdfac758ddae3758.tar.bz2
DungeonCrawl-2f7c963c8ffb3bceef08f760cdfac758ddae3758.zip
dungeon: always use seeded rand, other minor refactoring
Diffstat (limited to 'dungeon')
-rw-r--r--dungeon/src/entity.rs18
-rw-r--r--dungeon/src/map.rs97
-rw-r--r--dungeon/src/pos.rs62
3 files changed, 110 insertions, 67 deletions
diff --git a/dungeon/src/entity.rs b/dungeon/src/entity.rs
index badb43c..931947c 100644
--- a/dungeon/src/entity.rs
+++ b/dungeon/src/entity.rs
@@ -1,5 +1,7 @@
//! The `entity` module contains structures of all entities including players and enimies.
+use rand::Rng;
+
use crate::{Direction, FPos, Floor, Pos, astar, const_pos};
/// `PLAYER_FULL_HEALTH` is the starting health of the player entity
@@ -70,12 +72,13 @@ impl EnemyMoveState {
None
}
- pub fn new_roam(starting_pos: Pos, floor: &Floor) -> Self {
- let mut rand_dir = Direction::get_random_dir();
- let mut rand_tiles = rand::random_range(MIN_ROAM_DIST..=MAX_ROAM_DIST);
+ pub fn new_roam(starting_pos: Pos, floor: &mut Floor) -> Self {
let mut loop_index = 0;
loop {
- if let Some(p) = starting_pos.step_by(rand_dir, rand_tiles) {
+ let dir = Direction::random(floor.rand());
+ let dist = floor.rand().random_range(MIN_ROAM_DIST..=MIN_ROAM_DIST);
+
+ if let Some(p) = starting_pos.step_by(dir, dist) {
if !floor.get(p).is_wall() {
if let Some(data) = astar::astar(starting_pos, p, floor) {
let mut path = data.0;
@@ -85,9 +88,6 @@ impl EnemyMoveState {
}
}
- rand_dir = Direction::get_random_dir();
- rand_tiles = rand::random_range(MIN_ROAM_DIST..=MAX_ROAM_DIST);
-
// Safety check
loop_index += 1;
if loop_index >= 100 {
@@ -186,7 +186,7 @@ impl Entity {
}
}
- pub fn handle_movement(&mut self, player_pos: Pos, floor: &Floor, delta_time: f32) {
+ pub fn handle_movement(&mut self, player_pos: Pos, floor: &mut Floor, delta_time: f32) {
match &self.kind {
EntityKind::Zombie(move_state) => {
self.zombie_movement(move_state.clone(), player_pos, delta_time, floor);
@@ -201,7 +201,7 @@ impl Entity {
move_state: EnemyMoveState,
player_pos: Pos,
delta_time: f32,
- floor: &Floor,
+ floor: &mut Floor,
) {
// Check if player is in range
if !matches!(move_state, EnemyMoveState::Attack { .. })
diff --git a/dungeon/src/map.rs b/dungeon/src/map.rs
index c204cac..8d9a391 100644
--- a/dungeon/src/map.rs
+++ b/dungeon/src/map.rs
@@ -7,6 +7,7 @@ use strum_macros::EnumIter;
use std::{
cell::RefCell,
+ fmt::{Display, Write},
hash::{DefaultHasher, Hash, Hasher},
};
@@ -40,12 +41,14 @@ impl Tile {
}
/// Returns if the tile is a wall
- pub fn is_wall(self) -> bool {
- self == Self::Wall
+ #[must_use]
+ pub const fn is_wall(self) -> bool {
+ matches!(self, Self::Wall)
}
/// Returns if the tile is walkable
- pub fn is_walkable(self) -> bool {
+ #[must_use]
+ pub const fn is_walkable(self) -> bool {
matches!(self, Self::Air)
}
}
@@ -54,6 +57,16 @@ impl Default for Tile {
Self::Air
}
}
+impl Display for Tile {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let char = match self {
+ Self::Wall => '#',
+ Self::Air => '.',
+ Self::Stairs => '>',
+ };
+ f.write_char(char)
+ }
+}
/// The `Floor` type represents the current playing
/// grid of the dungeon. It contains the tiles of the
@@ -194,55 +207,22 @@ impl Floor {
*hash
}
- /// Display the floor as a string for debugging
- ///
- /// # Examples
- /// ```no_run
- /// use dungeon::Floor;
- /// let floor = Floor::generate();
- /// println!("{}", floor.display());
- /// ```
- #[must_use]
- pub fn display(&self) -> String {
- let mut output = String::new();
- for pos in Pos::values() {
- // If it's the player start, show 'P'
- if self.player_start == pos {
- output.push('P');
- continue;
- }
- // Otherwise, show the tile character
- let tile = self.get(pos);
- let char = match tile {
- Tile::Wall => '#',
- Tile::Air => '.',
- Tile::Stairs => '>',
- };
- output.push(char);
- // Newline at the end of each row
- if pos.xy().0 == MAP_SIZE - 1 {
- output.push('\n');
- }
- }
- output
- }
-
/// Returns a random open (no wall) position
pub fn random_pos(&mut self) -> Pos {
loop {
- let x = self.rng.random_range(0..MAP_SIZE);
- let y = self.rng.random_range(0..MAP_SIZE);
- let Some(pos) = Pos::new(x, y) else {
- continue;
- };
- if self.get(pos) != Tile::Air {
+ let pos = self.rand().random();
+ if !self.get(pos).is_walkable() {
continue;
}
break pos;
}
}
-}
+ /// Returns the random number gen for the `Floor`
+ pub fn rand(&mut self) -> &mut impl Rng {
+ &mut self.rng
+ }
+}
impl Default for Floor {
/// Returns a floor with a single set of walls on the map border
fn default() -> Self {
@@ -259,6 +239,34 @@ impl Default for Floor {
Self::new(tiles, player_start, seed)
}
}
+impl Display for Floor {
+ /// Display the floor as a string for debugging
+ ///
+ /// # Examples
+ /// ```no_run
+ /// use dungeon::Floor;
+ /// let floor = Floor::generate();
+ /// println!("{floor}");
+ /// ```
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ for pos in Pos::values() {
+ // If it's the player start, show 'P'
+ if self.player_start == pos {
+ f.write_char('P')?;
+ continue;
+ }
+ // Otherwise, show the tile character
+ let tile = self.get(pos);
+ write!(f, "{tile}")?;
+ // Newline at the end of each row
+ if pos.xy().0 == MAP_SIZE - 1 {
+ writeln!(f)?;
+ }
+ }
+
+ Ok(())
+ }
+}
/// Tests
#[cfg(test)]
@@ -269,8 +277,7 @@ mod tests {
#[test]
fn test_floor_display() {
let floor = Floor::generate();
- let display = floor.display();
// Print the display for visual inspection
- println!("{display}");
+ println!("{floor}");
}
}
diff --git a/dungeon/src/pos.rs b/dungeon/src/pos.rs
index f6c5eb4..0cad387 100644
--- a/dungeon/src/pos.rs
+++ b/dungeon/src/pos.rs
@@ -53,8 +53,15 @@ impl Direction {
Self::iter()
}
- pub fn get_random_dir() -> Self {
- rand::random()
+ /// Returns a random direction provided with a rng
+ #[must_use]
+ pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
+ match rng.random_range(0..4) {
+ 0 => Self::North,
+ 1 => Self::South,
+ 2 => Self::East,
+ _ => Self::West,
+ }
}
}
impl Display for Direction {
@@ -67,16 +74,9 @@ impl Display for Direction {
}
}
}
-
impl Distribution<Direction> for StandardUniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Direction {
- // match rng.gen_range(0, 3) { // rand 0.5, 0.6, 0.7
- match rng.random_range(0..=3) {
- 0 => Direction::North,
- 1 => Direction::East,
- 2 => Direction::South,
- _ => Direction::West,
- }
+ Direction::random(rng)
}
}
@@ -314,7 +314,18 @@ impl Pos {
}
/// Returns the manhattan distance between `self` and `other`
- pub fn manhattan(self, other: Self) -> u16 {
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dungeon::Pos;
+ ///
+ /// let pos1 = Pos::new(3, 6).unwrap();
+ /// let pos2 = Pos::new(5, 1).unwrap();
+ /// assert_eq!(pos1.manhattan(pos2), 7);
+ /// ```
+ #[must_use]
+ pub const fn manhattan(self, other: Self) -> u16 {
let abs_diff = Self::abs_diff(self, other);
abs_diff.0 + abs_diff.1
}
@@ -352,6 +363,14 @@ impl Pos {
pub fn values() -> impl Iterator<Item = Self> {
(0..MAP_SIZE).flat_map(|y| (0..MAP_SIZE).filter_map(move |x| Self::new(x, y)))
}
+
+ /// Returns a random position provided with a rng
+ #[must_use]
+ pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
+ let x = rng.random_range(0..MAP_SIZE);
+ let y = rng.random_range(0..MAP_SIZE);
+ Self(x, y)
+ }
}
impl Default for Pos {
/// Returns a default postion at the origin (0,0)
@@ -375,6 +394,11 @@ impl TryFrom<usize> for Pos {
Self::from_idx(value).ok_or(())
}
}
+impl Distribution<Pos> for StandardUniform {
+ fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Pos {
+ Pos::random(rng)
+ }
+}
/// The `FPos` type represents a floating 2D (temp) position.
///
@@ -512,13 +536,25 @@ impl FPos {
/// ```
///
#[must_use]
- pub fn abs_diff(self, other: Self) -> Self {
+ pub const fn abs_diff(self, other: Self) -> Self {
let x = (self.0 - other.0).abs();
let y = (self.1 - other.1).abs();
Self(x, y)
}
- pub fn manhattan(self, other: Self) -> f32 {
+ /// Returns the manhattan distance between `self` and `other`
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dungeon::FPos;
+ ///
+ /// let fpos1 = FPos::new(1.0, 0.0);
+ /// let fpos2 = FPos::new(1.2, 1.3);
+ /// assert_eq!(fpos1.manhattan(fpos2), 1.5);
+ /// ```
+ #[must_use]
+ pub const fn manhattan(self, other: Self) -> f32 {
let abs_diff = Self::abs_diff(self, other);
abs_diff.0 + abs_diff.1
}