Day 154, part 2

This commit is contained in:
Zoé Cassiopée Gauthier 2021-12-15 13:02:11 -05:00
parent 30b20a0a51
commit fd608ac482

View File

@ -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 { struct Vertex {
position: (u32, u32), position: (usize, usize),
risk: u32, risk: u32,
} }
fn remove_min(v: &mut Vec<Vertex>) -> Option<Vertex> { impl Ord for Vertex {
let index = v fn cmp(&self, other: &Self) -> Ordering {
.iter() other
.enumerate() .risk
.min_by(|(_, x), (_, y)| x.risk.cmp(&y.risk)) .cmp(&self.risk)
.map(|(index, _)| index); .then_with(|| self.position.cmp(&other.position))
index.map(|index| v.swap_remove(index)) }
}
impl PartialOrd for Vertex {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
} }
struct RiskMap { struct RiskMap {
levels: Vec<Vec<u32>>, levels: Vec<Vec<u32>>,
target: (u32, u32), size: usize,
explored: HashSet<(u32, u32)>, target: (usize, usize),
risks: Vec<u32>,
} }
impl RiskMap { impl RiskMap {
fn new(levels: Vec<Vec<u32>>) -> RiskMap { fn new(levels: Vec<Vec<u32>>) -> RiskMap {
let target = (levels[0].len() as u32 - 1, levels.len() as u32 - 1); let size = levels.len();
RiskMap { RiskMap {
levels, levels,
target, size,
explored: HashSet::new(), target: (size - 1, size - 1),
risks: (0..size * size).map(|_| u32::MAX).collect(),
} }
} }
fn explore(&mut self, origin: &Vertex, x: i32, y: i32) -> Option<Vertex> { fn explore(&mut self, position: (usize, usize), risk: u32) -> Option<Vertex> {
if x < 0 || y < 0 || x as u32 > self.target.0 || y as u32 > self.target.1 { let next_risk = risk + self.levels[position.1][position.0];
return None; 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<u32> { fn shortest_path(&mut self) -> Option<u32> {
let mut paths: Vec<Vertex> = Vec::new(); let mut paths: BinaryHeap<Vertex> = BinaryHeap::new();
paths.push(Vertex { paths.push(Vertex {
position: (0, 0), position: (0, 0),
risk: 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); while let Some(Vertex { position, risk }) = paths.pop() {
if let Some(neighbor) = self.explore(&path, x - 1, y) { if position == self.target {
paths.push(neighbor); return Some(risk);
} }
if let Some(neighbor) = self.explore(&path, x + 1, y) {
paths.push(neighbor); if risk > self.risks[position.1 * self.size + position.0] {
} continue;
if let Some(neighbor) = self.explore(&path, x, y - 1) { }
paths.push(neighbor);
} if position.0 > 0 {
if let Some(neighbor) = self.explore(&path, x, y + 1) { if let Some(next) = self.explore((position.0 - 1, position.1), risk) {
paths.push(neighbor); 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<u32>>) -> Vec<Vec<u32>> {
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::<Vec<u32>>(),
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() { fn main() {
const INPUT: &str = include_str!("../inputs/day15.txt"); const INPUT: &str = include_str!("../inputs/day15.txt");
let risk_level = INPUT let risk_level = INPUT
@ -87,6 +123,13 @@ fn main() {
.collect::<Vec<Vec<u32>>>(); .collect::<Vec<Vec<u32>>>();
let mut risk_map = RiskMap::new(risk_level); let mut risk_map = RiskMap::new(risk_level);
println!("solution {}", risk_map.shortest_path().unwrap()); 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::<Vec<Vec<u32>>>();
let mut risk_map = RiskMap::new(expand_map(risk_level));
println!("solution {}", risk_map.shortest_path().unwrap());
} }
#[test] #[test]
@ -108,3 +151,23 @@ fn test_shortest_path() {
let mut risk_map = RiskMap::new(risk_level); let mut risk_map = RiskMap::new(risk_level);
assert_eq!(risk_map.shortest_path().unwrap(), 40); 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::<Vec<Vec<u32>>>();
let mut risk_map = RiskMap::new(expand_map(risk_level));
assert_eq!(risk_map.shortest_path().unwrap(), 315);
}