screeps/local/position/
extra_math.rs

1//! Math utilities on `Position` which don't exist in the Screeps API
2//! proper.
3use std::ops::{Add, Sub};
4
5use super::Position;
6use crate::{constants::Direction, local::position::WorldPositionOutOfBoundsError};
7
8impl Position {
9    /// Returns whether this coordinate represents a room edge position (0 or
10    /// 49)
11    pub fn is_room_edge(self) -> bool {
12        self.xy().is_room_edge()
13    }
14
15    /// Returns a new position offset from this position by the specified x
16    /// coords and y coords.
17    ///
18    /// This function operates on world coordinates, and will wrap between rooms
19    /// if necessary.
20    ///
21    /// To return a new position rather than modifying in place, use `pos + (x,
22    /// y)`. See the implementation of `Add<(i32, i32)>` for
23    /// [`Position`] further down on this page.
24    ///
25    /// # Panics
26    ///
27    /// Will panic if the new position overflows the world. See
28    /// [`Position::from_world_coords`].
29    ///
30    /// # Example
31    ///
32    /// ```
33    /// # use std::convert::TryFrom;
34    /// # use screeps::{Position, RoomCoordinate};
35    /// let e21s21 = "E21S21".parse().unwrap();
36    /// let e21s22 = "E21S22".parse().unwrap();
37    ///
38    /// let mut pos = Position::new(
39    ///     RoomCoordinate::try_from(21).unwrap(),
40    ///     RoomCoordinate::try_from(21).unwrap(),
41    ///     e21s21,
42    /// );
43    /// pos.offset(5, 5);
44    /// assert_eq!(
45    ///     pos,
46    ///     Position::new(
47    ///         RoomCoordinate::try_from(26).unwrap(),
48    ///         RoomCoordinate::try_from(26).unwrap(),
49    ///         e21s21
50    ///     )
51    /// );
52    ///
53    /// pos.offset(0, 49);
54    /// assert_eq!(
55    ///     pos,
56    ///     Position::new(
57    ///         RoomCoordinate::try_from(26).unwrap(),
58    ///         RoomCoordinate::try_from(25).unwrap(),
59    ///         e21s22
60    ///     )
61    /// );
62    /// ```
63    #[inline]
64    #[track_caller]
65    pub fn offset(&mut self, x: i32, y: i32) {
66        *self = *self + (x, y);
67    }
68
69    /// Adds an `(x, y)` pair to this room position's world coordinates and
70    /// returns the result.
71    ///
72    /// Will change rooms if necessary.
73    ///
74    /// # Errors
75    /// Returns `Err` if the new position's room is outside bounds.
76    ///
77    /// For a panicking variant of this function, see [`Position::add`].
78    ///
79    /// See [`Position::from_world_coords`].
80    #[inline]
81    pub fn checked_add(self, rhs: (i32, i32)) -> Result<Position, WorldPositionOutOfBoundsError> {
82        let (x1, y1) = self.world_coords();
83        let (x2, y2) = rhs;
84
85        Position::checked_from_world_coords(x1 + x2, y1 + y2)
86    }
87
88    /// Adds a [`Direction`] to this room position's world coordinates and
89    /// returns the result.
90    ///
91    /// Will change rooms if necessary.
92    ///
93    /// # Errors
94    /// Returns `Err` if the new position's room is outside bounds.
95    ///
96    /// See [`Position::from_world_coords`].
97    #[inline]
98    pub fn checked_add_direction(
99        self,
100        direction: Direction,
101    ) -> Result<Position, WorldPositionOutOfBoundsError> {
102        let (x1, y1) = self.world_coords();
103        let (x2, y2) = direction.into();
104
105        Position::checked_from_world_coords(x1 + x2, y1 + y2)
106    }
107}
108
109impl Add<(i32, i32)> for Position {
110    type Output = Position;
111
112    /// Adds an `(x, y)` pair to this room position's world coordinates.
113    ///
114    /// Will change rooms if necessary.
115    ///
116    /// # Panics
117    ///
118    /// Will panic if the new position's room is outside bounds. See
119    /// [`Position::from_world_coords`].
120    ///
121    /// # Example
122    ///
123    /// ```
124    /// # use std::convert::TryFrom;
125    /// # use screeps::{Position, RoomCoordinate};
126    /// let w5s6 = "W5S6".parse().unwrap();
127    /// let w5s5 = "W5S5".parse().unwrap();
128    ///
129    /// let pos1 = Position::new(
130    ///     RoomCoordinate::try_from(42).unwrap(),
131    ///     RoomCoordinate::try_from(42).unwrap(),
132    ///     w5s6,
133    /// );
134    /// let pos2 = pos1 + (7, 7);
135    /// assert_eq!(
136    ///     pos2,
137    ///     Position::new(
138    ///         RoomCoordinate::try_from(49).unwrap(),
139    ///         RoomCoordinate::try_from(49).unwrap(),
140    ///         w5s6
141    ///     )
142    /// );
143    ///
144    /// let pos3 = pos2 + (0, -59);
145    /// assert_eq!(
146    ///     pos3,
147    ///     Position::new(
148    ///         RoomCoordinate::try_from(49).unwrap(),
149    ///         RoomCoordinate::try_from(40).unwrap(),
150    ///         w5s5
151    ///     )
152    /// );
153    ///
154    /// let pos4 = pos3 - (49, 0);
155    /// assert_eq!(
156    ///     pos4,
157    ///     Position::new(
158    ///         RoomCoordinate::try_from(0).unwrap(),
159    ///         RoomCoordinate::try_from(40).unwrap(),
160    ///         w5s5
161    ///     )
162    /// );
163    /// ```
164    #[inline]
165    #[track_caller]
166    fn add(self, (x, y): (i32, i32)) -> Self {
167        self.checked_add((x, y)).unwrap()
168    }
169}
170
171impl Add<Direction> for Position {
172    type Output = Position;
173    #[inline]
174    #[track_caller]
175    fn add(self, direction: Direction) -> Self {
176        self.checked_add_direction(direction).unwrap()
177    }
178}
179
180impl Sub<(i32, i32)> for Position {
181    type Output = Position;
182
183    /// See the implementation of `Add<(i32, i32)>` for [`Position`].
184    #[inline]
185    #[track_caller]
186    fn sub(self, (x, y): (i32, i32)) -> Self {
187        self.checked_add((-x, -y)).unwrap()
188    }
189}
190
191impl Sub<Direction> for Position {
192    type Output = Position;
193    #[inline]
194    fn sub(self, direction: Direction) -> Self {
195        self.checked_add_direction(-direction).unwrap()
196    }
197}
198
199impl Sub<Position> for Position {
200    type Output = (i32, i32);
201
202    /// Subtracts the other room position from this one, extracting the
203    /// difference as the output.
204    ///
205    /// # Example
206    ///
207    /// ```
208    /// # use std::convert::TryFrom;
209    /// # use screeps::{Position, RoomCoordinate};
210    /// let e5n5 = "E5N5".parse().unwrap();
211    /// let e5n6 = "E5N6".parse().unwrap();
212    ///
213    /// let pos1 = Position::new(
214    ///     RoomCoordinate::try_from(40).unwrap(),
215    ///     RoomCoordinate::try_from(40).unwrap(),
216    ///     e5n5,
217    /// );
218    /// let pos2 = Position::new(
219    ///     RoomCoordinate::try_from(0).unwrap(),
220    ///     RoomCoordinate::try_from(20).unwrap(),
221    ///     e5n6,
222    /// );
223    /// assert_eq!(pos1 - pos2, (40, 70));
224    /// ```
225    #[inline]
226    fn sub(self, other: Position) -> (i32, i32) {
227        let (mx, my) = self.world_coords();
228        let (ox, oy) = other.world_coords();
229        (mx - ox, my - oy)
230    }
231}