136 lines
3.4 KiB
Rust
136 lines
3.4 KiB
Rust
use aoc_runner_derive::{aoc, aoc_generator};
|
|
use pathfinding::matrix::{directions, Matrix};
|
|
use pathfinding::prelude::astar;
|
|
|
|
#[aoc_generator(day17)]
|
|
fn parse(input: &str) -> Matrix<usize> {
|
|
input
|
|
.lines()
|
|
.filter_map(|line| {
|
|
if !line.is_empty() {
|
|
Some(
|
|
line.chars()
|
|
.map(|c| c.to_digit(10).expect("bad digit") as usize),
|
|
)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect::<Matrix<usize>>()
|
|
}
|
|
|
|
#[derive(Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
|
|
struct Path {
|
|
position: (usize, usize),
|
|
dir: (isize, isize),
|
|
steps: usize,
|
|
}
|
|
|
|
fn distance(s: &(usize, usize), d: &(usize, usize)) -> usize {
|
|
s.0.abs_diff(d.0) + s.1.abs_diff(d.1)
|
|
}
|
|
|
|
fn find_path<const MIN: usize, const MAX: usize>(map: &Matrix<usize>) -> usize {
|
|
let start = Path {
|
|
position: (0, 0),
|
|
dir: (0, 0),
|
|
steps: 0,
|
|
};
|
|
let dest = (map.rows - 1, map.columns - 1);
|
|
|
|
let path = astar(
|
|
&start,
|
|
|cur| match cur.steps >= MIN || (cur.dir.0 == 0 && cur.dir.1 == 0) {
|
|
true => find_successors::<MAX>(map, cur),
|
|
false => continue_in_direction::<MIN>(map, cur),
|
|
},
|
|
|cur| distance(&dest, &cur.position),
|
|
|cur| cur.position == dest && cur.steps >= MIN,
|
|
)
|
|
.expect("path is missing");
|
|
path.1
|
|
}
|
|
|
|
fn find_successors<const MAX: usize>(map: &Matrix<usize>, p: &Path) -> Vec<(Path, usize)> {
|
|
[directions::N, directions::E, directions::S, directions::W]
|
|
.iter()
|
|
.flat_map(|dir| {
|
|
map.move_in_direction(p.position, *dir)
|
|
.map(|cur| (cur, *dir, *map.get(cur).unwrap()))
|
|
})
|
|
.filter(|(cur, dir, _weight)| {
|
|
let backwards = p.dir.0 == -dir.0 && p.dir.1 == -dir.1;
|
|
!backwards && *cur != (0, 0)
|
|
})
|
|
.flat_map(|(cur, dir, weight)| {
|
|
let steps = match p.dir == dir {
|
|
true => p.steps + 1,
|
|
false => 1,
|
|
};
|
|
|
|
match steps <= MAX {
|
|
true => {
|
|
let next = Path {
|
|
position: cur,
|
|
dir,
|
|
steps,
|
|
};
|
|
Some((next, weight))
|
|
}
|
|
false => None,
|
|
}
|
|
})
|
|
.collect::<Vec<_>>()
|
|
}
|
|
|
|
fn continue_in_direction<const MIN: usize>(map: &Matrix<usize>, p: &Path) -> Vec<(Path, usize)> {
|
|
match map.move_in_direction(p.position, p.dir) {
|
|
Some(point) => {
|
|
let heat = *map.get(point).unwrap();
|
|
let new_path = Path {
|
|
position: point,
|
|
dir: p.dir,
|
|
steps: p.steps + 1,
|
|
};
|
|
vec![(new_path, heat)]
|
|
}
|
|
None => Vec::with_capacity(0),
|
|
}
|
|
}
|
|
|
|
#[aoc(day17, part1)]
|
|
fn part1(input: &Matrix<usize>) -> usize {
|
|
find_path::<1, 3>(&input)
|
|
}
|
|
|
|
#[aoc(day17, part2)]
|
|
fn part2(input: &Matrix<usize>) -> usize {
|
|
find_path::<4, 10>(&input)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
const SAMPLE_DATA: &'static str = r#"2413432311323
|
|
3215453535623
|
|
3255245654254
|
|
3446585845452
|
|
4546657867536
|
|
1438598798454
|
|
4457876987766
|
|
3637877979653
|
|
4654967986887
|
|
4564679986453
|
|
1224686865563
|
|
2546548887735
|
|
4322674655533
|
|
"#;
|
|
|
|
#[test]
|
|
fn sample_data() {
|
|
let input = parse(&SAMPLE_DATA);
|
|
assert_eq!(part1(&input), 102);
|
|
}
|
|
}
|