Skip to main content

hexo_engine/
hex.rs

1use crate::types::Coord;
2
3/// Returns all (dq, dr) offsets within hex-distance ≤ `radius` of the origin.
4/// Used for legal move generation and cache updates.
5pub fn hex_offsets(radius: i32) -> Vec<Coord> {
6    let mut offsets = Vec::new();
7    for dq in -radius..=radius {
8        for dr in -radius..=radius {
9            if dq.abs().max(dr.abs()).max((dq + dr).abs()) <= radius {
10                offsets.push((dq, dr));
11            }
12        }
13    }
14    offsets
15}
16
17/// Returns the hex distance between two axial coordinates.
18/// Calculated as `max(|dq|, |dr|, |dq + dr|)`.
19pub fn hex_distance(a: Coord, b: Coord) -> i32 {
20    let dq = (b.0 - a.0).abs();
21    let dr = (b.1 - a.1).abs();
22    let ds = (b.0 - a.0 + b.1 - a.1).abs();
23    dq.max(dr).max(ds)
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29
30    #[test]
31    fn same_point_is_zero() {
32        assert_eq!(hex_distance((0, 0), (0, 0)), 0);
33        assert_eq!(hex_distance((3, -2), (3, -2)), 0);
34    }
35
36    #[test]
37    fn all_six_adjacents_are_distance_one() {
38        let origin = (0, 0);
39        let neighbors = [(1, 0), (-1, 0), (0, 1), (0, -1), (1, -1), (-1, 1)];
40        for n in neighbors {
41            assert_eq!(hex_distance(origin, n), 1, "neighbor {n:?} should be distance 1");
42        }
43    }
44
45    #[test]
46    fn along_axis_distance() {
47        // Moving 5 steps along the q-axis
48        assert_eq!(hex_distance((0, 0), (5, 0)), 5);
49        // Moving 4 steps along the r-axis
50        assert_eq!(hex_distance((0, 0), (0, 4)), 4);
51        // Moving 3 steps along the s-axis (q + r = const)
52        assert_eq!(hex_distance((0, 0), (3, -3)), 3);
53    }
54
55    #[test]
56    fn diagonal_distance() {
57        // (2, 1): dq=2, dr=1, dq+dr=3 → max = 3
58        assert_eq!(hex_distance((0, 0), (2, 1)), 3);
59        // (1, 2): dq=1, dr=2, dq+dr=3 → max = 3
60        assert_eq!(hex_distance((0, 0), (1, 2)), 3);
61    }
62
63    #[test]
64    fn distance_is_symmetric() {
65        assert_eq!(hex_distance((1, 2), (4, -1)), hex_distance((4, -1), (1, 2)));
66        assert_eq!(hex_distance((-3, 5), (2, -2)), hex_distance((2, -2), (-3, 5)));
67    }
68
69    #[test]
70    fn negative_coords() {
71        // (-2, -3) to (1, 1): dq=3, dr=4, dq+dr=7 → max = 7
72        assert_eq!(hex_distance((-2, -3), (1, 1)), 7);
73    }
74
75    #[test]
76    fn boundary_distance_eight() {
77        // (0,0) to (8,0): along q-axis, distance 8
78        assert_eq!(hex_distance((0, 0), (8, 0)), 8);
79    }
80
81    #[test]
82    fn boundary_distance_nine() {
83        // (0,0) to (9,0): along q-axis, distance 9
84        assert_eq!(hex_distance((0, 0), (9, 0)), 9);
85    }
86}