aoclib_rs/
dir.rs

1use std::iter;
2
3use crate::usize_plus_i;
4
5pub trait Direction: Sized + PartialEq + Copy {
6    /// Returns an iterator through all values of the `Direction`, starting with `Up` and
7    /// proceeding clockwise.
8    fn iter() -> impl Iterator<Item = Self>;
9
10    /// Returns an iterator through all values of the `Direction`, starting with the given
11    /// `initial_dir` and proceeding clockwise.
12    ///
13    /// ```
14    /// use aoclib_rs::dir::{Dir4, Dir8, Direction};
15    /// assert_eq!(
16    ///     Dir4::iter_from(Dir4::Left).collect::<Vec<_>>(),
17    ///     [Dir4::Left, Dir4::Up, Dir4::Right, Dir4::Down]
18    /// );
19    /// assert_eq!(
20    ///     Dir8::iter_from(Dir8::Dir4(Dir4::Left)).collect::<Vec<_>>(),
21    ///     [
22    ///         Dir8::Dir4(Dir4::Left),
23    ///         Dir8::UpLeft,
24    ///         Dir8::Dir4(Dir4::Up),
25    ///         Dir8::UpRight,
26    ///         Dir8::Dir4(Dir4::Right),
27    ///         Dir8::DownRight,
28    ///         Dir8::Dir4(Dir4::Down),
29    ///         Dir8::DownLeft
30    ///     ]
31    /// );
32    /// ```
33    fn iter_from(initial_dir: Self) -> impl Iterator<Item = Self> {
34        let mut d = initial_dir;
35        let mut first = true;
36        iter::from_fn(move || {
37            if d == initial_dir && !first {
38                return None;
39            }
40            first = false;
41
42            let r = d;
43            d = d.rotate_right();
44
45            Some(r)
46        })
47    }
48
49    /// Similar to `apply_delta_to_usizes()`, but iterates through all valid directions. A valid
50    /// direction is defined as any whose result is non-negative and is less than `size` in the
51    /// respective dimension.
52    ///
53    /// ```
54    /// use aoclib_rs::dir::{Dir8, Direction};
55    /// assert_eq!(
56    ///     Dir8::iter_valid_usizes_deltas((0, 5), (4, 6)).collect::<Vec<_>>(),
57    ///     [(0, 4), (1, 4), (1, 5)]
58    /// );
59    /// ```
60    fn iter_valid_usizes_deltas(
61        curr: (usize, usize),
62        size: (usize, usize),
63    ) -> impl Iterator<Item = (usize, usize)> {
64        let mut dir_iter = Self::iter();
65        iter::from_fn(move || {
66            loop {
67                match dir_iter.next() {
68                    None => return None,
69                    Some(d) => {
70                        let (dx, dy) = d.delta();
71                        let next = (
72                            i64::try_from(curr.0).unwrap() + i64::from(dx),
73                            i64::try_from(curr.1).unwrap() + i64::from(dy),
74                        );
75                        if next.0 >= 0
76                            && next.0 < i64::try_from(size.0).unwrap()
77                            && next.1 >= 0
78                            && next.1 < i64::try_from(size.1).unwrap()
79                        {
80                            return Some((
81                                usize::try_from(next.0).unwrap(),
82                                usize::try_from(next.1).unwrap(),
83                            ));
84                        }
85                    }
86                }
87            }
88        })
89    }
90
91    /// Returns the `(x, y)` delta for one unit of the `Direction`.
92    fn delta(self) -> (i8, i8);
93
94    /// Returns a new `Direction` which represents the given one being rotated "right" or clockwise by one unit.
95    fn rotate_right(self) -> Self;
96
97    /// Returns a new `Direction` which represents the given one being rotated "left" or counter-clockwise by one unit.
98    fn rotate_left(self) -> Self;
99
100    /// Returns a new `Direction` which represents the given one being rotated "right" or clockwise by 90 degrees.
101    fn rotate_right_90(self) -> Self;
102
103    /// Returns a new `Direction` which represents the given one being rotated "left" or counter-clockwise by 90 degrees.
104    fn rotate_left_90(self) -> Self;
105
106    /// Returns a new `Direction` which represents the "opposite" of the given one. Can also be
107    /// seen as rotating the given one by 180 degrees.
108    fn opposite(self) -> Self;
109
110    /// Takes in a pair of `usize`s representing (x, y) coordinates, and returns the result of
111    /// "moving" one unit in the given direction. Can panic (see `usizes_plus_i()` documentation).
112    ///
113    /// ```
114    /// use aoclib_rs::dir::{Dir4, Dir8, Direction};
115    /// assert_eq!(Dir4::Right.apply_delta_to_usizes((4, 5)), (5, 5));
116    /// assert_eq!(Dir8::UpRight.apply_delta_to_usizes((4, 5)), (5, 4));
117    ///
118    /// // Dir4::Left.apply_delta_to_usizes((0, 0));
119    /// // panics: -1 is an invalid usize
120    /// ```
121    fn apply_delta_to_usizes(self, usizes: (usize, usize)) -> (usize, usize) {
122        let (d_x, d_y) = self.delta();
123        (
124            usize_plus_i(usizes.0, i64::from(d_x)),
125            usize_plus_i(usizes.1, i64::from(d_y)),
126        )
127    }
128}
129
130/// The four cardinal directions.
131#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
132pub enum Dir4 {
133    Up,
134    Down,
135    Left,
136    Right,
137}
138
139impl Direction for Dir4 {
140    fn delta(self) -> (i8, i8) {
141        match self {
142            Dir4::Up => (0, -1),
143            Dir4::Down => (0, 1),
144            Dir4::Left => (-1, 0),
145            Dir4::Right => (1, 0),
146        }
147    }
148
149    fn rotate_right(self) -> Dir4 {
150        match self {
151            Dir4::Up => Dir4::Right,
152            Dir4::Down => Dir4::Left,
153            Dir4::Left => Dir4::Up,
154            Dir4::Right => Dir4::Down,
155        }
156    }
157
158    fn rotate_left(self) -> Dir4 {
159        self.rotate_right().opposite()
160    }
161
162    fn rotate_right_90(self) -> Dir4 {
163        self.rotate_right()
164    }
165
166    fn rotate_left_90(self) -> Dir4 {
167        self.rotate_left()
168    }
169
170    fn opposite(self) -> Dir4 {
171        match self {
172            Dir4::Up => Dir4::Down,
173            Dir4::Down => Dir4::Up,
174            Dir4::Left => Dir4::Right,
175            Dir4::Right => Dir4::Left,
176        }
177    }
178
179    fn iter() -> impl Iterator<Item = Dir4> {
180        Self::iter_from(Dir4::Up)
181    }
182}
183
184/// The four cardinal directions, plus the 4 diagonals in between.
185#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
186pub enum Dir8 {
187    Dir4(Dir4),
188    UpRight,
189    UpLeft,
190    DownRight,
191    DownLeft,
192}
193
194impl Direction for Dir8 {
195    fn delta(self) -> (i8, i8) {
196        match self {
197            Dir8::Dir4(d4) => d4.delta(),
198            Dir8::UpRight => (1, -1),
199            Dir8::UpLeft => (-1, -1),
200            Dir8::DownRight => (1, 1),
201            Dir8::DownLeft => (-1, 1),
202        }
203    }
204
205    fn rotate_right(self) -> Dir8 {
206        match self {
207            Dir8::Dir4(Dir4::Up) => Dir8::UpRight,
208            Dir8::Dir4(Dir4::Down) => Dir8::DownLeft,
209            Dir8::Dir4(Dir4::Left) => Dir8::UpLeft,
210            Dir8::Dir4(Dir4::Right) => Dir8::DownRight,
211            Dir8::UpRight => Dir8::Dir4(Dir4::Right),
212            Dir8::UpLeft => Dir8::Dir4(Dir4::Up),
213            Dir8::DownRight => Dir8::Dir4(Dir4::Down),
214            Dir8::DownLeft => Dir8::Dir4(Dir4::Left),
215        }
216    }
217
218    fn rotate_left(self) -> Dir8 {
219        match self {
220            Dir8::Dir4(Dir4::Up) => Dir8::UpLeft,
221            Dir8::Dir4(Dir4::Down) => Dir8::DownRight,
222            Dir8::Dir4(Dir4::Left) => Dir8::DownLeft,
223            Dir8::Dir4(Dir4::Right) => Dir8::UpRight,
224            Dir8::UpRight => Dir8::Dir4(Dir4::Up),
225            Dir8::UpLeft => Dir8::Dir4(Dir4::Left),
226            Dir8::DownRight => Dir8::Dir4(Dir4::Right),
227            Dir8::DownLeft => Dir8::Dir4(Dir4::Down),
228        }
229    }
230
231    fn rotate_right_90(self) -> Dir8 {
232        match self {
233            Dir8::Dir4(d4) => Dir8::Dir4(d4.rotate_right_90()),
234            Dir8::UpRight => Dir8::DownRight,
235            Dir8::UpLeft => Dir8::UpRight,
236            Dir8::DownRight => Dir8::DownLeft,
237            Dir8::DownLeft => Dir8::UpLeft,
238        }
239    }
240
241    fn rotate_left_90(self) -> Dir8 {
242        match self {
243            Dir8::Dir4(d4) => Dir8::Dir4(d4.rotate_left_90()),
244            Dir8::UpRight => Dir8::UpLeft,
245            Dir8::UpLeft => Dir8::DownLeft,
246            Dir8::DownRight => Dir8::UpRight,
247            Dir8::DownLeft => Dir8::DownRight,
248        }
249    }
250
251    fn opposite(self) -> Dir8 {
252        match self {
253            Dir8::Dir4(d4) => Dir8::Dir4(d4.opposite()),
254            Dir8::UpRight => Dir8::DownLeft,
255            Dir8::UpLeft => Dir8::DownRight,
256            Dir8::DownRight => Dir8::UpLeft,
257            Dir8::DownLeft => Dir8::UpRight,
258        }
259    }
260
261    fn iter() -> impl Iterator<Item = Dir8> {
262        Self::iter_from(Dir8::Dir4(Dir4::Up))
263    }
264}