Skip to content

Commit

Permalink
Day 16: Reindeer Maze
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 18, 2024
1 parent 09ef91b commit 7281c10
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ Development occurs in language-specific directories:
|[Day13.hs](hs/src/Day13.hs)|[Day13.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day13.kt)|[day13.py](py/aoc2024/day13.py)|[day13.rs](rs/src/day13.rs)|
|[Day14.hs](hs/src/Day14.hs)|[Day14.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day14.kt)|[day14.py](py/aoc2024/day14.py)|[day14.rs](rs/src/day14.rs)|
|[Day15.hs](hs/src/Day15.hs)|[Day15.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day15.kt)|[day15.py](py/aoc2024/day15.py)|[day15.rs](rs/src/day15.rs)|
|[Day16.hs](hs/src/Day16.hs)|[Day16.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day16.kt)|[day16.py](py/aoc2024/day16.py)||
|[Day16.hs](hs/src/Day16.hs)|[Day16.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day16.kt)|[day16.py](py/aoc2024/day16.py)|[day16.rs](rs/src/day16.rs)|
|[Day17.hs](hs/src/Day17.hs)|[Day17.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day17.kt)|||
9 changes: 8 additions & 1 deletion rs/benches/criterion.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use aoc2024::{
day1, day10, day11, day12, day13, day14, day15, day2, day3, day4, day5, day6, day7, day8, day9,
day1, day10, day11, day12, day13, day14, day15, day16, day2, day3, day4, day5, day6, day7,
day8, day9,
};
use criterion::{black_box, Criterion};
use std::env;
Expand Down Expand Up @@ -106,6 +107,12 @@ fn aoc2024_bench(c: &mut Criterion) -> io::Result<()> {
g.bench_function("part 2", |b| b.iter(|| day15::part2(black_box(&data))));
g.finish();

let data = get_day_input(16)?;
let mut g = c.benchmark_group("day 16");
g.bench_function("part 1", |b| b.iter(|| day16::part1(black_box(&data))));
g.bench_function("part 2", |b| b.iter(|| day16::part2(black_box(&data))));
g.finish();

Ok(())
}

Expand Down
158 changes: 158 additions & 0 deletions rs/src/day16.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use if_chain::if_chain;
use std::cmp::Reverse;
use std::collections::{BTreeMap, BTreeSet, BinaryHeap};

type YX = (usize, usize);

fn parse(data: &str) -> Option<(BTreeSet<YX>, YX, YX)> {
let mut maze = BTreeSet::new();
let mut start = None;
let mut end = None;
for (y, line) in data.lines().enumerate() {
for (x, b) in line.bytes().enumerate() {
match b {
b'#' => {
maze.insert((y, x));
}
b'S' => {
if start.replace((y, x)).is_some() {
return None;
}
}
b'E' => {
if end.replace((y, x)).is_some() {
return None;
}
}
_ => {}
}
}
}
Some((maze, start?, end?))
}

pub fn part1(data: &str) -> Option<usize> {
let (maze, start, end) = parse(data)?;
let mut queue: BinaryHeap<_> = [(Reverse(0), start, (0, 1))].into();
let mut visited = BTreeSet::new();
while let Some((Reverse(score), (y, x), (dy, dx))) = queue.pop() {
if !visited.insert(((y, x), (dy, dx))) {
continue;
}
if (y, x) == end {
return Some(score);
}
for (score, (dy, dx)) in [
(score + 1, (dy, dx)),
(score + 1001, (-dx, dy)),
(score + 1001, (dx, -dy)),
] {
if_chain! {
if let Some(y) = y.checked_add_signed(dy);
if let Some(x) = x.checked_add_signed(dx);
if !maze.contains(&(y, x));
then {
queue.push((Reverse(score), (y, x), (dy, dx)));
}
}
}
}
None
}

pub fn part2(data: &str) -> Option<usize> {
let (maze, start, end) = parse(data)?;
let mut best = None;
let mut acc = BTreeSet::new();
let mut queue: BinaryHeap<(_, _, _, BTreeSet<_>)> =
[(Reverse(0), start, (0, 1), [start].into())].into();
let mut visited = BTreeMap::new();
while let Some((Reverse(score), (y, x), (dy, dx), mut path)) = queue.pop() {
if best.filter(|best| *best < score).is_some() {
break;
}
if *visited.entry(((y, x), (dy, dx))).or_insert(score) < score {
continue;
}
if (y, x) == end {
best = Some(score);
acc.append(&mut path);
continue;
}
for (score, (dy, dx)) in [
(score + 1, (dy, dx)),
(score + 1001, (-dx, dy)),
(score + 1001, (dx, -dy)),
] {
if_chain! {
if let Some(y) = y.checked_add_signed(dy);
if let Some(x) = x.checked_add_signed(dx);
if !maze.contains(&(y, x));
then {
let mut path = path.clone();
if path.insert((y, x)) {
queue.push((Reverse(score), (y, x), (dy, dx), path));
}
}
}
}
}
Some(acc.len())
}

#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use pretty_assertions::assert_eq;

static EXAMPLE_1: &str = indoc! {"
###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############
"};

static EXAMPLE_2: &str = indoc! {"
#################
#...#...#...#..E#
#.#.#.#.#.#.#.#.#
#.#.#.#...#...#.#
#.#.#.#.###.#.#.#
#...#.#.#.....#.#
#.#.#.#.#.#####.#
#.#...#.#.#.....#
#.#.#####.#.###.#
#.#.#.......#...#
#.#.###.#####.###
#.#.#...#.....#.#
#.#.#.#####.###.#
#.#.#.........#.#
#.#.#.#########.#
#S#.............#
#################
"};

#[test]
fn part1_examples() {
assert_eq!(Some(7036), part1(EXAMPLE_1));
assert_eq!(Some(11048), part1(EXAMPLE_2));
}

#[test]
fn part2_examples() {
assert_eq!(Some(45), part2(EXAMPLE_1));
assert_eq!(Some(64), part2(EXAMPLE_2));
}
}
1 change: 1 addition & 0 deletions rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod day12;
pub mod day13;
pub mod day14;
pub mod day15;
pub mod day16;
pub mod day2;
pub mod day3;
pub mod day4;
Expand Down
11 changes: 10 additions & 1 deletion rs/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::anyhow;
use aoc2024::{
day1, day10, day11, day12, day13, day14, day15, day2, day3, day4, day5, day6, day7, day8, day9,
day1, day10, day11, day12, day13, day14, day15, day16, day2, day3, day4, day5, day6, day7,
day8, day9,
};
use std::collections::HashSet;
use std::env;
Expand Down Expand Up @@ -139,5 +140,13 @@ fn main() -> anyhow::Result<()> {
println!();
}

if args.is_empty() || args.contains("16") {
println!("Day 16");
let data = get_day_input(16)?;
println!("{:?}", day16::part1(&data).ok_or(anyhow!("None"))?);
println!("{:?}", day16::part2(&data).ok_or(anyhow!("None"))?);
println!();
}

Ok(())
}

0 comments on commit 7281c10

Please sign in to comment.