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",
|
||||
"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"
|
||||
|
@ -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"
|
||||
strum_macros = "0.25"
|
||||
prev-iter = "0.1.2"
|
||||
|
@ -34,8 +34,6 @@ fn part2(input: &Vec<String>) -> 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;
|
||||
|
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 day14;
|
||||
mod day13;
|
||||
|
Loading…
Reference in New Issue
Block a user