From 673c0eadb1fb761692fe4adad68d8dc78a3cf746 Mon Sep 17 00:00:00 2001 From: Adrian Groh Date: Mon, 16 Dec 2024 13:45:51 +0100 Subject: [PATCH] Add day16 --- day16/Cargo.lock | 126 ++++++++++++++++++++++++++++ day16/Cargo.toml | 8 ++ day16/src/main.rs | 210 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 344 insertions(+) create mode 100644 day16/Cargo.lock create mode 100644 day16/Cargo.toml create mode 100644 day16/src/main.rs diff --git a/day16/Cargo.lock b/day16/Cargo.lock new file mode 100644 index 0000000..59f6e5d --- /dev/null +++ b/day16/Cargo.lock @@ -0,0 +1,126 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "day16" +version = "0.1.0" +dependencies = [ + "ahash", + "glam", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glam" +version = "0.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677" + +[[package]] +name = "libc" +version = "0.2.168" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/day16/Cargo.toml b/day16/Cargo.toml new file mode 100644 index 0000000..459ef46 --- /dev/null +++ b/day16/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day16" +version = "0.1.0" +edition = "2021" + +[dependencies] +ahash = "0.8.11" +glam = "0.29.2" diff --git a/day16/src/main.rs b/day16/src/main.rs new file mode 100644 index 0000000..8dd2137 --- /dev/null +++ b/day16/src/main.rs @@ -0,0 +1,210 @@ +use std::{cmp::Reverse, collections::BinaryHeap}; + +use ahash::{AHashMap, AHashSet}; +use glam::IVec2; + +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +enum Tile { + Empty, + Wall, + Start, + End, +} +use Tile::*; + +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +enum Direction { + North, + South, + West, + East, +} +use Direction::*; + +impl Direction { + fn as_vec(&self) -> IVec2 { + match self { + North => IVec2::new(-1, 0), + South => IVec2::new(1, 0), + West => IVec2::new(0, -1), + East => IVec2::new(0, 1), + } + } + + fn turn_left(&self) -> Self { + match self { + North => West, + South => East, + West => South, + East => North, + } + } + + fn turn_right(&self) -> Self { + match self { + North => East, + South => West, + West => North, + East => South, + } + } + + fn reverse(&self) -> Self { + match self { + North => South, + South => North, + West => East, + East => West, + } + } +} + +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +struct Node { + score: u32, + direction: Direction, + position: IVec2, +} + +impl Ord for Node { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.score.cmp(&other.score) + } +} + +impl PartialOrd for Node { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +fn parse(input: &str) -> (Vec>, IVec2, IVec2) { + let mut start_pos = IVec2::new(0, 0); + let mut end_pos = IVec2::new(0, 0); + ( + input + .lines() + .enumerate() + .map(|(l_idx, l)| { + l.chars() + .enumerate() + .map(|(c_idx, c)| match c { + '#' => Wall, + 'S' => { + start_pos.x = l_idx as i32; + start_pos.y = c_idx as i32; + Start + } + 'E' => { + end_pos.x = l_idx as i32; + end_pos.y = c_idx as i32; + End + } + _ => Empty, + }) + .collect() + }) + .collect(), + start_pos, + end_pos, + ) +} + +fn get_scores(grid: &[Vec], start_pos: &IVec2) -> AHashMap<(IVec2, Direction), u32> { + let mut to_visit = BinaryHeap::new(); + let mut visited = AHashMap::new(); + let start_node = Node { + score: 0, + direction: East, + position: *start_pos, + }; + to_visit.push(Reverse(start_node)); + + while let Some(curr_node) = to_visit.pop() { + if visited + .get(&(curr_node.0.position, curr_node.0.direction)) + .map_or(false, |s| s < &curr_node.0.score) + { + continue; + } + visited.insert( + (curr_node.0.position, curr_node.0.direction), + curr_node.0.score, + ); + + let pos_in_front = curr_node.0.position + curr_node.0.direction.as_vec(); + if grid[pos_in_front.x as usize][pos_in_front.y as usize] != Wall { + to_visit.push(Reverse(Node { + score: curr_node.0.score + 1, + direction: curr_node.0.direction, + position: curr_node.0.position + curr_node.0.direction.as_vec(), + })); + } + + to_visit.push(Reverse(Node { + score: curr_node.0.score + 1000, + direction: curr_node.0.direction.turn_left(), + position: curr_node.0.position, + })); + + to_visit.push(Reverse(Node { + score: curr_node.0.score + 1000, + direction: curr_node.0.direction.turn_right(), + position: curr_node.0.position, + })); + } + visited +} + +fn part1(scores: &AHashMap<(IVec2, Direction), u32>, end_pos: &IVec2) -> u32 { + *scores + .iter() + .filter(|((p, _), _)| p == end_pos) + .map(|(_, v)| v) + .min() + .unwrap() +} + +fn part2(scores: &AHashMap<(IVec2, Direction), u32>, end_pos: &IVec2) -> usize { + let shortest_path_len = part1(scores, end_pos); + let mut seen: AHashSet = AHashSet::new(); + let mut to_visit: Vec<_> = scores + .iter() + .filter(|((p, _), val)| val == &&shortest_path_len && p == end_pos) + .map(|(x, y)| (*x, *y)) + .collect(); + + while let Some(curr) = to_visit.pop() { + // this is very ugly + + seen.insert(curr.0 .0); + + let pos_behind = curr.0 .0 + curr.0 .1.reverse().as_vec(); + if scores + .get(&(pos_behind, curr.0 .1)) + .map_or(false, |v| v == &(curr.1 - 1)) + { + to_visit.push(((pos_behind, curr.0 .1), (curr.1 - 1))); + } + if scores + .get(&(curr.0 .0, curr.0 .1.turn_left())) + .map_or(false, |v| v == &(curr.1 - 1000)) + { + to_visit.push(((curr.0 .0, curr.0 .1.turn_left()), (curr.1 - 1000))); + } + if scores + .get(&(curr.0 .0, curr.0 .1.turn_right())) + .map_or(false, |v| v == &(curr.1 - 1000)) + { + to_visit.push(((curr.0 .0, curr.0 .1.turn_right()), (curr.1 - 1000))); + } + } + seen.len() +} + +fn main() { + let (grid, start_pos, end_pos) = parse(include_str!("../input.txt")); + let scores = get_scores(&grid, &start_pos); + println!("{}", part1(&scores, &end_pos)); + println!("{}", part2(&scores, &end_pos)); +}