//! 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 { 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 { 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, } } }