retroactive, to derust my rust
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 219 lines 5.7 kB view raw
1use std::collections::HashSet; 2 3pub fn day6_part1(input: &str) -> String { 4 let map = Map::parse(input); 5 map.step_forever().len().to_string() 6} 7 8pub fn day6_part2(input: &str) -> String { 9 let map = Map::parse(input); 10 map.new_maps() 11 .map(|map| map.loops()) 12 .filter(|&map| map) 13 .count() 14 .to_string() 15} 16 17#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)] 18struct Point { 19 row: usize, 20 col: usize, 21} 22 23impl Point { 24 fn step(self, dir: CardinalDirection) -> Self { 25 match dir { 26 CardinalDirection::Up => self.up(), 27 CardinalDirection::Left => self.left(), 28 CardinalDirection::Right => self.right(), 29 CardinalDirection::Down => self.down(), 30 } 31 } 32 fn up(self) -> Self { 33 Point { 34 row: self.row - 1, 35 ..self 36 } 37 } 38 fn down(self) -> Self { 39 Point { 40 row: self.row + 1, 41 ..self 42 } 43 } 44 fn left(self) -> Self { 45 Point { 46 col: self.col - 1, 47 ..self 48 } 49 } 50 fn right(self) -> Self { 51 Point { 52 col: self.col + 1, 53 ..self 54 } 55 } 56} 57 58#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 59enum CardinalDirection { 60 Up, 61 Left, 62 Right, 63 Down, 64} 65 66impl CardinalDirection { 67 fn rotate_90degrees_clockwise(self) -> Self { 68 match self { 69 Self::Up => Self::Right, 70 Self::Left => Self::Up, 71 Self::Right => Self::Down, 72 Self::Down => Self::Left, 73 } 74 } 75} 76 77impl From<char> for CardinalDirection { 78 fn from(c: char) -> Self { 79 match c { 80 '^' => Self::Up, 81 'v' | 'V' => Self::Down, 82 '<' => Self::Left, 83 '>' => Self::Right, 84 _ => unreachable!(), 85 } 86 } 87} 88 89#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 90struct Guard { 91 location: Point, 92 direction: CardinalDirection, 93} 94 95impl Guard { 96 fn step(self) -> Self { 97 Self { 98 location: self.location.step(self.direction), 99 ..self 100 } 101 } 102 fn step_obstacle(self) -> Self { 103 let new_dir = self.direction.rotate_90degrees_clockwise(); 104 Self { 105 direction: new_dir, 106 ..self 107 } 108 } 109} 110 111struct Map { 112 width: usize, 113 height: usize, 114 guard: Guard, 115 obstructions: HashSet<Point>, 116 visited: HashSet<Guard>, 117 stuck_in_a_loop: bool, 118} 119 120impl Map { 121 fn new_maps(self) -> impl Iterator<Item = Self> { 122 (0..self.width) 123 .flat_map(|col| (0..self.height).map(move |row| Point { row, col })) 124 .filter(|point| &self.guard.location != point && !self.obstructions.contains(point)) 125 .collect::<Vec<_>>() 126 .into_iter() 127 .map(move |point| Self { 128 obstructions: { 129 let mut obstructions = self.obstructions.clone(); 130 obstructions.insert(point); 131 obstructions 132 }, 133 visited: self.visited.clone(), 134 ..self 135 }) 136 } 137 138 fn loops(mut self) -> bool { 139 while self.step() && !self.stuck_in_a_loop {} 140 self.stuck_in_a_loop 141 } 142 143 fn step_forever(mut self) -> HashSet<Point> { 144 while self.step() {} 145 self.visited.iter().map(|g| g.location).collect() 146 } 147 148 /// returns whether a step was taken 149 fn step(&mut self) -> bool { 150 self.stuck_in_a_loop |= !self.visited.insert(self.guard); 151 if self.is_guard_about_to_leave() { 152 return false; 153 } 154 155 if self.is_guard_facing_obstruction() { 156 self.guard = self.guard.step_obstacle(); 157 } else { 158 self.guard = self.guard.step() 159 } 160 161 true 162 } 163 164 fn is_guard_facing_obstruction(&self) -> bool { 165 if self.is_guard_about_to_leave() { 166 return false; 167 } 168 let point = self.guard.step().location; 169 self.obstructions.contains(&point) 170 } 171 172 fn is_guard_about_to_leave(&self) -> bool { 173 match self.guard.direction { 174 CardinalDirection::Up => self.guard.location.row == 0, 175 CardinalDirection::Down => self.guard.location.row == self.height - 1, 176 CardinalDirection::Left => self.guard.location.col == 0, 177 CardinalDirection::Right => self.guard.location.col == self.width - 1, 178 } 179 } 180 181 fn parse(input: &str) -> Self { 182 let lines = input.lines().collect::<Vec<_>>(); 183 let height = lines.len(); 184 let width = lines[0].chars().count(); 185 let guard = lines 186 .iter() 187 .enumerate() 188 .find_map(|(row, line)| { 189 line.char_indices() 190 .find(|&(_, ch)| ch == '^' || ch == '>' || ch == '<' || ch == 'V' || ch == 'v') 191 .map(|(col, _)| (row, col)) 192 }) 193 .unwrap(); 194 let obstructions = lines 195 .iter() 196 .enumerate() 197 .flat_map(|(row, line)| { 198 line.char_indices() 199 .filter(|&(_, ch)| ch == '#') 200 .map(move |(col, _): (usize, char)| Point { row, col }) 201 }) 202 .collect(); 203 204 Self { 205 width, 206 height, 207 guard: Guard { 208 location: Point { 209 row: guard.0, 210 col: guard.1, 211 }, 212 direction: lines[guard.0].chars().nth(guard.1).unwrap().into(), 213 }, 214 obstructions, 215 visited: HashSet::new(), 216 stuck_in_a_loop: false, 217 } 218 } 219}