day16
This commit is contained in:
parent
e0655bd700
commit
6e0f7b3c2f
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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
211
src/day16.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
mod day16;
|
||||||
mod day15;
|
mod day15;
|
||||||
mod day14;
|
mod day14;
|
||||||
mod day13;
|
mod day13;
|
||||||
|
Loading…
Reference in New Issue
Block a user