projective_grid/hex/
direction.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum HexDirection {
10 East,
11 West,
12 NorthEast,
13 SouthWest,
14 NorthWest,
15 SouthEast,
16}
17
18impl HexDirection {
19 pub const ALL: [HexDirection; 6] = [
21 Self::East,
22 Self::SouthEast,
23 Self::SouthWest,
24 Self::West,
25 Self::NorthWest,
26 Self::NorthEast,
27 ];
28
29 pub fn opposite(self) -> Self {
31 match self {
32 Self::East => Self::West,
33 Self::West => Self::East,
34 Self::NorthEast => Self::SouthWest,
35 Self::SouthWest => Self::NorthEast,
36 Self::NorthWest => Self::SouthEast,
37 Self::SouthEast => Self::NorthWest,
38 }
39 }
40
41 pub fn delta(self) -> (i32, i32) {
45 match self {
46 Self::East => (1, 0),
47 Self::West => (-1, 0),
48 Self::NorthEast => (1, -1),
49 Self::SouthWest => (-1, 1),
50 Self::NorthWest => (0, -1),
51 Self::SouthEast => (0, 1),
52 }
53 }
54
55 pub fn rotate_cw_60(self) -> Self {
57 match self {
58 Self::East => Self::SouthEast,
59 Self::SouthEast => Self::SouthWest,
60 Self::SouthWest => Self::West,
61 Self::West => Self::NorthWest,
62 Self::NorthWest => Self::NorthEast,
63 Self::NorthEast => Self::East,
64 }
65 }
66
67 pub fn axis_index(self) -> usize {
69 match self {
70 Self::East | Self::West => 0,
71 Self::NorthEast | Self::SouthWest => 1,
72 Self::NorthWest | Self::SouthEast => 2,
73 }
74 }
75
76 pub(crate) fn slot_index(self) -> usize {
78 match self {
79 Self::East => 0,
80 Self::West => 1,
81 Self::NorthEast => 2,
82 Self::SouthWest => 3,
83 Self::NorthWest => 4,
84 Self::SouthEast => 5,
85 }
86 }
87}
88
89#[derive(Debug)]
91pub struct HexNodeNeighbor {
92 pub direction: HexDirection,
94 pub index: usize,
96 pub distance: f32,
98 pub score: f32,
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn opposite_round_trips() {
108 for &d in &HexDirection::ALL {
109 assert_eq!(d.opposite().opposite(), d);
110 }
111 }
112
113 #[test]
114 fn opposite_deltas_sum_to_zero() {
115 for &d in &HexDirection::ALL {
116 let (dq1, dr1) = d.delta();
117 let (dq2, dr2) = d.opposite().delta();
118 assert_eq!(dq1 + dq2, 0);
119 assert_eq!(dr1 + dr2, 0);
120 }
121 }
122
123 #[test]
124 fn rotate_cw_60_cycles_all_six() {
125 let mut d = HexDirection::East;
126 let mut visited = Vec::new();
127 for _ in 0..6 {
128 visited.push(d);
129 d = d.rotate_cw_60();
130 }
131 assert_eq!(d, HexDirection::East);
132 for i in 0..6 {
134 for j in (i + 1)..6 {
135 assert_ne!(visited[i], visited[j]);
136 }
137 }
138 }
139
140 #[test]
141 fn axis_index_groups_opposites() {
142 for &d in &HexDirection::ALL {
143 assert_eq!(d.axis_index(), d.opposite().axis_index());
144 }
145 let axes: std::collections::HashSet<usize> =
147 HexDirection::ALL.iter().map(|d| d.axis_index()).collect();
148 assert_eq!(axes.len(), 3);
149 }
150
151 #[test]
152 fn slot_indices_are_unique() {
153 let slots: Vec<usize> = HexDirection::ALL.iter().map(|d| d.slot_index()).collect();
154 let set: std::collections::HashSet<usize> = slots.iter().copied().collect();
155 assert_eq!(set.len(), 6);
156 }
157}