summaryrefslogtreecommitdiff
path: root/dungeon/src/pos.rs
blob: bfd994f2f3ed8f291b5ab62c8ff9ae4611a3e59b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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,
		}
	}
}