1use std::fmt::{Debug, Display};
2use std::iter::{IntoIterator, Iterator};
3
4use itertools::{self, Itertools};
5
6#[repr(u8)]
20#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub enum Direction {
23 Up = 0,
24 NorthEast = 1,
25 Right = 2,
26 SouthEast = 3,
27 Down = 4,
28 SouthWest = 5,
29 Left = 6,
30 NorthWest = 7,
31}
32
33impl Direction {
34 pub const COUNT: usize = 8;
36
37 pub const ALL: [Direction; Direction::COUNT] = [
39 Direction::Up,
40 Direction::NorthEast,
41 Direction::Right,
42 Direction::SouthEast,
43 Direction::Down,
44 Direction::SouthWest,
45 Direction::Left,
46 Direction::NorthWest,
47 ];
48
49 #[expect(clippy::as_conversions)]
50 pub fn opposite(&self) -> Direction { OPPOSITES[usize::from(*self as u8)] }
51}
52
53impl Display for Direction {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 write!(f, "Direction::{self:?}")
56 }
57}
58
59const OPPOSITES: [Direction; Direction::COUNT] = [
60 Direction::Down,
61 Direction::SouthWest,
62 Direction::Left,
63 Direction::NorthWest,
64 Direction::Up,
65 Direction::NorthEast,
66 Direction::Right,
67 Direction::SouthEast,
68];
69
70const EMPTY: u8 = 0;
71
72const DIRECTIONS_FLAGS: [u8; Direction::COUNT] = [1, 2, 4, 8, 16, 32, 64, 128];
73
74const FLAGS_DIRECTIONS: [Direction; Direction::COUNT] = [
75 Direction::Up,
76 Direction::NorthEast,
77 Direction::Right,
78 Direction::SouthEast,
79 Direction::Down,
80 Direction::SouthWest,
81 Direction::Left,
82 Direction::NorthWest,
83];
84
85#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
114pub struct DirectionsSet(u8);
115
116impl DirectionsSet {
117 pub fn is_empty(&self) -> bool { self.0 == EMPTY }
118
119 #[expect(clippy::as_conversions)]
120 pub fn contains(&self, direction: Direction) -> bool {
121 self.0 & DIRECTIONS_FLAGS[usize::from(direction as u8)] != 0
122 }
123
124 pub fn iter(&self) -> DirectionsSetIter { DirectionsSetIter::new(*self) }
125}
126
127impl Display for DirectionsSet {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 write!(f, "{{{}}}", self.iter().map(|d| format!("{d}")).join(", "))
130 }
131}
132
133impl Default for DirectionsSet {
134 fn default() -> Self { Self(EMPTY) }
135}
136
137#[derive(Debug, Clone, Copy, PartialEq)]
139pub struct DirectionsSetIter {
140 src: u8,
141 current: usize,
142}
143
144impl DirectionsSetIter {
145 fn new(src: DirectionsSet) -> DirectionsSetIter {
146 DirectionsSetIter {
147 src: src.0,
148 current: 0,
149 }
150 }
151}
152
153impl Iterator for DirectionsSetIter {
154 type Item = Direction;
155
156 fn next(&mut self) -> Option<Direction> {
157 while self.current < Direction::COUNT {
158 let idx = self.current;
159 self.current += 1;
160 if self.src & DIRECTIONS_FLAGS[idx] != 0 {
161 return Some(FLAGS_DIRECTIONS[idx]);
162 }
163 }
164 None
165 }
166}
167
168impl IntoIterator for &DirectionsSet {
169 type Item = Direction;
170 type IntoIter = DirectionsSetIter;
171
172 fn into_iter(self) -> Self::IntoIter { DirectionsSetIter::new(*self) }
173}
174
175impl IntoIterator for DirectionsSet {
176 type Item = Direction;
177 type IntoIter = DirectionsSetIter;
178
179 fn into_iter(self) -> Self::IntoIter { DirectionsSetIter::new(self) }
180}
181
182impl<const N: usize> From<&[Direction; N]> for DirectionsSet {
183 fn from(data: &[Direction; N]) -> Self { Self::from_iter(data) }
184}
185
186impl<const N: usize> From<&mut [Direction; N]> for DirectionsSet {
187 fn from(data: &mut [Direction; N]) -> Self { Self::from_iter(data) }
188}
189
190impl<const N: usize> From<[Direction; N]> for DirectionsSet {
191 fn from(data: [Direction; N]) -> Self { Self::from_iter(data) }
192}
193
194impl From<&[Direction]> for DirectionsSet {
195 fn from(data: &[Direction]) -> Self { Self::from_iter(data) }
196}
197
198impl From<&mut [Direction]> for DirectionsSet {
199 fn from(data: &mut [Direction]) -> Self { Self::from_iter(data) }
200}
201
202impl FromIterator<Direction> for DirectionsSet {
203 #[expect(clippy::as_conversions)]
204 fn from_iter<I: IntoIterator<Item = Direction>>(iter: I) -> Self {
205 let mut retv = EMPTY;
206 for d in iter {
207 retv |= DIRECTIONS_FLAGS[usize::from(d as u8)];
208 }
209 Self(retv)
210 }
211}
212
213impl<'a> FromIterator<&'a Direction> for DirectionsSet {
214 fn from_iter<I: IntoIterator<Item = &'a Direction>>(iter: I) -> Self {
215 Self::from_iter(iter.into_iter().copied())
216 }
217}
218
219impl<'a> FromIterator<&'a mut Direction> for DirectionsSet {
220 fn from_iter<I: IntoIterator<Item = &'a mut Direction>>(iter: I) -> Self {
221 Self::from_iter(iter.into_iter().map(|d| *d))
222 }
223}
224
225impl<const N: usize> PartialEq<[Direction; N]> for DirectionsSet {
226 fn eq(&self, other: &[Direction; N]) -> bool { self.0 == DirectionsSet::from(other).0 }
227}
228
229impl<const N: usize> PartialEq<&[Direction; N]> for DirectionsSet {
230 fn eq(&self, other: &&[Direction; N]) -> bool { self.0 == DirectionsSet::from(*other).0 }
231}
232
233impl<const N: usize> PartialEq<&mut [Direction; N]> for DirectionsSet {
234 fn eq(&self, other: &&mut [Direction; N]) -> bool {
235 self.0 == DirectionsSet::from_iter(other.iter()).0
236 }
237}
238
239impl PartialEq<[Direction]> for DirectionsSet {
240 fn eq(&self, other: &[Direction]) -> bool { self.0 == DirectionsSet::from(other).0 }
241}
242
243impl PartialEq<&[Direction]> for DirectionsSet {
244 fn eq(&self, other: &&[Direction]) -> bool { self.0 == DirectionsSet::from(*other).0 }
245}
246
247impl PartialEq<&mut [Direction]> for DirectionsSet {
248 fn eq(&self, other: &&mut [Direction]) -> bool {
249 self.0 == DirectionsSet::from_iter(other.iter()).0
250 }
251}
252
253impl<const N: usize> PartialEq<DirectionsSet> for [Direction; N] {
254 fn eq(&self, other: &DirectionsSet) -> bool { DirectionsSet::from(self).0 == other.0 }
255}
256
257impl<const N: usize> PartialEq<DirectionsSet> for &[Direction; N] {
258 fn eq(&self, other: &DirectionsSet) -> bool {
259 DirectionsSet::from_iter(self.iter()).0 == other.0
260 }
261}
262
263impl<const N: usize> PartialEq<DirectionsSet> for &mut [Direction; N] {
264 fn eq(&self, other: &DirectionsSet) -> bool {
265 DirectionsSet::from_iter(self.iter()).0 == other.0
266 }
267}
268
269impl PartialEq<DirectionsSet> for [Direction] {
270 fn eq(&self, other: &DirectionsSet) -> bool { DirectionsSet::from(self).0 == other.0 }
271}
272
273impl PartialEq<DirectionsSet> for &[Direction] {
274 fn eq(&self, other: &DirectionsSet) -> bool {
275 DirectionsSet::from_iter(self.iter()).0 == other.0
276 }
277}
278
279impl PartialEq<DirectionsSet> for &mut [Direction] {
280 fn eq(&self, other: &DirectionsSet) -> bool {
281 DirectionsSet::from_iter(self.iter()).0 == other.0
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn it_calculates_opposite_direction() {
291 assert_eq!(Direction::Up.opposite(), Direction::Down);
292 assert_eq!(Direction::Down.opposite(), Direction::Up);
293 assert_eq!(Direction::Left.opposite(), Direction::Right);
294 assert_eq!(Direction::Right.opposite(), Direction::Left);
295 assert_eq!(Direction::NorthWest.opposite(), Direction::SouthEast);
296 assert_eq!(Direction::SouthEast.opposite(), Direction::NorthWest);
297 assert_eq!(Direction::NorthEast.opposite(), Direction::SouthWest);
298 assert_eq!(Direction::SouthWest.opposite(), Direction::NorthEast);
299 }
300
301 #[test]
302 fn it_is_empty_by_default() {
303 let neighbors = DirectionsSet::default();
304 assert!(neighbors.is_empty());
305 }
306
307 #[test]
308 fn it_can_be_constructed_from_and_compared_to_slice() {
309 let neighbors: DirectionsSet = [Direction::Up, Direction::Down].into();
310 assert_eq!(neighbors, [Direction::Up, Direction::Down]);
311 }
312
313 #[test]
314 fn test_order_in_source_slice_doesnt_matter() {
315 let neighbors1: DirectionsSet = [Direction::Up, Direction::Down].into();
316 let neighbors2: DirectionsSet = [Direction::Down, Direction::Up].into();
317 assert_eq!(neighbors1, neighbors2);
318 }
319
320 #[test]
321 fn it_can_be_iterated() {
322 let neighbors: DirectionsSet = [Direction::Up, Direction::Down].into();
323 let mut v: Vec<Direction> = Vec::new();
324 for d in neighbors {
325 v.push(d);
326 }
327 assert_eq!(v.len(), 2);
328 assert_eq!(v[0], Direction::Up);
329 assert_eq!(v[1], Direction::Down);
330 }
331
332 #[test]
333 fn it_iterates_in_clockwise_direction() {
334 let neighbors: DirectionsSet =
335 [Direction::NorthWest, Direction::Down, Direction::Up].into();
336 let mut v: Vec<Direction> = Vec::new();
337 for d in neighbors {
338 v.push(d);
339 }
340 assert_eq!(v.len(), 3);
341 assert_eq!(v[0], Direction::Up);
342 assert_eq!(v[1], Direction::Down);
343 assert_eq!(v[2], Direction::NorthWest);
344 }
345}