This commit is contained in:
Jan-Bulthuis 2024-12-16 17:44:55 +01:00
parent 4ce7184b1e
commit dc5f488212
2 changed files with 288 additions and 0 deletions

287
aoc_2024/src/day16.rs Normal file
View File

@ -0,0 +1,287 @@
use std::{
cmp::{Ordering, Reverse},
collections::BinaryHeap,
};
use aoc_runner_derive::{aoc, aoc_generator};
use hashbrown::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Cell {
Empty,
Wall,
}
type Input = ((usize, usize), (usize, usize), Vec<Vec<Cell>>);
#[aoc_generator(day16)]
fn parse(input: &str) -> Input {
let mut pos = (0, 0);
let mut end = (0, 0);
let map = input
.lines()
.enumerate()
.map(|(y, line)| {
line.chars()
.enumerate()
.map(|(x, c)| match c {
'#' => Cell::Wall,
'.' => Cell::Empty,
'E' => {
end = (x, y);
Cell::Empty
}
'S' => {
pos = (x, y);
Cell::Empty
}
_ => unreachable!(),
})
.collect()
})
.collect();
(pos, end, map)
}
#[aoc(day16, part1)]
fn part1(input: &Input) -> i64 {
let start = (input.0 .0 as i64, input.0 .1 as i64);
let end = (input.1 .0 as i64, input.1 .1 as i64);
let map = input.2.clone();
let w = map[0].len();
let h = map.len();
let mut d = vec![vec![[i64::MAX; 4]; w]; h];
#[allow(clippy::type_complexity)]
let mut queue: BinaryHeap<Reverse<(i64, i64, i64, i64, i64)>> = BinaryHeap::new();
d[start.1 as usize][start.0 as usize][1] = 0;
queue.push(Reverse((0, start.0, start.1, 1, 0)));
while let Some(Reverse((dist, pos_x, pos_y, dir_x, dir_y))) = queue.pop() {
let dir_num = match (dir_x, dir_y) {
(0, -1) => 0,
(1, 0) => 1,
(0, 1) => 2,
(-1, 0) => 3,
_ => unreachable!(),
};
if d[pos_y as usize][pos_x as usize][dir_num] < dist {
continue;
}
if (pos_x, pos_y) == end {
break;
}
if map[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize] == Cell::Empty {
let dist_next = &mut d[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize][dir_num];
if (dist + 1).cmp(dist_next) == Ordering::Less {
*dist_next = dist + 1;
queue.push(Reverse((
*dist_next,
pos_x + dir_x,
pos_y + dir_y,
dir_x,
dir_y,
)));
}
}
{
let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 3) % 4];
if (dist + 1000).cmp(dist_next) == Ordering::Less {
*dist_next = dist + 1000;
queue.push(Reverse((*dist_next, pos_x, pos_y, dir_y, -dir_x)));
}
}
{
let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 1) % 4];
if (dist + 1000).cmp(dist_next) == Ordering::Less {
*dist_next = dist + 1000;
queue.push(Reverse((*dist_next, pos_x, pos_y, -dir_y, dir_x)));
}
}
}
*d[end.1 as usize][end.0 as usize].iter().min().unwrap()
}
#[aoc(day16, part2)]
fn part2(input: &Input) -> i64 {
let start = (input.0 .0 as i64, input.0 .1 as i64);
let end = (input.1 .0 as i64, input.1 .1 as i64);
let map = input.2.clone();
let w = map[0].len();
let h = map.len();
let mut d = vec![vec![[i64::MAX; 4]; w]; h];
let mut b = vec![vec![[vec![], vec![], vec![], vec![]]; w]; h];
#[allow(clippy::type_complexity)]
let mut queue: BinaryHeap<Reverse<(i64, i64, i64, i64, i64)>> = BinaryHeap::new();
d[start.1 as usize][start.0 as usize][1] = 0;
queue.push(Reverse((0, start.0, start.1, 1, 0)));
while let Some(Reverse((dist, pos_x, pos_y, dir_x, dir_y))) = queue.pop() {
let dir_num = match (dir_x, dir_y) {
(0, -1) => 0,
(1, 0) => 1,
(0, 1) => 2,
(-1, 0) => 3,
_ => unreachable!(),
};
if d[pos_y as usize][pos_x as usize][dir_num] < dist {
continue;
}
if (pos_x, pos_y) == end {
break;
}
if map[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize] == Cell::Empty {
let dist_next = &mut d[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize][dir_num];
let back_next = &mut b[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize][dir_num];
if (dist + 1).cmp(dist_next) == Ordering::Less {
*dist_next = dist + 1;
back_next.clear();
back_next.push((pos_x, pos_y, dir_num));
queue.push(Reverse((
*dist_next,
pos_x + dir_x,
pos_y + dir_y,
dir_x,
dir_y,
)));
} else if (dist + 1).cmp(dist_next) == Ordering::Equal {
back_next.push((pos_x, pos_y, dir_num));
}
}
{
let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 3) % 4];
let back_next = &mut b[pos_y as usize][pos_x as usize][(dir_num + 3) % 4];
if (dist + 1000).cmp(dist_next) == Ordering::Less {
*dist_next = dist + 1000;
back_next.clear();
back_next.push((pos_x, pos_y, dir_num));
queue.push(Reverse((*dist_next, pos_x, pos_y, dir_y, -dir_x)));
} else if (dist + 1000).cmp(dist_next) == Ordering::Equal {
back_next.push((pos_x, pos_y, dir_num));
}
}
{
let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 1) % 4];
let back_next = &mut b[pos_y as usize][pos_x as usize][(dir_num + 1) % 4];
if (dist + 1000).cmp(dist_next) == Ordering::Less {
*dist_next = dist + 1000;
back_next.clear();
back_next.push((pos_x, pos_y, dir_num));
queue.push(Reverse((*dist_next, pos_x, pos_y, -dir_y, dir_x)));
} else if (dist + 1000).cmp(dist_next) == Ordering::Equal {
back_next.push((pos_x, pos_y, dir_num));
}
}
}
let mut visited = HashSet::new();
let dist_min = *d[end.1 as usize][end.0 as usize].iter().min().unwrap();
let mut stack = vec![];
d[end.1 as usize][end.0 as usize]
.iter()
.enumerate()
.for_each(|(i, d)| {
if *d == dist_min {
stack.push((end.0, end.1, i));
}
});
while let Some((pos_x, pos_y, dir_num)) = stack.pop() {
visited.insert((pos_x, pos_y));
stack.append(&mut b[pos_y as usize][pos_x as usize][dir_num]);
}
visited.len() as i64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_example() {
assert_eq!(
part1(&parse(
"###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############"
)),
7036
);
assert_eq!(
part1(&parse(include_str!("../input/2024/day16.txt"))),
85420
);
}
#[test]
fn part2_example() {
assert_eq!(
part2(&parse(
"###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############"
)),
45
);
assert_eq!(
part2(&parse(
"#################
#...#...#...#..E#
#.#.#.#.#.#.#.#.#
#.#.#.#...#...#.#
#.#.#.#.###.#.#.#
#...#.#.#.....#.#
#.#.#.#.#.#####.#
#.#...#.#.#.....#
#.#.#####.#.###.#
#.#.#.......#...#
#.#.###.#####.###
#.#.#...#.....#.#
#.#.#.#####.###.#
#.#.#.........#.#
#.#.#.#########.#
#S#.............#
#################"
)),
64
);
assert_eq!(part2(&parse(include_str!("../input/2024/day16.txt"))), 492);
}
}

View File

@ -5,6 +5,7 @@ mod day12;
mod day13;
mod day14;
mod day15;
mod day16;
mod day2;
mod day3;
mod day4;