Skip to main content

aoc_core/
dir.rs

1use std::ops::Index;
2
3/// Cardinal directions enum.
4#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
5pub enum Dir {
6    /// South.
7    S,
8
9    /// East
10    E,
11
12    /// North
13    N,
14
15    /// West
16    W,
17}
18
19/// Internal macro for Dir functions match implementation.
20macro_rules! match_enum_transform {
21    ($match:expr, $enum:ident, [$($v1:ident : $v2:ident),+]) => {
22        match $match {
23            $($enum::$v1 => $enum::$v2,)+
24        }
25    };
26}
27
28impl Dir {
29    /// Returns the opposite direction.
30    #[inline]
31    #[must_use]
32    pub const fn opposite(self) -> Self {
33        match_enum_transform!(self, Dir, [S:N, E:W, N:S, W:E])
34    }
35
36    /// Changes the direction to its opposite.
37    #[inline]
38    pub const fn opposite_mut(&mut self) {
39        *self = self.opposite();
40    }
41
42    /// Returns the next direction in counter-clockwise rotation.
43    #[inline]
44    #[must_use]
45    pub const fn rotate_ccw(self) -> Self {
46        match_enum_transform!(self, Dir, [S:E, E:N, N:W, W:S])
47    }
48
49    /// Changes the direction to the next one in counter-clockwise rotation.
50    #[inline]
51    pub const fn rotate_ccw_mut(&mut self) {
52        *self = self.rotate_ccw();
53    }
54
55    /// Returns the next direction in clockwise rotation.
56    #[inline]
57    #[must_use]
58    pub const fn rotate_cw(self) -> Self {
59        match_enum_transform!(self, Dir, [S:W, E:S, N:E, W:N])
60    }
61
62    /// Changes the direction to the next one in clockwise rotation.
63    #[inline]
64    pub const fn rotate_cw_mut(&mut self) {
65        *self = self.rotate_cw();
66    }
67}
68
69// ------------------------------------------------------------------------------------------------
70// Trait implementations
71
72impl From<usize> for Dir {
73    #[inline]
74    fn from(value: usize) -> Self {
75        match value {
76            0 => Self::S,
77            1 => Self::E,
78            2 => Self::N,
79            3 => Self::W,
80            _ => unreachable!("Invalid direction value"),
81        }
82    }
83}
84
85impl From<Dir> for usize {
86    #[inline]
87    fn from(value: Dir) -> Self {
88        value as Self
89    }
90}
91
92impl<T> Index<Dir> for [T] {
93    type Output = T;
94
95    #[inline]
96    fn index(&self, index: Dir) -> &Self::Output {
97        &self[index as usize]
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::Dir::{self, *};
104
105    macro_rules! assert_dir {
106        ([$($in:ident),*], [$($ex:ident),*], $func:ident) => {
107            let input = [$($in),*];
108            let expected = [$($ex),*];
109            let output = input.map(Dir::$func);
110            assert_eq!(expected, output, "\n input: {input:?}");
111        };
112    }
113
114    macro_rules! assert_dir_mut {
115        ([$($in:ident),*], [$($ex:ident),*], $func:ident) => {
116            let input = [$($in),*];
117            let expected = [$($ex),*];
118            let mut output = input;
119            for dir in &mut output {
120                dir.$func();
121            }
122            assert_eq!(expected, output, "\n input: {input:?}");
123        };
124    }
125
126    // ------------------------------------------------------------------------------------------------
127    // Opposite
128
129    #[test]
130    fn opposite() {
131        assert_dir!([S, E, N, W], [N, W, S, E], opposite);
132    }
133
134    #[test]
135    fn opposite_mut() {
136        assert_dir_mut!([S, E, N, W], [N, W, S, E], opposite_mut);
137    }
138
139    // ------------------------------------------------------------------------------------------------
140    // Rotation
141
142    #[test]
143    fn rotate_ccw() {
144        assert_dir!([S, E, N, W], [E, N, W, S], rotate_ccw);
145    }
146
147    #[test]
148    fn rotate_ccw_mut() {
149        assert_dir_mut!([S, E, N, W], [E, N, W, S], rotate_ccw_mut);
150    }
151
152    #[test]
153    fn rotate_cw() {
154        assert_dir!([S, E, N, W], [W, S, E, N], rotate_cw);
155    }
156
157    #[test]
158    fn rotate_cw_mut() {
159        assert_dir_mut!([S, E, N, W], [W, S, E, N], rotate_cw_mut);
160    }
161
162    // ------------------------------------------------------------------------------------------------
163    // From
164
165    #[test]
166    fn from_usize() {
167        let input = [0, 1, 2, 3];
168        let expected = [S, E, N, W];
169        let output = input.map(Dir::from);
170        assert_eq!(expected, output, "\n input: {input:?}");
171    }
172
173    #[test]
174    #[should_panic = "Invalid direction"]
175    fn from_usize_panic() {
176        let _ = Dir::from(4);
177    }
178
179    #[test]
180    fn into_usize() {
181        let input = [S, E, N, W];
182        let expected = [0, 1, 2, 3];
183        let output = input.map(usize::from);
184        assert_eq!(expected, output, "\n input: {input:?}");
185    }
186
187    // ------------------------------------------------------------------------------------------------
188    // Index
189
190    #[test]
191    fn index_array() {
192        let input = ([0, 1, 2, 3], [S, E, N, W]);
193        let expected = [0, 1, 2, 3];
194        let output = input.1.map(|dir| input.0[dir]);
195        assert_eq!(expected, output, "\n input: {input:?}");
196    }
197}