screeps/local/position/
world_utils.rs1use crate::local::{position::WorldPositionOutOfBoundsError, VALID_WORLD_POSITIONS};
2
3use super::{Position, HALF_WORLD_SIZE};
4
5impl Position {
6 #[inline]
11 pub fn world_x(self) -> i32 {
12 self.room_x() * 50 + (u8::from(self.x()) as i32)
13 }
14
15 #[inline]
20 pub fn world_y(self) -> i32 {
21 self.room_y() * 50 + (u8::from(self.y()) as i32)
22 }
23
24 #[inline]
37 pub fn world_coords(self) -> (i32, i32) {
38 (self.world_x(), self.world_y())
39 }
40
41 #[inline]
52 #[track_caller]
53 pub fn from_world_coords(x: i32, y: i32) -> Self {
54 Self::checked_from_world_coords(x, y).unwrap()
55 }
56
57 #[inline]
65 pub fn checked_from_world_coords(
66 x: i32,
67 y: i32,
68 ) -> Result<Self, WorldPositionOutOfBoundsError> {
69 if VALID_WORLD_POSITIONS.contains(&x) && VALID_WORLD_POSITIONS.contains(&y) {
70 let pos_x = (x + HALF_WORLD_SIZE * 50) as u32;
73 let pos_y = (y + HALF_WORLD_SIZE * 50) as u32;
74 let room_x = pos_x / 50;
75 let room_y = pos_y / 50;
76 let x = (pos_x % 50) as u8;
77 let y = (pos_y % 50) as u8;
78
79 Ok(Self::from_coords_and_world_coords_adjusted(
80 x, y, room_x, room_y,
81 ))
82 } else {
83 Err(WorldPositionOutOfBoundsError(x, y))
84 }
85 }
86}
87
88#[cfg(test)]
89mod test {
90 use super::Position;
91 use crate::{
92 local::{position::WorldPositionOutOfBoundsError, RoomCoordinate},
93 ROOM_SIZE,
94 };
95 use core::ops::Range;
96
97 const TEST_ROOM_NAMES: &[&str] = &[
98 "E1N1", "E20N0", "W0N0", "E0N0", "W0S0", "E0S0", "W0N0", "E0N0", "W0S0", "E0S0", "W50S20",
99 "W127S127", "W127N127", "E127S127", "E127N127",
100 ];
101
102 fn gen_test_coords() -> [RoomCoordinate; 4] {
103 unsafe {
104 [
105 RoomCoordinate::unchecked_new(0),
106 RoomCoordinate::unchecked_new(21),
107 RoomCoordinate::unchecked_new(44),
108 RoomCoordinate::unchecked_new(49),
109 ]
110 }
111 }
112
113 #[test]
114 fn world_coords_round_trip() {
115 for room_name in TEST_ROOM_NAMES {
116 for x in gen_test_coords().iter().cloned() {
117 for y in gen_test_coords().iter().cloned() {
118 let original_pos = Position::new(x, y, room_name.parse().unwrap());
119 let (wx, wy) = original_pos.world_coords();
120 let new = Position::from_world_coords(wx, wy);
121 assert_eq!(original_pos, new);
122 }
123 }
124 }
125 }
126
127 #[test]
128 fn checked_world_coords() {
129 const ROOM_RANGE: Range<i32> = -((ROOM_SIZE as i32) * 2)..((ROOM_SIZE as i32) * 2);
135 for x in ROOM_RANGE {
136 for y in ROOM_RANGE {
137 let room_x = x.div_euclid(50);
138 let room_y = y.div_euclid(50);
139 let pos_x = x.rem_euclid(50) as u8;
140 let pos_y = y.rem_euclid(50) as u8;
141
142 let new_pos = Position::checked_from_world_coords(x, y).unwrap();
143 assert_eq!(room_x, new_pos.room_x());
144 assert_eq!(room_y, new_pos.room_y());
145 assert_eq!(pos_x, new_pos.x().u8());
146 assert_eq!(pos_y, new_pos.y().u8());
147 }
148 }
149
150 const CORNERS: [(i32, i32); 4] =
151 [(-6400, 6399), (6399, 6399), (-6400, -6400), (6399, 6400)];
152 for (corner_x, corner_y) in CORNERS {
153 for x in ROOM_RANGE {
154 for y in ROOM_RANGE {
155 let x = corner_x + x;
156 let y = corner_y + y;
157
158 if !(-6400..=6399).contains(&x) || !(-6400..=6399).contains(&y) {
159 assert_eq!(
160 Err(WorldPositionOutOfBoundsError(x, y)),
161 Position::checked_from_world_coords(x, y)
162 );
163 } else {
164 let room_x = x.div_euclid(50);
165 let room_y = y.div_euclid(50);
166 let pos_x = x.rem_euclid(50) as u8;
167 let pos_y = y.rem_euclid(50) as u8;
168
169 let new_pos = Position::checked_from_world_coords(x, y).unwrap();
170 assert_eq!(room_x, new_pos.room_x());
171 assert_eq!(room_y, new_pos.room_y());
172 assert_eq!(pos_x, new_pos.x().u8());
173 assert_eq!(pos_y, new_pos.y().u8());
174 }
175 }
176 }
177 }
178 }
179
180 #[test]
181 #[should_panic(expected = "WorldPositionOutOfBoundsError(6400, 6400)")]
182 fn oob_world_coords_panic() {
183 let _val = Position::from_world_coords(6400, 6400);
185 }
186
187 #[test]
188 #[should_panic(expected = "WorldPositionOutOfBoundsError(6400, 6399)")]
189 fn oob_coords_add() {
190 let pos = Position::from_world_coords(6395, 6399);
191 let _new_pos = pos + (5, 0);
192 }
193
194 #[cfg(not(debug_assertions))]
196 #[test]
197 fn exhaustive_checked_world_coords() {
198 use crate::local::VALID_WORLD_POSITIONS;
199 for x in i32::MIN..=i32::MAX {
205 for y in i32::MIN..=i32::MAX {
206 if VALID_WORLD_POSITIONS.contains(&x) && VALID_WORLD_POSITIONS.contains(&y) {
207 assert!(matches!(Position::checked_from_world_coords(x, y), Ok(_)));
208 } else {
209 assert_eq!(
210 Err(WorldPositionOutOfBoundsError(x, y)),
211 Position::checked_from_world_coords(x, y)
212 );
213 }
214 }
215 }
216 }
217}