This commit is contained in:
Andrew Glaze 2023-12-19 10:57:36 -05:00
parent e0655bd700
commit 6e0f7b3c2f
5 changed files with 221 additions and 3 deletions

7
Cargo.lock generated
View File

@ -10,6 +10,7 @@ dependencies = [
"aoc-runner-derive", "aoc-runner-derive",
"array2d", "array2d",
"itertools", "itertools",
"prev-iter",
"rust-crypto", "rust-crypto",
"strum", "strum",
"strum_macros", "strum_macros",
@ -95,6 +96,12 @@ version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "prev-iter"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceacb352f798299b2044f4b9a894e8ef5d21115ccd293a323d44fc665132ec83"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.70" version = "1.0.70"

View File

@ -14,3 +14,4 @@ itertools = "0.12.0"
array2d = "0.3.0" array2d = "0.3.0"
strum = "0.25.0" strum = "0.25.0"
strum_macros = "0.25" strum_macros = "0.25"
prev-iter = "0.1.2"

View File

@ -34,8 +34,6 @@ fn part2(input: &Vec<String>) -> usize {
.try_into() .try_into()
.unwrap(); .unwrap();
for step in input { for step in input {
if let Some(dash) = step.find('-') { if let Some(dash) = step.find('-') {
let label = &step[0..dash]; let label = &step[0..dash];
let box_number = hash(label) as usize; let box_number = hash(label) as usize;

211
src/day16.rs Normal file
View File

@ -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<Vec<Tile>> {
let input = input.lines()
.map(|line| {
line.chars()
.map(|char| char.to_tile())
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
input
}
#[aoc(day16, part1)]
fn part1(map: &Vec<Vec<Tile>>) -> 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<Vec<Tile>>) -> 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::<HashSet<_>>().len()
}
fn do_one_step(cur: (Position, Direction), map: &Vec<Vec<Tile>>) -> (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<Direction>, map: &Vec<Vec<Tile>>) -> 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<Vec<Tile>>) -> 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::<Vec<_>>();
let mut left = (0..y_size).map(|y| (Position{ x: 0, y: y as i32}, Direction::Right)).collect::<Vec<_>>();
let mut bottom = (0..x_size).map(|x| (Position{ x: x as i32, y: (y_size - 1) as i32}, Direction::Down)).collect::<Vec<_>>();
let mut right = (0..y_size).map(|y| (Position{ x: (x_size - 1) as i32, y: y as i32}, Direction::Left)).collect::<Vec<_>>();
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::<Vec<_>>();
*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);
}
}

View File

@ -1,3 +1,4 @@
mod day16;
mod day15; mod day15;
mod day14; mod day14;
mod day13; mod day13;