118 lines
2.8 KiB
Rust
118 lines
2.8 KiB
Rust
|
#![feature(test, try_blocks)]
|
||
|
extern crate test;
|
||
|
use aoc2023::{
|
||
|
boilerplate,
|
||
|
common::*,
|
||
|
direction::Direction::{self, *},
|
||
|
};
|
||
|
use fnv::FnvHashSet;
|
||
|
use std::mem::transmute;
|
||
|
use Tile::*;
|
||
|
|
||
|
const DAY: usize = 16;
|
||
|
type Parsed<'a> = Vec<&'a [Tile]>;
|
||
|
|
||
|
#[repr(u8)]
|
||
|
#[allow(dead_code)]
|
||
|
#[derive(Debug, PartialEq, Eq)]
|
||
|
enum Tile {
|
||
|
Empty = b'.',
|
||
|
HSplit = b'|',
|
||
|
VSplit = b'-',
|
||
|
Angle1 = b'/',
|
||
|
Angle2 = b'\\',
|
||
|
}
|
||
|
|
||
|
fn parse_input(raw: &str) -> Parsed {
|
||
|
raw.lines().map(|l| unsafe { transmute(l) }).collect()
|
||
|
}
|
||
|
|
||
|
fn part1(grid: &Parsed) -> usize {
|
||
|
let mut points = FnvHashSet::default();
|
||
|
points.insert((0, 0));
|
||
|
let mut known = FnvHashSet::default();
|
||
|
// my real input starts with a mirror,
|
||
|
// and I don’t want to rewrite the code to not blindly make the first step.
|
||
|
energized(grid, &mut points, &mut known, 0, 0, if grid[0][0] == Empty { Right } else { Down });
|
||
|
points.len()
|
||
|
}
|
||
|
|
||
|
fn angle1(dir: Direction) -> Direction {
|
||
|
match dir {
|
||
|
Up => Right,
|
||
|
Right => Up,
|
||
|
Down => Left,
|
||
|
Left => Down,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn angle2(dir: Direction) -> Direction {
|
||
|
match dir {
|
||
|
Up => Left,
|
||
|
Left => Up,
|
||
|
Down => Right,
|
||
|
Right => Down,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn energized(
|
||
|
grid: &Parsed,
|
||
|
points: &mut FnvHashSet<(usize, usize)>,
|
||
|
known: &mut FnvHashSet<(usize, usize, Direction)>,
|
||
|
mut x: usize,
|
||
|
mut y: usize,
|
||
|
dir: Direction,
|
||
|
) {
|
||
|
if !known.insert((x, y, dir)) {
|
||
|
return;
|
||
|
}
|
||
|
if let Some(tile) = try {
|
||
|
y = y.checked_sub((dir == Up) as usize)? + (dir == Down) as usize;
|
||
|
x = x.checked_sub((dir == Left) as usize)? + (dir == Right) as usize;
|
||
|
grid.get(y)?.get(x)?
|
||
|
} {
|
||
|
points.insert((x, y));
|
||
|
match (tile, dir) {
|
||
|
(Empty, _) => {
|
||
|
energized(grid, points, known, x, y, dir);
|
||
|
}
|
||
|
(Angle1, _) => energized(grid, points, known, x, y, angle1(dir)),
|
||
|
(Angle2, _) => energized(grid, points, known, x, y, angle2(dir)),
|
||
|
(HSplit, Up | Down) => energized(grid, points, known, x, y, dir),
|
||
|
(HSplit, Left | Right) => {
|
||
|
energized(grid, points, known, x, y, Up);
|
||
|
energized(grid, points, known, x, y, Down);
|
||
|
}
|
||
|
(VSplit, Up | Down) => {
|
||
|
energized(grid, points, known, x, y, Right);
|
||
|
energized(grid, points, known, x, y, Left);
|
||
|
}
|
||
|
(VSplit, Left | Right) => energized(grid, points, known, x, y, dir),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn part2(parsed: &Parsed) -> usize {
|
||
|
unimplemented!()
|
||
|
}
|
||
|
|
||
|
boilerplate! {
|
||
|
TEST_INPUT == r".|...\....
|
||
|
|.-.\.....
|
||
|
.....|-...
|
||
|
........|.
|
||
|
..........
|
||
|
.........\
|
||
|
..../.\\..
|
||
|
.-.-/..|..
|
||
|
.|....-|.\
|
||
|
..//.|...."
|
||
|
for tests: {
|
||
|
part1: { TEST_INPUT => 46 },
|
||
|
part2: { TEST_INPUT => 0 },
|
||
|
},
|
||
|
bench1 == 7517,
|
||
|
bench2 == 0,
|
||
|
bench_parse: Vec::len => 110,
|
||
|
}
|