From 6e0f7b3c2f39a02d17f741b044f3df8838a68ea3 Mon Sep 17 00:00:00 2001 From: Andrew Glaze Date: Tue, 19 Dec 2023 10:57:36 -0500 Subject: [PATCH] day16 --- Cargo.lock | 7 ++ Cargo.toml | 3 +- src/day15.rs | 2 - src/day16.rs | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 5 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 src/day16.rs diff --git a/Cargo.lock b/Cargo.lock index 389a5fc..67c96ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,6 +10,7 @@ dependencies = [ "aoc-runner-derive", "array2d", "itertools", + "prev-iter", "rust-crypto", "strum", "strum_macros", @@ -95,6 +96,12 @@ version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "prev-iter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceacb352f798299b2044f4b9a894e8ef5d21115ccd293a323d44fc665132ec83" + [[package]] name = "proc-macro2" version = "1.0.70" diff --git a/Cargo.toml b/Cargo.toml index 737fb76..9b4e87d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,5 @@ aoc-runner-derive = "0.3.0" itertools = "0.12.0" array2d = "0.3.0" strum = "0.25.0" -strum_macros = "0.25" \ No newline at end of file +strum_macros = "0.25" +prev-iter = "0.1.2" diff --git a/src/day15.rs b/src/day15.rs index c7440d3..cfe692e 100644 --- a/src/day15.rs +++ b/src/day15.rs @@ -34,8 +34,6 @@ fn part2(input: &Vec) -> usize { .try_into() .unwrap(); for step in input { - - if let Some(dash) = step.find('-') { let label = &step[0..dash]; let box_number = hash(label) as usize; diff --git a/src/day16.rs b/src/day16.rs new file mode 100644 index 0000000..6b7bf93 --- /dev/null +++ b/src/day16.rs @@ -0,0 +1,211 @@ +use std::collections::{HashSet, VecDeque}; +use prev_iter::PrevPeekable; + +use aoc_runner_derive::{aoc, aoc_generator}; +use strum_macros::Display; + +#[derive(Debug, Clone, Copy)] +enum Tile { + None, + MirrorLeft, + MirrorRight, + SplitterVert, + SplitterHori +} + +trait TileConvertable { + fn to_tile(&self) -> Tile; +} + +impl TileConvertable for char { + fn to_tile(&self) -> Tile { + match self { + '.' => Tile::None, + '/' => Tile::MirrorLeft, + '\\' => Tile::MirrorRight, + '|' => Tile::SplitterVert, + '-' => Tile::SplitterHori, + _ => panic!("invalid tile char: {} AHHH", self) + } + } +} + + +#[aoc_generator(day16)] +fn parse(input: &str) -> Vec> { + let input = input.lines() + .map(|line| { + line.chars() + .map(|char| char.to_tile()) + .collect::>() + }) + .collect::>(); + + input +} + +#[aoc(day16, part1)] +fn part1(map: &Vec>) -> usize { + let start = (Position{ x:0 , y:0}, Direction::Right); + + trace_beams(start, map) +} + +fn trace_beams((start_pos, start_dir): (Position, Direction), map: &Vec>) -> usize { + let mut in_progress = VecDeque::from([(start_pos, start_dir)]); + let mut seen: HashSet<(Position, Direction)> = HashSet::new(); + + while !in_progress.is_empty() { + let mut cur = in_progress.pop_front(); + while &cur != &None { + seen.insert(cur.clone().unwrap()); + let (next, new_beam) = do_one_step(cur.clone().unwrap(), map); + if let Some(next) = next { + if !seen.contains(&next) { + cur = Some(next); + } else { + cur = None; + } + } else { + cur = None + } + + if let Some(new_beam) = new_beam { + in_progress.push_back(new_beam); + } + } + } + + seen.iter().map(|x| x.0).collect::>().len() +} + +fn do_one_step(cur: (Position, Direction), map: &Vec>) -> (Option<(Position, Direction)>, Option<(Position, Direction)>) { + let tile = map[cur.0.y as usize][cur.0.x as usize]; + match tile { + Tile::None => (move_and_turn(&cur, None, map), None), + Tile::SplitterHori if {cur.1.is_hori()} => (move_and_turn(&cur, None, map), None), + Tile::SplitterVert if {cur.1.is_vert()} => (move_and_turn(&cur, None, map), None), + Tile::MirrorRight => (move_and_turn(&cur, if cur.1.is_hori() {Some(Direction::Right)} else {Some(Direction::Left)}, map), None), + Tile::MirrorLeft => (move_and_turn(&cur, if cur.1.is_hori() {Some(Direction::Left)} else {Some(Direction::Right)}, map), None), + Tile::SplitterVert => (move_and_turn(&cur, Some(Direction::Left), map), move_and_turn(&cur, Some(Direction::Right), map)), + Tile::SplitterHori => (move_and_turn(&cur, Some(Direction::Right), map), move_and_turn(&cur, Some(Direction::Left), map)) + } +} + +fn move_and_turn(cur: &(Position, Direction), turn: Option, map: &Vec>) -> Option<(Position, Direction)> { + let next_dir = if let Some(turn) = turn {cur.1.turn(turn)} else {cur.1}; + let next_pos = cur.0.move_pos(next_dir); + + if let Some(line) = map.get(next_pos.y as usize) { + if let Some(_) = line.get(next_pos.x as usize) { + return Some((next_pos, next_dir)); + } + } + + None +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +struct Position { + x: i32, + y: i32 +} + +impl Position { + fn move_pos(&self, dir: Direction) -> Position { + let (dx, dy) = match dir { + Direction::Up => (0, -1), + Direction::Down => (0, 1), + Direction::Left => (-1, 0), + Direction::Right => (1, 0), + }; + + return Position { x: self.x + dx, y: self.y + dy}; + } +} + +#[derive(Debug, Hash, PartialEq, Eq, Display, Copy, Clone)] +enum Direction { + Up, + Down, + Left, + Right +} + +impl Direction { + fn turn(&self, turn: Direction) -> Direction { + const TURN_ORDER: [Direction; 4] = [Direction::Up, Direction::Right, Direction::Down, Direction::Left]; + let mut iter = PrevPeekable::new(TURN_ORDER.iter().cycle()); + _ = iter.find(|dir| dir == &self).unwrap(); + + if turn == Direction::Left && *self == Direction::Up { + return Direction::Left; + } + + let index = match turn { + Direction::Left => iter.prev(), + Direction::Right => iter.next(), + _ => panic!("Cannot turn {}", turn) + }; + + return *index.clone().unwrap(); + } + + fn is_hori(&self) -> bool { + [Direction::Right, Direction::Left].contains(self) + } + + fn is_vert(&self) -> bool { + [Direction::Up, Direction::Down].contains(self) + } +} + + + +#[aoc(day16, part2)] +fn part2(map: &Vec>) -> usize { + let x_size = map[0].len(); + let y_size = map.len(); + let mut edges = vec![]; + + let mut top = (0..x_size).map(|x| (Position{ x: x as i32, y: 0}, Direction::Down)).collect::>(); + let mut left = (0..y_size).map(|y| (Position{ x: 0, y: y as i32}, Direction::Right)).collect::>(); + let mut bottom = (0..x_size).map(|x| (Position{ x: x as i32, y: (y_size - 1) as i32}, Direction::Down)).collect::>(); + let mut right = (0..y_size).map(|y| (Position{ x: (x_size - 1) as i32, y: y as i32}, Direction::Left)).collect::>(); + + edges.append(&mut top); + edges.append(&mut left); + edges.append(&mut bottom); + edges.append(&mut right); + + let total = edges.iter().map(|x| trace_beams(*x, map)).collect::>(); + + *total.iter().max().unwrap() +} + + +#[cfg(test)] +mod tests { + use super::*; + + const EX: &str = r".|...\.... +|.-.\..... +.....|-... +........|. +.......... +.........\ +..../.\\.. +.-.-/..|.. +.|....-|.\ +..//.|...."; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 46); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX)), 51); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 792deb9..bb09eb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod day16; mod day15; mod day14; mod day13;