Skip to content

Commit c5f0e23

Browse files
authored
Merge pull request #243 from rustyscreeps/approximate-offset-position-methods
Some Position utility methods for approximate offsets
2 parents f6d9532 + 7f9f0d3 commit c5f0e23

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed

src/local/room_position.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::{
1010

1111
use super::{RoomName, HALF_WORLD_SIZE};
1212

13+
mod approximate_offsets;
1314
mod extra_math;
1415
mod game_math;
1516
mod game_methods;
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
//! Methods related to approximating positions between other positions.
2+
use crate::objects::HasPosition;
3+
4+
use super::Position;
5+
6+
impl Position {
7+
/// Calculates an approximate midpoint between this point and the target.
8+
///
9+
/// In case of a tie, rounds towards this point.
10+
///
11+
/// If `distance_towards_target` is bigger than the distance to the target,
12+
/// the target is returned.
13+
pub fn towards<T>(self, target: &T, distance_towards_target: i32) -> Position
14+
where
15+
T: ?Sized + HasPosition,
16+
{
17+
let target = target.pos();
18+
19+
let (offset_x, offset_y) = target - self;
20+
let total_distance = offset_x.abs().max(offset_y.abs());
21+
if distance_towards_target > total_distance {
22+
return target;
23+
}
24+
25+
let new_offset_x = (offset_x * distance_towards_target) / total_distance;
26+
let new_offset_y = (offset_y * distance_towards_target) / total_distance;
27+
28+
self + (new_offset_x, new_offset_y)
29+
}
30+
31+
/// Calculates an approximate midpoint between this point and the target.
32+
///
33+
/// In case of a tie, rounds towards the target.
34+
///
35+
/// If `distance_from_target` is bigger than the distance to the target,
36+
/// this position is returned.
37+
pub fn between<T>(self, target: &T, distance_from_target: i32) -> Position
38+
where
39+
T: ?Sized + HasPosition,
40+
{
41+
target.pos().towards(&self, distance_from_target)
42+
}
43+
44+
/// Calculates an approximate midpoint between this point and the target.
45+
///
46+
/// In case of a tie, rounds towards the target.
47+
pub fn midpoint_between<T>(self, target: &T) -> Position
48+
where
49+
T: ?Sized + HasPosition,
50+
{
51+
let target = target.pos();
52+
53+
let (offset_x, offset_y) = self - target;
54+
55+
let new_offset_x = offset_x / 2;
56+
let new_offset_y = offset_y / 2;
57+
58+
target + (new_offset_x, new_offset_y)
59+
}
60+
}
61+
62+
#[cfg(test)]
63+
mod test {
64+
use super::Position;
65+
use crate::RoomName;
66+
67+
fn test_rooms() -> impl Iterator<Item = RoomName> {
68+
["E0N0", "E20N20", "W20N0", "E20S20", "W20S20"]
69+
.iter()
70+
.map(|s| s.parse().unwrap())
71+
}
72+
73+
fn pos(room: RoomName, x: u32, y: u32) -> Position {
74+
Position::new(x, y, room)
75+
}
76+
77+
#[test]
78+
fn towards_accurate() {
79+
for room in test_rooms() {
80+
let start = pos(room, 10, 10);
81+
assert_eq!(start.towards(&pos(room, 10, 15), 1), pos(room, 10, 11));
82+
assert_eq!(start.towards(&pos(room, 10, 15), 4), pos(room, 10, 14));
83+
assert_eq!(start.towards(&pos(room, 10, 15), 10), pos(room, 10, 15));
84+
assert_eq!(start.towards(&pos(room, 15, 15), 1), pos(room, 11, 11));
85+
assert_eq!(start.towards(&pos(room, 15, 15), 3), pos(room, 13, 13));
86+
assert_eq!(start.towards(&pos(room, 15, 20), 2), pos(room, 11, 12));
87+
assert_eq!(start.towards(&pos(room, 0, 5), 2), pos(room, 8, 9));
88+
}
89+
}
90+
#[test]
91+
fn towards_approximate() {
92+
for room in test_rooms() {
93+
let start = pos(room, 10, 10);
94+
assert_eq!(start.towards(&pos(room, 15, 20), 1), pos(room, 10, 11));
95+
assert_eq!(start.towards(&pos(room, 15, 20), 9), pos(room, 14, 19));
96+
assert_eq!(start.towards(&pos(room, 0, 5), 1), pos(room, 9, 10));
97+
}
98+
}
99+
#[test]
100+
fn midpoint_accurate() {
101+
for room in test_rooms() {
102+
let start = pos(room, 10, 10);
103+
assert_eq!(
104+
start.midpoint_between(&pos(room, 10, 16)),
105+
pos(room, 10, 13)
106+
);
107+
assert_eq!(
108+
start.midpoint_between(&pos(room, 20, 10)),
109+
pos(room, 15, 10)
110+
);
111+
assert_eq!(
112+
start.midpoint_between(&pos(room, 12, 12)),
113+
pos(room, 11, 11)
114+
);
115+
assert_eq!(start.midpoint_between(&pos(room, 4, 4)), pos(room, 7, 7));
116+
}
117+
}
118+
#[test]
119+
fn midpoint_approximate() {
120+
for room in test_rooms() {
121+
let start = pos(room, 10, 10);
122+
assert_eq!(
123+
start.midpoint_between(&pos(room, 10, 15)),
124+
pos(room, 10, 13)
125+
);
126+
assert_eq!(
127+
start.midpoint_between(&pos(room, 19, 10)),
128+
pos(room, 15, 10)
129+
);
130+
assert_eq!(
131+
start.midpoint_between(&pos(room, 11, 11)),
132+
pos(room, 11, 11)
133+
);
134+
assert_eq!(
135+
start.midpoint_between(&pos(room, 15, 15)),
136+
pos(room, 13, 13)
137+
);
138+
assert_eq!(
139+
start.midpoint_between(&pos(room, 15, 25)),
140+
pos(room, 13, 18)
141+
);
142+
assert_eq!(start.midpoint_between(&pos(room, 9, 10)), pos(room, 9, 10));
143+
assert_eq!(start.midpoint_between(&pos(room, 7, 10)), pos(room, 8, 10));
144+
assert_eq!(start.midpoint_between(&pos(room, 1, 3)), pos(room, 5, 6));
145+
}
146+
}
147+
}

0 commit comments

Comments
 (0)