Skip to content

Commit

Permalink
[2024] Day 20 initial solution
Browse files Browse the repository at this point in the history
  • Loading branch information
connorslade committed Dec 20, 2024
1 parent 04fd360 commit 91fe2c4
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Thank you to [Eric Wastl](http://was.tl) for running this incredible yearly even
- [Day 17: Chronospatial Computer](aoc_2024/src/day_17.rs)
- [Day 18: RAM Run](aoc_2024/src/day_18.rs)
- [Day 19: Linen Layout](aoc_2024/src/day_19.rs)
- [Day 20: Race Condition](aoc_2024/src/day_20.rs)
<!-- MARKER -->

## [2023](https://adventofcode.com/2023) [![aoc_2023](https://github.com/connorslade/advent-of-code/actions/workflows/aoc_2023.yml/badge.svg)](https://github.com/connorslade/advent-of-code/actions/workflows/aoc_2023.yml)
Expand Down
121 changes: 121 additions & 0 deletions aoc_2024/src/day_20.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use std::{collections::VecDeque, convert::identity, u32};

use aoc_lib::{direction::cardinal::Direction, matrix::Grid};
use common::{solution, Answer};
use itertools::Itertools;
use nd_vec::{vector, Vec2};

solution!("Race Condition", 20);

fn part_a(input: &str) -> Answer {
Problem::parse(input).solve(2).into()
}

fn part_b(input: &str) -> Answer {
Problem::parse(input).solve(20).into()
}

struct Problem {
board: Grid<char>,
start: Vec2<usize>,
end: Vec2<usize>,
}

impl Problem {
fn parse(input: &str) -> Self {
let board = Grid::parse(input, identity);

let start = board.find('S').unwrap();
let end = board.find('E').unwrap();

Self { board, start, end }
}

fn solve(&self, max_skip: i32) -> u32 {
let (sc, ec) = (self.cost_map(self.start), self.cost_map(self.end));
let base_cost = sc[self.end];

let mut out = 0;

for (pos, tile) in self.board.iter() {
if *tile == '#' || sc[pos] == u32::MAX {
continue;
}

for (x, y) in (-max_skip..=max_skip).cartesian_product(-max_skip..=max_skip) {
let offset = vector!(x, y);
let dist = offset.manhattan_distance(&Vec2::zero());
if dist > max_skip {
continue;
}

let end = pos.try_cast::<i32>().unwrap() + offset;
if !self.board.contains(end) || self.board[end] == '#' || ec[end] == u32::MAX {
continue;
}

let cost = sc[pos] + ec[end] + dist as u32;
out += (cost + 100 <= base_cost) as u32;
}
}

out
}

fn cost_map(&self, start: Vec2<usize>) -> Grid<u32> {
let mut costs = Grid::new(self.board.size, u32::MAX);
let mut queue = VecDeque::new();
queue.push_back((start, 0));

while let Some((pos, dist)) = queue.pop_front() {
if costs[pos] != u32::MAX {
continue;
}

costs[pos] = dist;
for dir in Direction::ALL {
let next = dir.wrapping_advance(pos);
if let Some(tile) = self.board.get(next) {
if matches!(tile, '.' | 'E') {
queue.push_back((next, dist + 1));
}
}
}
}

costs
}
}

#[cfg(test)]
mod test {
use indoc::indoc;

const CASE: &str = indoc! {"
###############
#...#...#.....#
#.#.#.#.#.###.#
#S#...#.#.#...#
#######.#.#.###
#######.#.#...#
#######.#.###.#
###..E#...#...#
###.#######.###
#...###...#...#
#.#####.#.###.#
#.#...#.#.#...#
#.#.#.#.#.#.###
#...#...#...###
###############
"};

#[test]
fn part_a() {
assert_eq!(super::part_a(CASE), 44.into());
}

#[test]
fn part_b() {
assert_eq!(super::part_b(CASE), ().into());
}
}
2 changes: 2 additions & 0 deletions aoc_2024/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod day_16;
mod day_17;
mod day_18;
mod day_19;
mod day_20;
// [import_marker]

pub const SOLUTIONS: &[Solution] = &[
Expand All @@ -41,5 +42,6 @@ pub const SOLUTIONS: &[Solution] = &[
day_17::SOLUTION,
day_18::SOLUTION,
day_19::SOLUTION,
day_20::SOLUTION,
// [list_marker]
];

0 comments on commit 91fe2c4

Please sign in to comment.