diff --git a/src/day18.rs b/src/day18.rs index 4f3d5a9..942b71d 100644 --- a/src/day18.rs +++ b/src/day18.rs @@ -1,7 +1,7 @@ -use std::{cmp::Reverse, collections::BinaryHeap}; - use aoc_runner_derive::aoc; use grid::Grid; +use itertools::Itertools; +use std::{cmp::Reverse, collections::BinaryHeap}; #[derive(Clone)] struct MemoryMap { @@ -9,6 +9,34 @@ struct MemoryMap { byte_stream: Vec<(i64, i64)>, } +trait PathTrack { + fn new() -> Self; + fn push(&mut self, pos: (i64, i64)); + fn finalize(&mut self) {} +} + +struct LengthPath(usize); +impl PathTrack for LengthPath { + fn new() -> Self { + LengthPath(0) + } + fn push(&mut self, _: (i64, i64)) { + self.0 += 1 + } +} + +impl PathTrack for Vec<(i64, i64)> { + fn new() -> Self { + Vec::new() + } + fn push(&mut self, pos: (i64, i64)) { + self.push(pos); + } + fn finalize(&mut self) { + self.reverse(); + } +} + impl MemoryMap { fn from_str(input: &str, width: usize, height: usize) -> Self { let map = Grid::with_shape(width, height, true); @@ -36,45 +64,46 @@ impl MemoryMap { } } - fn valid_moves(&self, pos: &(i64, i64)) -> Vec<(i64, i64)> { - [(0, 1), (1, 0), (0, -1), (-1, 0)] + fn valid_moves<'a>(&'a self, pos: &'a (i64, i64)) -> impl Iterator + 'a { + ([(0, 1), (1, 0), (0, -1), (-1, 0)]) .iter() .filter(|ofs| self.map.get(&(pos.0 + ofs.0, pos.1 + ofs.1)).is_some_and(|v| *v)) .map(|ofs| (pos.0 + ofs.0, pos.1 + ofs.1)) - .collect() } - fn dijkstra(&self) -> Option> { - let start = (0i64, 0i64); + fn dijkstra(&self, start: (i64, i64)) -> Option { let goal = (self.map.width() as i64 - 1, self.map.height() as i64 - 1); - let mut distances = self.map.same_shape(i64::MAX); + let mut costs = self.map.same_shape(i64::MAX); let mut prev = self.map.same_shape((i64::MAX, i64::MAX)); let mut queue = BinaryHeap::new(); - distances.set(&start, 0); + costs.set(&start, 0); queue.push((Reverse(0), start)); while let Some((cost, pos)) = queue.pop() { if pos == goal { - let mut visited = Vec::new(); let mut visited_pos = goal; - - visited.push(pos); + let mut path = T::new(); + path.push(pos); while let Some(next) = prev.get(&visited_pos) { visited_pos = *next; - visited.push(*next); + path.push(*next); + if *next == start { + path.finalize(); + return Some(path); + } } - return Some(visited); } - if distances.get(&pos).is_some_and(|v| cost.0 > *v) { + if costs.get(&pos).is_some_and(|v| cost.0 > *v) { continue; } - for new_pos in self.valid_moves(&pos) { - if distances.get(&new_pos).is_none_or(|best_cost| cost.0 + 1 < *best_cost) { - distances.set(&new_pos, cost.0 + 1); + let moves = self.valid_moves(&pos); + for new_pos in moves { + if costs.get(&new_pos).is_none_or(|best_cost| cost.0 + 1 < *best_cost) { + costs.set(&new_pos, cost.0 + 1); prev.set(&new_pos, pos); queue.push((Reverse(cost.0 + 1), new_pos)); } @@ -87,34 +116,27 @@ impl MemoryMap { pub fn part1_impl(input: &str, width: usize, height: usize, n: usize) -> usize { let mut map = MemoryMap::from_str(input, width, height); map.place_bytes(n); - let path = map.dijkstra().expect("no path found"); - let mut sol_map = map.map.same_shape(b'.'); - sol_map.data = map - .map - .data - .iter() - .map(|clear| if *clear { b'.' } else { b'#' }) - .collect(); - for visited in &path { - sol_map.set(visited, b'O'); - } + let path = map.dijkstra::((0, 0)).expect("no path found"); - path.len() - 2 // count vertexes, not visited nodes + path.0 - 1 // count edges, not visited nodes (start doesn't count) } pub fn part2_impl(input: &str, width: usize, height: usize, n: usize) -> (i64, i64) { let mut input_map = MemoryMap::from_str(input, width, height); input_map.place_bytes(n); - let mut path = input_map.dijkstra().expect("no path found"); + let mut path = input_map.dijkstra::>((0, 0)).expect("no path found"); + println!("{:?}", path); for byte in n..input_map.byte_stream.len() { input_map.place_byte(byte); - if path.contains(&input_map.byte_stream[byte]) { - if let Some(new_path) = input_map.dijkstra() { - path = new_path; + + if let Some((obs_at, _)) = path.iter().find_position(|v| *v == &input_map.byte_stream[byte]) { + let (before, _) = path.split_at(obs_at); + + if let Some(new_path) = input_map.dijkstra::>(path[obs_at - 1]) { + path = [before, &new_path].concat(); } else { - println!("obstruction found on trial {}", byte); return input_map.byte_stream[byte]; } }