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}