summaryrefslogtreecommitdiff
path: root/dungeon
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-10-07 11:01:19 -0400
committerFreya Murphy <freya@freyacat.org>2025-10-08 19:37:15 -0400
commitb11f074ceba10af62b35b414ecaa51a8f13c6550 (patch)
treed12979f7eeb384f94b145ea763a97dcde353745b /dungeon
parentInitial commit (diff)
downloadDungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.tar.gz
DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.tar.bz2
DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.zip
initial baseline
Diffstat (limited to 'dungeon')
-rw-r--r--dungeon/Cargo.lock157
-rw-r--r--dungeon/Cargo.toml10
-rw-r--r--dungeon/src/lib.rs58
-rw-r--r--dungeon/src/map.rs189
-rw-r--r--dungeon/src/pos.rs122
5 files changed, 536 insertions, 0 deletions
diff --git a/dungeon/Cargo.lock b/dungeon/Cargo.lock
new file mode 100644
index 0000000..b9a6727
--- /dev/null
+++ b/dungeon/Cargo.lock
@@ -0,0 +1,157 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "cfg-if"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
+
+[[package]]
+name = "dungeon"
+version = "0.1.0"
+dependencies = [
+ "rand",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.176"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
+
+[[package]]
+name = "wasi"
+version = "0.14.7+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
+dependencies = [
+ "wasip2",
+]
+
+[[package]]
+name = "wasip2"
+version = "1.0.1+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+
+[[package]]
+name = "zerocopy"
+version = "0.8.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/dungeon/Cargo.toml b/dungeon/Cargo.toml
new file mode 100644
index 0000000..5548248
--- /dev/null
+++ b/dungeon/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "dungeon"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+rand = "0.9"
+
+[lints]
+workspace = true
diff --git a/dungeon/src/lib.rs b/dungeon/src/lib.rs
new file mode 100644
index 0000000..66faec3
--- /dev/null
+++ b/dungeon/src/lib.rs
@@ -0,0 +1,58 @@
+//! The `dungon` crate contains the core functionality for
+//! interacting with a `Dungeon` and its components.
+
+mod map;
+mod pos;
+
+pub use map::*;
+pub use pos::*;
+
+/// The `Dungeon` type represents the game state of the
+/// dungeon crawler.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Dungeon {
+ player: Entity,
+ floor: Floor,
+}
+impl Dungeon {
+ /// Creates a new `Dungeon`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use dungeon::Dungeon;
+ ///
+ /// let dungeon = Dungeon::new();
+ /// ```
+ #[must_use]
+ pub fn new() -> Self {
+ let floor = Floor::generate();
+ let player = Entity::player(floor.player_start());
+
+ Self { player, floor }
+ }
+
+ /// Creates a new `Dungeon` with a provided seed.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use dungeon::Dungeon;
+ ///
+ /// let seed = 234690523482u64;
+ /// let dungeon = Dungeon::new_seeded(seed);
+ /// ```
+ #[must_use]
+ pub fn new_seeded(seed: u64) -> Self {
+ let floor = Floor::generate_seeded(seed);
+ let player = Entity::player(floor.player_start());
+
+ Self { player, floor }
+ }
+}
+
+impl Default for Dungeon {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/dungeon/src/map.rs b/dungeon/src/map.rs
new file mode 100644
index 0000000..8c4328c
--- /dev/null
+++ b/dungeon/src/map.rs
@@ -0,0 +1,189 @@
+//! The `map` module contains structures of the dungeon game map
+//! including the current `Floor`, map `Tile`, and `Entity`.
+
+use crate::pos::{Direction, Pos};
+
+/// `MAP_SIZE` is the size of the size of the dungeon grid.
+pub const MAP_SIZE: u16 = 100;
+
+/// The number of tiles in the dungeon grid
+pub const TILE_COUNT: usize = MAP_SIZE as usize * MAP_SIZE as usize;
+
+/// The `EntityKind` represents what kind of entity this is.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum EntityKind {
+ Player,
+}
+
+/// The `Entity` kind represents the main player, or any other
+/// ai autonomous character that can move freely across the
+/// dungeon.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct Entity {
+ pos: Pos,
+ dir: Direction,
+ kind: EntityKind,
+}
+impl Entity {
+ /// Creates a new `Entity` at a given `Pos`, `Direction`, and `EntityKind`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dungeon::{Pos, Direction, Entity, EntityKind};
+ ///
+ /// let pos = Pos::new(0, 0).unwrap();
+ /// let dir = Direction::North;
+ /// let kind = EntityKind::Player;
+ /// let entity = Entity::new(pos, dir, kind);
+ /// ```
+ #[must_use]
+ pub const fn new(pos: Pos, dir: Direction, kind: EntityKind) -> Self {
+ Self { pos, dir, kind }
+ }
+
+ /// Creates the Player version of the `Entity`
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dungeon::{Pos, Entity};
+ ///
+ /// let pos = Pos::new(0, 0).unwrap();
+ /// let player = Entity::player(pos);
+ /// ```
+ #[must_use]
+ pub const fn player(pos: Pos) -> Self {
+ let dir = Direction::East;
+ let kind = EntityKind::Player;
+ Self::new(pos, dir, kind)
+ }
+
+ /// Returns the `Pos` of the entity
+ #[must_use]
+ pub const fn pos(&self) -> Pos {
+ self.pos
+ }
+
+ /// Returns a mutable referense to the `Pos` of the entity
+ #[must_use]
+ pub const fn pos_mut(&mut self) -> &mut Pos {
+ &mut self.pos
+ }
+
+ /// Returns the `Direction` of the entity
+ #[must_use]
+ pub const fn dir(&self) -> Direction {
+ self.dir
+ }
+
+ /// Returns a mutable referense to the `Direction` of the entity
+ #[must_use]
+ pub const fn dir_mut(&mut self) -> &mut Direction {
+ &mut self.dir
+ }
+
+ /// Returns the `EntityKind` of this entity
+ #[must_use]
+ pub const fn kind(&self) -> EntityKind {
+ self.kind
+ }
+}
+
+/// The `Tile` enum represents what is (or is not) at
+/// any given spot in the dungeon grid.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum Tile {
+ /// `Wall` represents an impassible wall
+ Wall,
+ /// `Air` represents empty walkable space
+ Air,
+}
+
+/// The `Floor` type represents the current playing
+/// grid of the dungeon. It contains the tiles of the
+/// grid, and the starting position of the player.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Floor {
+ /// The dungeon grid
+ tiles: Box<[Tile; TILE_COUNT]>,
+ /// The position the player starts at
+ player_start: Pos,
+ /// The seed used when generating the dungeon grid
+ seed: u64,
+}
+impl Floor {
+ /// Generates a dungeon `Floor` using wave function collapse.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use dungeon::Floor;
+ ///
+ /// let floor = Floor::generate();
+ /// ```
+ #[must_use]
+ pub fn generate() -> Self {
+ let seed = rand::random();
+ Self::generate_seeded(seed)
+ }
+
+ /// Genreates a dungeon `Floor` using wave function collapse provided with a seed.
+ ///
+ /// The provided seed is used for randomness in the wave function
+ /// collapse algorithm.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use dungeon::Floor;
+ ///
+ /// /// here is our very seedy seed
+ /// let seed = 2893249402u64;
+ /// let floor = Floor::generate_seeded(seed);
+ /// ```
+ #[must_use]
+ pub fn generate_seeded(_seed: u64) -> Self {
+ unimplemented!()
+ }
+
+ /// Returns the start position of the player
+ #[must_use]
+ pub const fn player_start(&self) -> Pos {
+ self.player_start
+ }
+
+ /// Returns the seed used to generate the map
+ #[must_use]
+ pub const fn seed(&self) -> u64 {
+ self.seed
+ }
+
+ /// Returns a `Tile` on the dungeon grid at `Pos`.
+ #[must_use]
+ pub const fn get(&self, pos: Pos) -> Tile {
+ let idx = pos.idx();
+ self.tiles[idx]
+ }
+
+ /// Returns a multable reference to a `Tile` on the dungeon grid at `Pos`.
+ #[must_use]
+ pub const fn get_mut(&mut self, pos: Pos) -> &mut Tile {
+ let idx = pos.idx();
+ &mut self.tiles[idx]
+ }
+
+ /// Returns a reference to all tiles in the `Floor`.
+ /// The size of this lise will always be `TILE_COUNT` long.
+ #[must_use]
+ pub const fn tiles(&self) -> &[Tile] {
+ &*self.tiles
+ }
+
+ /// Returns a mutable reference to all tiles in the `Floor`.
+ /// The size of this lise will always be `TILE_COUNT` long.
+ #[must_use]
+ pub const fn tiles_mut(&mut self) -> &mut [Tile] {
+ &mut *self.tiles
+ }
+}
diff --git a/dungeon/src/pos.rs b/dungeon/src/pos.rs
new file mode 100644
index 0000000..bfd994f
--- /dev/null
+++ b/dungeon/src/pos.rs
@@ -0,0 +1,122 @@
+//! The `pos` module contains structures for representation an
+//! entity or objects position and facing direction inside the
+//! dungeon grid.
+
+use crate::map::MAP_SIZE;
+
+/// The `Direction` type represents a direction an entity
+/// or any position object is facing inside the dungeon map.
+/// Since the dungeon lives on a grid, there are only four
+/// possible directions.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum Direction {
+ North,
+ South,
+ East,
+ West,
+}
+
+/// The `Pos` type represents a 2D position inside the dungeon grid.
+///
+/// The max size for the dungeon map is set by the `MAP_SIZE` constant
+/// and therefore the x and y positions can be between 0 and `MAP_SIZE - 1`.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Pos(u16, u16);
+impl Pos {
+ /// Creates a new position from a given x and y position.
+ ///
+ /// Returns `None` if the position goes out of the map.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dungeon::Pos;
+ ///
+ /// let pos = Pos::new(0,0);
+ /// assert!(pos.is_some());
+ /// ```
+ ///
+ /// ```
+ /// use dungeon::{Pos, MAP_SIZE};
+ ///
+ /// let pos = Pos::new(MAP_SIZE, MAP_SIZE);
+ /// assert!(pos.is_none())
+ /// ```
+ #[must_use]
+ pub const fn new(x: u16, y: u16) -> Option<Self> {
+ if x >= MAP_SIZE || y >= MAP_SIZE {
+ None
+ } else {
+ Some(Self(x, y))
+ }
+ }
+
+ /// Returns the x and y positions of `Pos`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dungeon::Pos;
+ ///
+ /// let pos = Pos::new(5,7).unwrap();
+ /// let (x,y) = pos.xy();
+ /// assert_eq!(x, 5);
+ /// assert_eq!(y, 7);
+ /// ```
+ #[must_use]
+ pub const fn xy(self) -> (u16, u16) {
+ (self.0, self.1)
+ }
+
+ /// Converts the x and y positions into an index of a continous list.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dungeon::{Pos, MAP_SIZE};
+ ///
+ /// let pos = Pos::new(1,2).unwrap();
+ /// let idx = pos.idx();
+ /// assert_eq!(idx, 1 + 2 * MAP_SIZE as usize);
+ /// ```
+ #[must_use]
+ pub const fn idx(self) -> usize {
+ let (x, y) = self.xy();
+ let idx = x + y * MAP_SIZE;
+ idx as usize
+ }
+
+ /// Steps `Pos` one space in the `Direction` `dir`.
+ ///
+ /// Returns `None` if the position goes out of the map.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use dungeon::{Direction, Pos};
+ ///
+ /// let pos = Pos::new(0, 1).unwrap();
+ /// let new_pos = pos.step(Direction::North);
+ /// assert_eq!(new_pos, Pos::new(0, 0));
+ /// ```
+ ///
+ /// ```
+ /// use dungeon::{Direction, Pos};
+ ///
+ /// let pos = Pos::new(0, 1).unwrap();
+ /// let new_pos = pos.step(Direction::West);
+ /// assert!(new_pos.is_none());
+ /// ```
+ #[must_use]
+ pub const fn step(self, dir: Direction) -> Option<Self> {
+ use Direction as D;
+ let (x, y) = self.xy();
+ match dir {
+ D::North if y > 0 => Self::new(x, y - 1),
+ D::South => Self::new(x, y + 1),
+ D::East => Self::new(x + 1, y),
+ D::West if x > 0 => Self::new(x - 1, y),
+ _ => None,
+ }
+ }
+}