solution: day 17

This commit is contained in:
Matej Janezic 2023-12-17 14:41:03 +01:00
parent 0000032032
commit 0000033086
Signed by: janezicmatej
GPG Key ID: 4298E230ED37B2C0
2 changed files with 205 additions and 0 deletions

13
data/examples/17.txt Normal file
View File

@ -0,0 +1,13 @@
2413432311323
3215453535623
3255245654254
3446585845452
4546657867536
1438598798454
4457876987766
3637877979653
4654967986887
4564679986453
1224686865563
2546548887735
4322674655533

192
src/bin/17.rs Normal file
View File

@ -0,0 +1,192 @@
use std::{
collections::{BinaryHeap, HashMap},
ops::Neg,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Direction {
Up,
Down,
Left,
Right,
}
impl Direction {
const ALL: [Direction; 4] = [
Direction::Up,
Direction::Down,
Direction::Left,
Direction::Right,
];
}
impl Neg for Direction {
type Output = Self;
fn neg(self) -> Self::Output {
use Direction::*;
match self {
Up => Down,
Down => Up,
Left => Right,
Right => Left,
}
}
}
#[derive(Debug, PartialEq, Eq)]
struct State {
heat: usize,
position: (usize, usize),
direction: Direction,
steps: usize,
}
impl PartialOrd for State {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for State {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other
.heat
.cmp(&self.heat)
.then_with(|| self.position.cmp(&other.position))
}
}
fn find_path(
grid: &[Vec<usize>],
start: (usize, usize),
target: (usize, usize),
min_steps: usize,
max_steps: usize,
) -> Option<usize> {
let valid_indexing = |y: usize, x: usize| grid.get(y).and_then(|row| row.get(x)).is_some();
let mut heap = BinaryHeap::new();
heap.push(State {
heat: 0,
position: (0, 0),
direction: Direction::Right,
steps: 0,
});
let mut heatmap = HashMap::new();
heatmap.insert((start, Direction::Down, 0), 0);
heatmap.insert((start, Direction::Up, 0), 0);
while let Some(State {
heat,
position,
direction,
steps,
}) = heap.pop()
{
if position == target {
return Some(heat);
}
if *heatmap
.get(&(position, direction, steps))
.unwrap_or(&usize::MAX)
< heat
{
continue;
}
let (y, x) = position;
for d in Direction::ALL.iter().filter(|&x| *x != -direction) {
if steps < min_steps && *d != direction {
continue;
}
let (ny, nx) = match d {
Direction::Up => (y.wrapping_sub(1), x),
Direction::Down => (y + 1, x),
Direction::Left => (y, x.wrapping_sub(1)),
Direction::Right => (y, x + 1),
};
if !valid_indexing(ny, nx) {
continue;
}
let new_steps = if *d == direction { steps + 1 } else { 1 };
if new_steps > max_steps {
continue;
}
let state = State {
heat: heat + grid[ny][nx],
position: (ny, nx),
direction: *d,
steps: new_steps,
};
if *heatmap
.get(&((ny, nx), *d, new_steps))
.unwrap_or(&usize::MAX)
> state.heat
{
heatmap.insert(((ny, nx), *d, new_steps), state.heat);
heap.push(state);
}
}
}
None
}
pub fn part_one(input: &str) -> Option<usize> {
let grid: Vec<Vec<_>> = input
.lines()
.map(|x| {
x.chars()
.filter_map(|x| x.to_digit(10).map(|x| x as usize))
.collect()
})
.collect();
let target = (grid.len() - 1, grid[0].len() - 1);
find_path(&grid, (0, 0), target, 0, 3)
}
pub fn part_two(input: &str) -> Option<usize> {
let grid: Vec<Vec<_>> = input
.lines()
.map(|x| {
x.chars()
.filter_map(|x| x.to_digit(10).map(|x| x as usize))
.collect()
})
.collect();
let target = (grid.len() - 1, grid[0].len() - 1);
find_path(&grid, (0, 0), target, 4, 10)
}
aoc::solution!(17);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(
part_one(&aoc::template::read_file("examples", 17)),
Some(102)
);
}
#[test]
fn test_part_two() {
assert_eq!(
part_two(&aoc::template::read_file("examples", 17)),
Some(94)
);
}
}