screeps/local/position/
approximate_offsets.rs

1//! Methods related to approximating positions between other positions.
2use super::Position;
3
4impl Position {
5    /// Calculates an approximate midpoint between this point and the target.
6    ///
7    /// In case of a tie, rounds towards this point.
8    ///
9    /// If `distance_towards_target` is bigger than the distance to the target,
10    /// the target is returned.
11    pub fn towards(self, target: Position, distance_towards_target: i32) -> Position {
12        let (offset_x, offset_y) = target - self;
13        let total_distance = offset_x.abs().max(offset_y.abs());
14        if distance_towards_target > total_distance {
15            return target;
16        }
17
18        let new_offset_x = (offset_x * distance_towards_target) / total_distance;
19        let new_offset_y = (offset_y * distance_towards_target) / total_distance;
20
21        self + (new_offset_x, new_offset_y)
22    }
23
24    /// Calculates an approximate midpoint between this point and the target.
25    ///
26    /// In case of a tie, rounds towards the target.
27    ///
28    /// If `distance_from_target` is bigger than the distance to the target,
29    /// this position is returned.
30    pub fn between(self, target: Position, distance_from_target: i32) -> Position {
31        target.towards(self, distance_from_target)
32    }
33
34    /// Calculates an approximate midpoint between this point and the target.
35    ///
36    /// In case of a tie, rounds towards the target.
37    pub fn midpoint_between(self, target: Position) -> Position {
38        let (offset_x, offset_y) = self - target;
39
40        let new_offset_x = offset_x / 2;
41        let new_offset_y = offset_y / 2;
42
43        target + (new_offset_x, new_offset_y)
44    }
45}
46
47#[cfg(test)]
48mod test {
49    use super::Position;
50    use crate::RoomName;
51
52    fn test_rooms() -> impl Iterator<Item = RoomName> {
53        ["E0N0", "E20N20", "W20N0", "E20S20", "W20S20"]
54            .iter()
55            .map(|s| s.parse().unwrap())
56    }
57
58    fn pos(room: RoomName, x: u8, y: u8) -> Position {
59        Position::new(x.try_into().unwrap(), y.try_into().unwrap(), room)
60    }
61
62    #[test]
63    fn towards_accurate() {
64        for room in test_rooms() {
65            let start = pos(room, 10, 10);
66            assert_eq!(start.towards(pos(room, 10, 15), 1), pos(room, 10, 11));
67            assert_eq!(start.towards(pos(room, 10, 15), 4), pos(room, 10, 14));
68            assert_eq!(start.towards(pos(room, 10, 15), 10), pos(room, 10, 15));
69            assert_eq!(start.towards(pos(room, 15, 15), 1), pos(room, 11, 11));
70            assert_eq!(start.towards(pos(room, 15, 15), 3), pos(room, 13, 13));
71            assert_eq!(start.towards(pos(room, 15, 20), 2), pos(room, 11, 12));
72            assert_eq!(start.towards(pos(room, 0, 5), 2), pos(room, 8, 9));
73        }
74    }
75    #[test]
76    fn towards_approximate() {
77        for room in test_rooms() {
78            let start = pos(room, 10, 10);
79            assert_eq!(start.towards(pos(room, 15, 20), 1), pos(room, 10, 11));
80            assert_eq!(start.towards(pos(room, 15, 20), 9), pos(room, 14, 19));
81            assert_eq!(start.towards(pos(room, 0, 5), 1), pos(room, 9, 10));
82        }
83    }
84    #[test]
85    fn midpoint_accurate() {
86        for room in test_rooms() {
87            let start = pos(room, 10, 10);
88            assert_eq!(start.midpoint_between(pos(room, 10, 16)), pos(room, 10, 13));
89            assert_eq!(start.midpoint_between(pos(room, 20, 10)), pos(room, 15, 10));
90            assert_eq!(start.midpoint_between(pos(room, 12, 12)), pos(room, 11, 11));
91            assert_eq!(start.midpoint_between(pos(room, 4, 4)), pos(room, 7, 7));
92        }
93    }
94    #[test]
95    fn midpoint_approximate() {
96        for room in test_rooms() {
97            let start = pos(room, 10, 10);
98            assert_eq!(start.midpoint_between(pos(room, 10, 15)), pos(room, 10, 13));
99            assert_eq!(start.midpoint_between(pos(room, 19, 10)), pos(room, 15, 10));
100            assert_eq!(start.midpoint_between(pos(room, 11, 11)), pos(room, 11, 11));
101            assert_eq!(start.midpoint_between(pos(room, 15, 15)), pos(room, 13, 13));
102            assert_eq!(start.midpoint_between(pos(room, 15, 25)), pos(room, 13, 18));
103            assert_eq!(start.midpoint_between(pos(room, 9, 10)), pos(room, 9, 10));
104            assert_eq!(start.midpoint_between(pos(room, 7, 10)), pos(room, 8, 10));
105            assert_eq!(start.midpoint_between(pos(room, 1, 3)), pos(room, 5, 6));
106        }
107    }
108}