1use super::Position;
3
4impl Position {
5 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 pub fn between(self, target: Position, distance_from_target: i32) -> Position {
31 target.towards(self, distance_from_target)
32 }
33
34 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}