From 5c5bb4f8d87826f34709202ced58a6722f90f8b7 Mon Sep 17 00:00:00 2001 From: Anthony Oteri Date: Mon, 18 Dec 2023 12:34:29 -0500 Subject: [PATCH] Day 16 - Part 1 Signed-off-by: Anthony Oteri --- day-16/Cargo.toml | 29 ++++ day-16/benches/benchmark.rs | 15 ++ day-16/input.txt | 110 +++++++++++++++ day-16/src/bin/part1.rs | 21 +++ day-16/src/bin/part2.rs | 21 +++ day-16/src/error.rs | 9 ++ day-16/src/lib.rs | 4 + day-16/src/part1.rs | 263 ++++++++++++++++++++++++++++++++++++ day-16/src/part2.rs | 18 +++ day-16/test-input.txt | 10 ++ 10 files changed, 500 insertions(+) create mode 100644 day-16/Cargo.toml create mode 100644 day-16/benches/benchmark.rs create mode 100644 day-16/input.txt create mode 100644 day-16/src/bin/part1.rs create mode 100644 day-16/src/bin/part2.rs create mode 100644 day-16/src/error.rs create mode 100644 day-16/src/lib.rs create mode 100644 day-16/src/part1.rs create mode 100644 day-16/src/part2.rs create mode 100644 day-16/test-input.txt diff --git a/day-16/Cargo.toml b/day-16/Cargo.toml new file mode 100644 index 0000000..fc16800 --- /dev/null +++ b/day-16/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "day-16" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itertools = { workspace = true } +nom = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +miette = { workspace = true } +thiserror = { workspace = true } +dhat = { workspace = true } +glam = { workspace = true } + +[dev-dependencies] +divan = { workspace = true } +env_logger = { workspace = true } +test-log = { workspace = true } + +[[bench]] +name = "day-16" +path = "benches/benchmark.rs" +harness = false + +[features] +dhat-heap = [] diff --git a/day-16/benches/benchmark.rs b/day-16/benches/benchmark.rs new file mode 100644 index 0000000..ddbec40 --- /dev/null +++ b/day-16/benches/benchmark.rs @@ -0,0 +1,15 @@ +use day_16::*; + +fn main() { + divan::main(); +} + +#[divan::bench] +fn part1() { + part1::process(divan::black_box(include_str!("../input.txt"))).unwrap(); +} + +#[divan::bench] +fn part2() { + part2::process(divan::black_box(include_str!("../input.txt"))).unwrap(); +} diff --git a/day-16/input.txt b/day-16/input.txt new file mode 100644 index 0000000..dc28324 --- /dev/null +++ b/day-16/input.txt @@ -0,0 +1,110 @@ +\.\............/........|............/.-....................|............|.......-..............//....../../.. +......\.-/..|...............-...-\......../.............-.../.............../........-................../../.. +.........\..\......................................................../\.......\.............................-. +....|.........-..................|..../....-......................................|.../...............|.../... +.....|...|..-....|................/............./......./............../|.......|.....|.................../... +.........-............../.|.-...\..........................|..................//............-\......|......... +.........|...................||.........-.|/.......---...-...........\........\.-...\..../......|............. +.......|-................................................................./........../..../................... +......-......|......././...............................\.....\.........|...........|..\-............|\........ +|...\.............................-...\\-.................|................................-.........|........ +...\....|.|........................-.........................\..|............................-....|........... +\.........|..|./....................../..........-......|...-....................\....-../...\.-.....|/....... +......\./................|...................-....../..................................|.......-....-......... +......-.........................................|....\..................../-./-.......|........./............. +................|...|...\....\....-....//........./...../.....-..............|\.|..\.........\......./\....\.\ +.\...|.....|......./....-...........|.............-..\...-..-....................|.../.....-...........--..... +......................................-................../..............\.\\........../.../................\.. +.-..........|..|......................|....|.-|.....................-..../............|......./..../..\/.-.\.. +..\..\.............../-......-................-......................................\....../......|.......... +.........\.....\.......................................-../........./-....-.................\....../..../..... +......-..\..-.............|...................|....../.......\../........../.....-............................ +.............|../.\...\....|...............|.........../..|-....../........../..../|.|.\........|........\..|. +..\............|...../...................\......................./...-.......-..........................-..... +....|......................|......../.|.\.../......|...|..-.....................|................../..\....... +.....................................................-................/\..-....|..........................-.\. +................../...\..........\...........\............................................./.../.........|.|.. +.....-......................................./....-........|.-........................|.........\.....\....... +...............\/\..........|.|................................|.\......\......\............|...-............. +..................................\.......-.........|-........../...........\\..\.|../......|........\..|..... +..............-...........\.....\\./......................./...-...........-..........................././.... +...-..............-..........................\..........|...........-/........../.../...|..................... +...\..............-......./-...........|............./.......-./........-..-....|......./\.-.......\./........ +.........|......././..\.-......./............-....-..................|....-......../........-.........\....... +....|.........................../....................-..................-...\|............................|... +.-........-...........|..............................|.........................................\......-......- +............./......../..-....\...............................././.......|................................|... +.|\../......-./........./...............-./..............\../..............|....|........./................... +..................................................|............................-.............................. +.............-...................\.......|........|.............../......-....|......../.../.-...\|........... +.................../..............-|........................|.............\..\.........................././... +...../.........../..-.......|........../.......|........................./......|......./..........|.......... +../...-........../...........................-.|..\../..................\.....-...........|................... +.........\.......-.......-.\.|......|.......||......|.\....-...-.................-..........-..\.............. +................................\./.......\/............-................./.....\......../.................... +/...........-......|..|....../.....\.........|.....................................\...................|....-. +...........\.............../.......\......................................................................\..- +....../...................-..................|-\......................-..................\.................... +/............./...........|.....|............/.................................-....................-......\|. +...............\..|..............|............\../.../..........................\\................|......./... +....--/............|......|......-........-...../........./.........../-..\..|........-....................... +|.-....-.........................|../................\................../......\..../.\..|...../.../|......... +....................................../.........\........-....................../.-..-.\.....././.../.....\..\ +......\...../............./................-...........\.................../.....-../......-.................. +....../............../...........|......|..................................|...................|.............. +......-../.../.|.........././..........\.......\........./......|..............\.................-.........../ +...../...|........//...../.................\................../.\.............-............/.........../...... +..............-.....|................\................/.........|.....\.......\....................\.....\.... +..........|..........-.../..................\.-.......................................\..-.........|.......... +....|..................-.....|..................|...........-......./.....-......../...|........\....\-.|..... +.......................-......./......................./.-.-.........|.....|.................|................ +...|................/......-.....|....-..-.........-|....|..................../.../....-......\.......-......\ +............-.............................-...................../..............|.|.......-.................... +-....-..............|................./......../.\............./../............../..-...-...........|......... +.|............|..........-................./................|.....................|./....\......./.....\...... +..........|\.|...................-.............\......|..-..\.......\..........\........|......-.../........\. +\.......\....................\..............|................................|..............-.../............. +......../-....................|...............-........................\................\..............-...... +...|..-.............//.-.............../........-........../..|.............\|../....................-........ +..../.\.-.-|.....\.-|..../.....\..|.........../........................-.....................-..........|..... +...../-..........\.........-....................\.............................\............|-..\..........|... +...-...../....................../-......\...|.....-........................../.|.........|.......\..\.....\../ +...................\...../......................./...-.....\.-.........-...../.|...................|......./.. +../.....|..|..\.........|.../.................../..-............\../........\.....\.....................\..... +.........................../...\.|........\...||..........\......\.-....|.........\-..\....................... +../.........|............................/../-||..............|-........\............\....\.\................| +../....|....................|.....|-.......|.................../...\.-.\.............-.................-...... +....|..............................................................-|......................................... +..................\-.\/.........|../|....../|..\...-...\.........|/............./...\...\..................... +.\.........-../....|-......................./................/\..\........................................./.. +./......................-...............|...............|................-.......................\............ +................\..........././............................-....\/..........-.../../....|.........-....|...../ +.-..-..|\..\/........\./....|.......................\.........../.....|......./-.-..../..../.................. +\.....|........................../.|...../.-...............|.../\...|.........|.\|....../.-......|............ +.........-|............./..../.\.|//.......-....-...........\.........-..-.........\....\..........|.......... +....................|.......-../.....\-..........\................-..................\.-........|............. +.....|.\-........................./........................................../.......-.\....-......../........ +...................|..............\..|/................................|/..../........../..|.................| +.......\...|./......../..........-............-....|...........-/.\...-......./....\......./........\....../.. +...\............\..........\../................./....-......|..........................................\...... +............|......|......../.............\...|.....................\.\\...................................... +-.-.-......\.......|..........-.|.....|\....\/..............-/..........|............|......./.........-...... +......................../...|................./......\............../..............-/.......\.....\\.......... +...|.|.-..-./..................../................................-....\|........./...-..|........\......-.... +....\....-../.........|....|........-./..\../...|...../-.|.|....\................................./...|......- +..............\../..............|.|................\......./.............../.\......|.....|..\...../....../... +............|....\......|........|.......\.................................................../.-.............. +.|............|...\....................../............................................\.......|.-.-........... +.....-.\|......\.............|............|....-.........\....|.............................../....\....-.|... +............|./.........................\...|..............................\...|..|..............|...-........ +.-.....|..................|....-.............-..\..-...............-....-..................-.................. +..\......-.-./..................................-............\........./........-.-.............-............- +..../.\--...............//..................................||................\.................|......|...... +..|./...\......................|.........../........\.....-....-.................................../-....\.... +.........-........\..............................|.......|......../...........|....../......|..\.|..../....... +....../....-....|\............|.....|..\../||....................................\.....................-...... +.....\.....--...././......................-........\\............-...............\....-....|................./ +........./........../...-................/...........-............................................-........./. +.........-..-...|/..\.........................\............\..-........-.........-...............\......|./.-. +|......................-.....-..-.........../-..................|.....|.........\./........./...-/............ +..............-...........|-....-.../.............-........./...../.................../.......--.|....-..../.. \ No newline at end of file diff --git a/day-16/src/bin/part1.rs b/day-16/src/bin/part1.rs new file mode 100644 index 0000000..42d4f77 --- /dev/null +++ b/day-16/src/bin/part1.rs @@ -0,0 +1,21 @@ +use day_16::part1::process; +use miette::Context; + +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + +#[tracing::instrument] +fn main() -> miette::Result<()> { + #[cfg(feature = "dhat-heap")] + let _profiler = dhat::Profiler::new_heap(); + + #[cfg(not(feature = "dhat-heap"))] + tracing_subscriber::fmt::init(); + + let file = include_str!("../../input.txt"); + let result = process(file).context("process part 1")?; + + println!("{}", result); + Ok(()) +} diff --git a/day-16/src/bin/part2.rs b/day-16/src/bin/part2.rs new file mode 100644 index 0000000..b9c5477 --- /dev/null +++ b/day-16/src/bin/part2.rs @@ -0,0 +1,21 @@ +use day_16::part2::process; +use miette::Context; + +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + +#[tracing::instrument] +fn main() -> miette::Result<()> { + #[cfg(feature = "dhat-heap")] + let _profiler = dhat::Profiler::new_heap(); + + #[cfg(not(feature = "dhat-heap"))] + tracing_subscriber::fmt::init(); + + let file = include_str!("../../input.txt"); + let result = process(file).context("process part 1")?; + + println!("{}", result); + Ok(()) +} diff --git a/day-16/src/error.rs b/day-16/src/error.rs new file mode 100644 index 0000000..e08a17f --- /dev/null +++ b/day-16/src/error.rs @@ -0,0 +1,9 @@ +use miette::Diagnostic; +use thiserror::Error; + +#[derive(Error, Diagnostic, Debug)] +pub enum AocError { + #[error(transparent)] + #[diagnostic(code(aoc::io_error))] + IoError(#[from] std::io::Error), +} diff --git a/day-16/src/lib.rs b/day-16/src/lib.rs new file mode 100644 index 0000000..681e54b --- /dev/null +++ b/day-16/src/lib.rs @@ -0,0 +1,4 @@ +pub mod error; + +pub mod part1; +pub mod part2; diff --git a/day-16/src/part1.rs b/day-16/src/part1.rs new file mode 100644 index 0000000..e012a03 --- /dev/null +++ b/day-16/src/part1.rs @@ -0,0 +1,263 @@ +use crate::error::AocError; +use glam::IVec2; +use itertools::Itertools; +use nom::bytes::complete::is_a; +use nom::character::complete::line_ending; +use nom::multi::separated_list1; +use nom::IResult; +use std::collections::HashMap; +use std::collections::HashSet; + +#[derive(Debug, Clone)] +enum Tile { + Empty, + RightMirror, + LeftMirror, + HorizontalSplit, + VerticalSplit, +} +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +enum Direction { + North, + South, + East, + West, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +struct Beam { + direction: Direction, + position: IVec2, +} + +fn parse(input: &str) -> IResult<&str, HashMap> { + let (input, rows) = separated_list1(line_ending, is_a(r".|\/-"))(input)?; + + let grid = rows + .into_iter() + .enumerate() + .flat_map(|(y, row)| { + row.chars().enumerate().map(move |(x, tile)| { + let position = IVec2::new(x as i32, y as i32); + let tile = match tile { + '.' => Tile::Empty, + '|' => Tile::VerticalSplit, + '/' => Tile::RightMirror, + '\\' => Tile::LeftMirror, + '-' => Tile::HorizontalSplit, + _ => unreachable!(), + }; + (position, tile) + }) + }) + .collect::>(); + + Ok((input, grid)) +} + +fn step(grid: &HashMap, beams: &Vec) -> Vec { + beams + .iter() + .flat_map(|beam| { + let position = match beam.direction { + Direction::North => beam.position + IVec2::new(0, -1), + Direction::South => beam.position + IVec2::new(0, 1), + Direction::East => beam.position + IVec2::new(1, 0), + Direction::West => beam.position + IVec2::new(-1, 0), + }; + + match grid.get(&position) { + Some(Tile::VerticalSplit) => match beam.direction { + Direction::North | Direction::South => vec![Beam { + direction: beam.direction.clone(), + position, + }], + Direction::East | Direction::West => vec![ + Beam { + direction: Direction::North, + position, + }, + Beam { + direction: Direction::South, + position, + }, + ], + }, + + Some(Tile::HorizontalSplit) => match beam.direction { + Direction::East | Direction::West => vec![Beam { + direction: beam.direction.clone(), + position, + }], + Direction::North | Direction::South => vec![ + Beam { + direction: Direction::East, + position, + }, + Beam { + direction: Direction::West, + position, + }, + ], + }, + Some(Tile::RightMirror) => match beam.direction { + Direction::North => vec![Beam { + direction: Direction::East, + position, + }], + Direction::South => vec![Beam { + direction: Direction::West, + position, + }], + Direction::East => vec![Beam { + direction: Direction::North, + position, + }], + Direction::West => vec![Beam { + direction: Direction::South, + position, + }], + }, + Some(Tile::LeftMirror) => match beam.direction { + Direction::North => vec![Beam { + direction: Direction::West, + position, + }], + Direction::South => vec![Beam { + direction: Direction::East, + position, + }], + Direction::East => vec![Beam { + direction: Direction::South, + position, + }], + Direction::West => vec![Beam { + direction: Direction::North, + position, + }], + }, + Some(Tile::Empty) | None => vec![Beam { + direction: beam.direction.clone(), + position, + }], + } + .to_vec() + }) + .collect_vec() +} + +fn check_bounds(_grid: &HashMap, beams: &Vec, boundary: &IVec2) -> Vec { + beams + .iter() + .filter(|beam| { + beam.position.x <= boundary.x + && beam.position.y <= boundary.y + && beam.position.x >= 0 + && beam.position.y >= 0 + }) + .cloned() + .collect_vec() +} + +fn check_history(beams: &Vec, visited: &HashSet) -> Vec { + beams + .iter() + .filter(|beam| !visited.contains(beam)) + .cloned() + .collect_vec() +} + +fn visualize(grid: &HashMap, visited: &HashSet) { + let boundary = grid.keys().fold(IVec2::new(0, 0), |max, position| { + IVec2::new(max.x.max(position.x), max.y.max(position.y)) + }); + + for y in 0..=boundary.y { + for x in 0..=boundary.x { + let position = IVec2::new(x, y); + if visited.contains(&Beam { + direction: Direction::North, + position, + }) { + print!("^"); + } else if visited.contains(&Beam { + direction: Direction::South, + position, + }) { + print!("v"); + } else if visited.contains(&Beam { + direction: Direction::East, + position, + }) { + print!(">"); + } else if visited.contains(&Beam { + direction: Direction::West, + position, + }) { + print!("<"); + } else if let Some(tile) = grid.get(&position) { + match tile { + Tile::Empty => print!("."), + Tile::VerticalSplit => print!("|"), + Tile::HorizontalSplit => print!("-"), + Tile::RightMirror => print!("/"), + Tile::LeftMirror => print!("\\"), + } + } else { + print!(" "); + } + } + println!(); + } +} + +#[tracing::instrument] +pub fn process(input: &str) -> miette::Result { + let (input, grid) = parse(input).unwrap(); + debug_assert!(input.is_empty(), "Not all input was parsed"); + + let boundary = grid.keys().fold(IVec2::new(0, 0), |max, position| { + IVec2::new(max.x.max(position.x), max.y.max(position.y)) + }); + + let mut visited: HashSet = HashSet::new(); + + let mut beams: Vec = vec![Beam { + direction: Direction::East, + position: IVec2::new(-1, 0), + }]; + //visited.extend(beams.iter().cloned()); + + loop { + beams = step(&grid, &beams); + beams = check_bounds(&grid, &beams, &boundary); + beams = check_history(&beams, &visited); + visited.extend(beams.iter().cloned()); + + if beams.is_empty() { + break; + } + } + + #[cfg(test)] + visualize(&grid, &visited); + + let energized = visited + .iter() + .map(|node| node.position) + .collect::>(); + + Ok(energized.len() as u64) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test_log::test] + fn test_process() -> miette::Result<()> { + let input = include_str!("../test-input.txt"); + assert_eq!(46, process(input)?); + Ok(()) + } +} diff --git a/day-16/src/part2.rs b/day-16/src/part2.rs new file mode 100644 index 0000000..1ff3976 --- /dev/null +++ b/day-16/src/part2.rs @@ -0,0 +1,18 @@ +use crate::error::AocError; + +#[tracing::instrument] +pub fn process(input: &str) -> miette::Result { + Ok(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test_log::test] + fn test_process() -> miette::Result<()> { + let input = include_str!("../test-input.txt"); + assert_eq!(0, process(input)?); + Ok(()) + } +} diff --git a/day-16/test-input.txt b/day-16/test-input.txt new file mode 100644 index 0000000..c78b2e7 --- /dev/null +++ b/day-16/test-input.txt @@ -0,0 +1,10 @@ +.|...\.... +|.-.\..... +.....|-... +........|. +.......... +.........\ +..../.\\.. +.-.-/..|.. +.|....-|.\ +..//.|.... \ No newline at end of file