From fd608ac482c326700c6dff13bd503e78488167cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zo=C3=A9=20Cassiop=C3=A9e=20Gauthier?= Date: Wed, 15 Dec 2021 13:02:11 -0500 Subject: [PATCH] Day 154, part 2 --- src/day15.rs | 159 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 111 insertions(+), 48 deletions(-) diff --git a/src/day15.rs b/src/day15.rs index 7b58233..450c304 100644 --- a/src/day15.rs +++ b/src/day15.rs @@ -1,84 +1,120 @@ -use std::collections::HashSet; +use std::cmp::Ordering; +use std::collections::BinaryHeap; -#[derive(Clone)] +#[derive(Copy, Clone, Eq, PartialEq)] struct Vertex { - position: (u32, u32), + position: (usize, usize), risk: u32, } -fn remove_min(v: &mut Vec) -> Option { - let index = v - .iter() - .enumerate() - .min_by(|(_, x), (_, y)| x.risk.cmp(&y.risk)) - .map(|(index, _)| index); - index.map(|index| v.swap_remove(index)) +impl Ord for Vertex { + fn cmp(&self, other: &Self) -> Ordering { + other + .risk + .cmp(&self.risk) + .then_with(|| self.position.cmp(&other.position)) + } +} + +impl PartialOrd for Vertex { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } struct RiskMap { levels: Vec>, - target: (u32, u32), - explored: HashSet<(u32, u32)>, + size: usize, + target: (usize, usize), + risks: Vec, } impl RiskMap { fn new(levels: Vec>) -> RiskMap { - let target = (levels[0].len() as u32 - 1, levels.len() as u32 - 1); + let size = levels.len(); RiskMap { levels, - target, - explored: HashSet::new(), + size, + target: (size - 1, size - 1), + risks: (0..size * size).map(|_| u32::MAX).collect(), } } - fn explore(&mut self, origin: &Vertex, x: i32, y: i32) -> Option { - if x < 0 || y < 0 || x as u32 > self.target.0 || y as u32 > self.target.1 { - return None; + fn explore(&mut self, position: (usize, usize), risk: u32) -> Option { + let next_risk = risk + self.levels[position.1][position.0]; + if next_risk < self.risks[position.1 * self.size + position.0] { + self.risks[position.1 * self.size + position.0] = next_risk; + Some(Vertex { + position, + risk: next_risk, + }) + } else { + None } - - let position = (x as u32, y as u32); - if self.explored.contains(&position) { - return None; - } - - let risk = origin.risk + self.levels[y as usize][x as usize]; - Some(Vertex { position, risk }) } fn shortest_path(&mut self) -> Option { - let mut paths: Vec = Vec::new(); + let mut paths: BinaryHeap = BinaryHeap::new(); + paths.push(Vertex { position: (0, 0), risk: 0, }); - loop { - match remove_min(&mut paths) { - Some(path) => { - if path.position == self.target { - return Some(path.risk); - } - self.explored.insert(path.position); - let (x, y) = (path.position.0 as i32, path.position.1 as i32); - if let Some(neighbor) = self.explore(&path, x - 1, y) { - paths.push(neighbor); - } - if let Some(neighbor) = self.explore(&path, x + 1, y) { - paths.push(neighbor); - } - if let Some(neighbor) = self.explore(&path, x, y - 1) { - paths.push(neighbor); - } - if let Some(neighbor) = self.explore(&path, x, y + 1) { - paths.push(neighbor); - } + while let Some(Vertex { position, risk }) = paths.pop() { + if position == self.target { + return Some(risk); + } + + if risk > self.risks[position.1 * self.size + position.0] { + continue; + } + + if position.0 > 0 { + if let Some(next) = self.explore((position.0 - 1, position.1), risk) { + paths.push(next); + } + } + if position.0 < self.size - 1 { + if let Some(next) = self.explore((position.0 + 1, position.1), risk) { + paths.push(next); + } + } + if position.1 > 0 { + if let Some(next) = self.explore((position.0, position.1 - 1), risk) { + paths.push(next); + } + } + if position.1 < self.size - 1 { + if let Some(next) = self.explore((position.0, position.1 + 1), risk) { + paths.push(next); } - None => return None, } } + + None } } +fn expand_map(risk_map: Vec>) -> Vec> { + let mut result = Vec::new(); + for i in 0..5 { + for row in risk_map.iter() { + let rows = [ + row.iter() + .map(|x| (x + i - 1) % 9 + 1) + .collect::>(), + row.iter().map(|x| (x + i) % 9 + 1).collect(), + row.iter().map(|x| (x + i + 1) % 9 + 1).collect(), + row.iter().map(|x| (x + i + 2) % 9 + 1).collect(), + row.iter().map(|x| (x + i + 3) % 9 + 1).collect(), + ]; + result.push(rows.concat()); + } + } + result +} + fn main() { const INPUT: &str = include_str!("../inputs/day15.txt"); let risk_level = INPUT @@ -87,6 +123,13 @@ fn main() { .collect::>>(); let mut risk_map = RiskMap::new(risk_level); println!("solution {}", risk_map.shortest_path().unwrap()); + + let risk_level = INPUT + .lines() + .map(|line| line.chars().map(|c| c.to_digit(10).unwrap()).collect()) + .collect::>>(); + let mut risk_map = RiskMap::new(expand_map(risk_level)); + println!("solution {}", risk_map.shortest_path().unwrap()); } #[test] @@ -108,3 +151,23 @@ fn test_shortest_path() { let mut risk_map = RiskMap::new(risk_level); assert_eq!(risk_map.shortest_path().unwrap(), 40); } + +#[test] +fn test_expand_map() { + const INPUT: &str = "1163751742 +1381373672 +2136511328 +3694931569 +7463417111 +1319128137 +1359912421 +3125421639 +1293138521 +2311944581"; + let risk_level = INPUT + .lines() + .map(|line| line.chars().map(|c| c.to_digit(10).unwrap()).collect()) + .collect::>>(); + let mut risk_map = RiskMap::new(expand_map(risk_level)); + assert_eq!(risk_map.shortest_path().unwrap(), 315); +}