From e60f6effa3615290a5afb1696d891bc9cfa1cb47 Mon Sep 17 00:00:00 2001 From: Keenan Tims Date: Fri, 20 Dec 2024 20:56:07 -0800 Subject: [PATCH] day20: part 1 solution --- .aoc_tiles/tiles/2024/20.png | Bin 4402 -> 7925 bytes README.md | 5 +- src/day20.rs | 165 +++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 src/day20.rs diff --git a/.aoc_tiles/tiles/2024/20.png b/.aoc_tiles/tiles/2024/20.png index 2b1c6787ba6db0a725095fa41e4f1fb254032a52..852cf09c6fbef1cf7dfcec6b87f0f5e82e43c5a8 100644 GIT binary patch literal 7925 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 Myt-Vaj9K{q07qAFd;kCd literal 4402 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

- 2024 - 38 ⭐ - Rust + 2024 - 39 ⭐ - Rust

@@ -59,4 +59,7 @@ + + + diff --git a/src/day20.rs b/src/day20.rs new file mode 100644 index 0000000..b680046 --- /dev/null +++ b/src/day20.rs @@ -0,0 +1,165 @@ +use std::{ + cmp::Reverse, + collections::{BinaryHeap, VecDeque}, +}; + +use aoc_runner_derive::aoc; +use grid::Grid; +use rustc_hash::{FxHashMap, FxHashSet}; + +trait PathTrack { + const DOES_WORK: bool = true; + fn new() -> Self; + fn push(&mut self, pos: (i64, i64)); + fn finalize(&mut self) {} +} + +struct RaceTrack { + map: Grid, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +struct State { + pos: (i64, i64), + cost: usize, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +struct CheatState { + s: State, + p: Vec<(i64, i64)>, +} + +const DIRECTIONS: [(i64, i64); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)]; + +impl RaceTrack { + fn valid_moves<'a>(&'a self, CheatState { s: state, p }: &'a CheatState) -> impl Iterator + 'a { + let mut new_path = p.clone(); + new_path.push(state.pos); + DIRECTIONS + .iter() + .map(|dir| (state.pos.0 + dir.0, state.pos.1 + dir.1)) + .filter_map(move |pos| match &self.map.get(&pos) { + Some(b'.') | Some(b'S') | Some(b'E') => Some(CheatState { + p: new_path.clone(), + s: State { + pos, + cost: state.cost + 1, + }, + }), + _ => None, + }) + } + fn path_costs(&self, start: (i64, i64), goal: (i64, i64)) -> (Vec<(i64, i64)>, Grid>) { + let mut queue = VecDeque::new(); + let mut visited = self.map.same_shape(None); + + let start_state = CheatState { + s: State { + pos: start, + cost: 0usize, + }, + p: Vec::new(), + }; + visited.set(&start, Some(0)); + queue.push_back(start_state); + + while let Some(state) = queue.pop_front() { + if state.s.pos == goal { + let mut final_path = state.p; + final_path.push(goal); + return (final_path, visited); + } + + let moves = self.valid_moves(&state); + for new_state in moves { + if visited.get(&new_state.s.pos).unwrap().is_some() { + continue; + } + visited.set(&new_state.s.pos, Some(new_state.s.cost)); + queue.push_back(new_state); + } + } + panic!("no path"); + } + + fn find_cheats( + &self, + path: &Vec<(i64, i64)>, + costs: &Grid>, + min: usize, + ) -> Vec<((i64, i64), (i64, i64), usize)> { + let mut cheats = Vec::new(); + for pos in path { + let local_cost = costs.get(pos).unwrap().unwrap(); + for ofs in DIRECTIONS { + let cheat_start = (pos.0 + ofs.0, pos.1 + ofs.1); + let cheat_exit = (pos.0 + ofs.0 * 2, pos.1 + ofs.1 * 2); + if let Some(Some(cheat_cost)) = costs.get(&cheat_exit) { + if *cheat_cost > local_cost + 2 { + let cheat_savings = cheat_cost - local_cost - 2; + if cheat_savings >= min { + cheats.push((cheat_start, cheat_exit, cheat_savings)); + } + } + } + } + } + cheats + } +} + +fn parse(input: &str) -> RaceTrack { + let map = input.as_bytes().into(); + RaceTrack { map } +} + +fn part1_impl(input: &str, cheat_min: usize) -> i64 { + let track = parse(input); + let start = track.map.find(&b'S').unwrap(); + let goal = track.map.find(&b'E').unwrap(); + let (best_path, costs) = track.path_costs(start, goal); + let cheats = track.find_cheats(&best_path, &costs, cheat_min); + + cheats.len() as i64 +} + +#[aoc(day20, part1)] +pub fn part1(input: &str) -> i64 { + part1_impl(input, 100) +} + +#[aoc(day20, part2)] +pub fn part2(input: &str) -> i64 { + todo!() +} + +#[cfg(test)] +mod tests { + use super::*; + const EXAMPLE: &str = "############### +#...#...#.....# +#.#.#.#.#.###.# +#S#...#.#.#...# +#######.#.#.### +#######.#.#...# +#######.#.###.# +###..E#...#...# +###.#######.### +#...###...#...# +#.#####.#.###.# +#.#...#.#.#...# +#.#.#.#.#.#.### +#...#...#...### +###############"; + + #[test] + fn part1_example() { + assert_eq!(part1_impl(EXAMPLE, 0), 44); + } + + #[test] + fn part2_example() { + assert_eq!(part2(EXAMPLE), 0); + } +} diff --git a/src/lib.rs b/src/lib.rs index 2495903..0d05b93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ pub mod day17; pub mod day18; pub mod day19; pub mod day2; +pub mod day20; pub mod day3; pub mod day4; pub mod day5;