From 603321cfbeb9c5e1d32a178a5d362645ba4e7ceb Mon Sep 17 00:00:00 2001 From: Xiretza Date: Mon, 18 Dec 2023 19:11:13 +0000 Subject: [PATCH] 2023 day16/rust: add solution --- 2023/day16/rust/Cargo.toml | 10 ++ 2023/day16/rust/src/main.rs | 179 ++++++++++++++++++++++++++++++++++++ Cargo.lock | 8 ++ Cargo.toml | 1 + 4 files changed, 198 insertions(+) create mode 100644 2023/day16/rust/Cargo.toml create mode 100644 2023/day16/rust/src/main.rs diff --git a/2023/day16/rust/Cargo.toml b/2023/day16/rust/Cargo.toml new file mode 100644 index 0000000..eece701 --- /dev/null +++ b/2023/day16/rust/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rust_2023_16" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aoc = { path = "../../../common/rust" } +enum-map = "2.7.3" diff --git a/2023/day16/rust/src/main.rs b/2023/day16/rust/src/main.rs new file mode 100644 index 0000000..adc1e92 --- /dev/null +++ b/2023/day16/rust/src/main.rs @@ -0,0 +1,179 @@ +#![warn(clippy::pedantic)] + +use std::{collections::HashMap, io::stdin}; + +use aoc::vec2::{Direction, Vec2}; +use enum_map::EnumMap; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Device { + MirrorTopLeft, + MirrorTopRight, + SplitterHorizontal, + SplitterVertical, +} + +impl Device { + pub fn get_outputs(self, input_direction: Direction) -> DeviceOutput { + use DeviceOutput::{One, Two}; + + match (self, input_direction) { + (Device::MirrorTopLeft, Direction::Up) | (Device::MirrorTopRight, Direction::Down) => { + One(Direction::Right) + } + + (Device::MirrorTopLeft, Direction::Down) | (Device::MirrorTopRight, Direction::Up) => { + One(Direction::Left) + } + (Device::MirrorTopLeft, Direction::Left) + | (Device::MirrorTopRight, Direction::Right) => One(Direction::Down), + (Device::MirrorTopLeft, Direction::Right) + | (Device::MirrorTopRight, Direction::Left) => One(Direction::Up), + + (Device::SplitterHorizontal, Direction::Up | Direction::Down) => { + Two(Direction::Left, Direction::Right) + } + (Device::SplitterVertical, Direction::Left | Direction::Right) => { + Two(Direction::Up, Direction::Down) + } + + (Device::SplitterHorizontal, Direction::Left | Direction::Right) + | (Device::SplitterVertical, Direction::Up | Direction::Down) => One(input_direction), + } + } + + pub fn parse(c: char) -> Option { + match c { + '/' => Some(Device::MirrorTopLeft), + '\\' => Some(Device::MirrorTopRight), + '-' => Some(Device::SplitterHorizontal), + '|' => Some(Device::SplitterVertical), + '.' => None, + _ => unreachable!(), + } + } +} + +#[derive(Debug, Clone, Copy)] +enum DeviceOutput { + One(Direction), + Two(Direction, Direction), +} + +#[derive(Debug, Clone, Copy)] +struct Field { + device: Option, + seen_beams: EnumMap, +} + +#[derive(Debug, Clone, Copy)] +struct PathPosition { + pos: Vec2, + direction: Direction, +} + +impl PathPosition { + pub fn advance_to(self, direction: Direction) -> Self { + Self { + pos: self.pos + Vec2::from(direction), + direction, + } + } +} + +fn follow_path(start: PathPosition, fields: &mut HashMap) { + let mut pos = start; + + loop { + let Some(field) = fields.get_mut(&pos.pos) else { + return; + }; + let seen = &mut field.seen_beams[pos.direction]; + + if *seen { + return; + } + + *seen = true; + + let outputs = match field.device { + Some(d) => d.get_outputs(pos.direction), + None => DeviceOutput::One(pos.direction), + }; + + match outputs { + DeviceOutput::One(d) => { + pos = pos.advance_to(d); + } + DeviceOutput::Two(d1, d2) => { + follow_path(pos.advance_to(d1), fields); + pos = pos.advance_to(d2); + } + } + } +} + +fn energize_fields(start: PathPosition, mut fields: HashMap) -> usize { + follow_path(start, &mut fields); + + fields + .values() + .filter(|field| field.seen_beams.into_iter().any(|(_dir, seen)| seen)) + .count() +} + +fn main() { + let lines: Vec<_> = stdin().lines().map(Result::unwrap).collect(); + let max_y = i32::try_from(lines.len()).unwrap() - 1; + let max_x = i32::try_from(lines[0].len()).unwrap() - 1; + + let fields: HashMap<_, _> = lines + .iter() + .enumerate() + .flat_map(|(row, l)| { + l.chars().enumerate().map(move |(col, c)| { + let pos = Vec2::new(col.try_into().unwrap(), max_y - i32::try_from(row).unwrap()); + let device = Device::parse(c); + ( + pos, + Field { + device, + seen_beams: EnumMap::default(), + }, + ) + }) + }) + .collect(); + + println!( + "{}", + energize_fields( + PathPosition { + pos: Vec2::new(0, max_y), + direction: Direction::Right, + }, + fields.clone() + ), + ); + + let hor_starts = (0..=max_x).flat_map(|x| { + [ + (Vec2::new(x, 0), Direction::Up), + (Vec2::new(x, max_y), Direction::Down), + ] + }); + let ver_starts = (0..=max_y).flat_map(|y| { + [ + (Vec2::new(0, y), Direction::Right), + (Vec2::new(max_x - 1, y), Direction::Left), + ] + }); + + let max_energized = hor_starts + .chain(ver_starts) + .map(|(pos, direction)| energize_fields(PathPosition { pos, direction }, fields.clone())) + .max() + .unwrap(); + + println!("{max_energized}"); +} diff --git a/Cargo.lock b/Cargo.lock index 32005b6..76a87d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -703,6 +703,14 @@ dependencies = [ "aoc", ] +[[package]] +name = "rust_2023_16" +version = "0.1.0" +dependencies = [ + "aoc", + "enum-map", +] + [[package]] name = "rustversion" version = "1.0.14" diff --git a/Cargo.toml b/Cargo.toml index 1a01457..e46a370 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,4 +37,5 @@ members = [ "2023/day9/rust", "2023/day11/rust", "2023/day15/rust", + "2023/day16/rust", ]