Advent of Code solutions
at main 169 lines 5.0 kB view raw
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];