Advent of Code solutions
1use std::ops::Range;
2
3/// Module containing utilities related to direction and movement.
4use crate::pos::Position;
5
6/// Trait used to define an object that can be used to move around a grid.
7///
8/// This is meant for complex scenarios where you want to move around a grid in a non-standard way.
9/// By implementing this trait you can use various methods from the [Position] struct to move around.
10///
11/// # Implementing
12///
13/// Implementing this trait requires you to define a `get_kernel` method that returns a `Position`.
14/// This position is used to move around the grid by applying it to the current position.
15///
16/// # Examples
17///
18/// ```
19/// use utils::prelude::*;
20///
21/// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
22/// struct RightBy(usize);
23///
24/// impl Movement for RightBy {
25/// fn get_kernel(&self) -> Position {
26/// Position::new(self.0 as isize, 0)
27/// }
28/// }
29///
30/// let pos = Position::new(0, 0);
31/// assert_eq!(pos.move_dir(RightBy(1)), Position::new(1, 0));
32/// ```
33///
34/// # See also
35///
36/// - [Direction] is a simple implementation of this trait.
37/// - [Position] is the main user of this trait.
38///
39pub trait Movement: std::fmt::Debug + Copy + Clone + PartialEq + std::hash::Hash {
40 fn get_kernel(&self) -> Position;
41
42 /// Repeat the given movement across the range `range`.
43 fn repeat(&self, range: Range<isize>) -> impl Iterator<Item = Position> {
44 let k = self.get_kernel();
45 range.map(move |i| k.multiply_comp(i))
46 }
47}
48
49/// The four cardinal directions.
50/// Useful for iterating over all four directions.
51pub const CARDINALS: [Direction; 4] = [
52 Direction::North,
53 Direction::South,
54 Direction::East,
55 Direction::West,
56];
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
59/// The four cardinal directions.
60/// This is a simple implementation of the [Movement] trait.
61///
62/// # Examples
63///
64/// ```
65/// use utils::prelude::*;
66///
67/// let pos = Position::new(0, 0);
68/// assert_eq!(pos.move_dir(Direction::North), Position::new(0, -1));
69/// assert_eq!(pos.move_dir(Direction::South), Position::new(0, 1));
70/// assert_eq!(pos.move_dir(Direction::East), Position::new(1, 0));
71/// assert_eq!(pos.move_dir(Direction::West), Position::new(-1, 0));
72/// ```
73///
74pub enum Direction {
75 North,
76 South,
77 East,
78 West,
79}
80
81impl Direction {
82 /// Returns the direction that is opposite to the current one.
83 ///
84 /// # Examples
85 ///
86 /// ```
87 /// use utils::prelude::*;
88 ///
89 /// assert_eq!(Direction::North.opposite(), Direction::South);
90 /// assert_eq!(Direction::South.opposite(), Direction::North);
91 /// assert_eq!(Direction::East.opposite(), Direction::West);
92 /// assert_eq!(Direction::West.opposite(), Direction::East);
93 /// ```
94 ///
95 pub fn opposite(&self) -> Self {
96 match self {
97 Self::North => Self::South,
98 Self::South => Self::North,
99 Self::East => Self::West,
100 Self::West => Self::East,
101 }
102 }
103
104 /// Returns the direction that is 90 degrees to the current one.
105 ///
106 /// # Examples
107 ///
108 /// ```
109 /// use utils::prelude::*;
110 ///
111 /// assert_eq!(Direction::North.ninety_deg(true), Direction::East);
112 /// assert_eq!(Direction::North.ninety_deg(false), Direction::West);
113 ///
114 /// assert_eq!(Direction::South.ninety_deg(true), Direction::West);
115 /// assert_eq!(Direction::South.ninety_deg(false), Direction::East);
116 /// ```
117 ///
118 pub fn ninety_deg(&self, clockwise: bool) -> Self {
119 match (self, clockwise) {
120 (Self::North, true) => Self::East,
121 (Self::North, false) => Self::West,
122 (Self::South, true) => Self::West,
123 (Self::South, false) => Self::East,
124 (Self::East, true) => Self::South,
125 (Self::East, false) => Self::North,
126 (Self::West, true) => Self::North,
127 (Self::West, false) => Self::South,
128 }
129 }
130
131 pub fn is_horizontal(&self) -> bool {
132 matches!(self, Direction::East | Direction::West)
133 }
134}
135
136impl From<Position> for Direction {
137 fn from(pos: Position) -> Self {
138 let pos = pos.normalize();
139 match (pos.x, pos.y) {
140 (0, -1) => Self::North,
141 (0, 1) => Self::South,
142 (1, 0) => Self::East,
143 (-1, 0) => Self::West,
144 _ => panic!("Invalid position"),
145 }
146 }
147}
148
149impl Movement for Direction {
150 fn get_kernel(&self) -> Position {
151 match self {
152 Direction::North => Position::new(0, -1),
153 Direction::South => Position::new(0, 1),
154 Direction::East => Position::new(1, 0),
155 Direction::West => Position::new(-1, 0),
156 }
157 }
158}
159
160pub const ALL_8: [Position; 8] = [
161 Position::new(0, -1),
162 Position::new(0, 1),
163 Position::new(1, 0),
164 Position::new(-1, 0),
165 Position::new(1, 1),
166 Position::new(1, -1),
167 Position::new(-1, 1),
168 Position::new(-1, -1),
169];