Skip to content

Commit 6f32d2b

Browse files
committed
feat: add 16(b)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
1 parent f5b6abf commit 6f32d2b

File tree

2 files changed

+258
-2
lines changed

2 files changed

+258
-2
lines changed

src/bin/16.rs

+256
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/*!
2+
# 2023 Day 16 - Beams and mirrors
3+
4+
<https://adventofcode.com/2023/day/16>
5+
6+
This is currently a bit messy. Some features in Grid would make itgit much nicer.
7+
I might rewrite this to use some custom traits to make it simpler.
8+
9+
*/
10+
11+
use std::collections::HashMap;
12+
13+
use derive_more::Constructor;
14+
use grid::Grid;
15+
use strum::{EnumIter, EnumString, IntoEnumIterator};
16+
17+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, EnumIter)]
18+
enum Direction {
19+
Up,
20+
Down,
21+
Left,
22+
Right,
23+
}
24+
25+
#[derive(Debug, Constructor, Copy, Clone)]
26+
struct Position {
27+
row: usize,
28+
col: usize,
29+
max_row: usize,
30+
max_col: usize,
31+
}
32+
33+
impl Position {
34+
fn step(&self, dir: Direction) -> Option<Self> {
35+
let newpos = match dir {
36+
Direction::Up => Self::new(
37+
self.row.checked_sub(1)?,
38+
self.col,
39+
self.max_row,
40+
self.max_col,
41+
),
42+
Direction::Down => Self::new(self.row + 1, self.col, self.max_row, self.max_col),
43+
Direction::Left => Self::new(
44+
self.row,
45+
self.col.checked_sub(1)?,
46+
self.max_row,
47+
self.max_col,
48+
),
49+
Direction::Right => Self::new(self.row, self.col + 1, self.max_row, self.max_col),
50+
};
51+
if newpos.row >= self.max_row || newpos.col >= self.max_col {
52+
None
53+
} else {
54+
Some(newpos)
55+
}
56+
}
57+
}
58+
59+
#[allow(clippy::from_over_into)]
60+
impl Into<(usize, usize)> for Position {
61+
fn into(self) -> (usize, usize) {
62+
(self.row, self.col)
63+
}
64+
}
65+
66+
#[derive(Debug)]
67+
enum Next {
68+
Single(Direction),
69+
Double((Direction, Direction)),
70+
}
71+
72+
#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumString)]
73+
enum Tiles {
74+
#[strum(serialize = ".")]
75+
Empty,
76+
77+
#[strum(serialize = "|")]
78+
VerticalSplitter,
79+
80+
#[strum(serialize = "-")]
81+
HorizontalSplitter,
82+
83+
#[strum(serialize = "/")]
84+
ForwardDiagonal,
85+
86+
#[strum(serialize = r"\")]
87+
BackwardDiagonal,
88+
}
89+
90+
impl Tiles {
91+
const fn next(self, dir: Direction) -> Next {
92+
use Direction::{Down, Left, Right, Up};
93+
use Next::{Double, Single};
94+
use Tiles::{
95+
BackwardDiagonal, Empty, ForwardDiagonal, HorizontalSplitter, VerticalSplitter,
96+
};
97+
98+
match (self, dir) {
99+
(Empty, _) | (VerticalSplitter, Up | Down) | (HorizontalSplitter, Left | Right) => {
100+
Single(dir)
101+
}
102+
(VerticalSplitter, Left | Right) => Double((Up, Down)),
103+
(HorizontalSplitter, Up | Down) => Double((Left, Right)),
104+
(ForwardDiagonal, Up) | (BackwardDiagonal, Down) => Single(Right),
105+
(ForwardDiagonal, Right) | (BackwardDiagonal, Left) => Single(Up),
106+
(ForwardDiagonal, Down) | (BackwardDiagonal, Up) => Single(Left),
107+
(ForwardDiagonal, Left) | (BackwardDiagonal, Right) => Single(Down),
108+
}
109+
}
110+
}
111+
112+
fn parse(text: &str) -> Grid<Tiles> {
113+
let grid: Vec<Tiles> = text
114+
.lines()
115+
.flat_map(|line| {
116+
line.chars()
117+
.map(|x| x.to_string().parse::<Tiles>().unwrap())
118+
})
119+
.collect();
120+
121+
Grid::from_vec(grid, text.lines().next().unwrap().len())
122+
}
123+
124+
fn path(
125+
grid: &Grid<Tiles>,
126+
pos: &Position,
127+
dir: Direction,
128+
energized: &mut HashMap<Direction, Grid<bool>>,
129+
) {
130+
use Next::{Double, Single};
131+
let mut pos = *pos;
132+
let mut dir = dir;
133+
// loop here
134+
loop {
135+
if energized[&dir][pos.into()] {
136+
break;
137+
}
138+
energized.get_mut(&dir).unwrap()[pos.into()] = true;
139+
let tile: Tiles = grid[pos.into()];
140+
match tile.next(dir) {
141+
Single(d) => {
142+
if let Some(newpos) = pos.step(d) {
143+
dir = d;
144+
pos = newpos;
145+
} else {
146+
break;
147+
}
148+
}
149+
Double((d1, d2)) => {
150+
if let Some(newpos) = pos.step(d2) {
151+
path(grid, &newpos, d2, energized);
152+
}
153+
if let Some(newpos) = pos.step(d1) {
154+
dir = d1;
155+
pos = newpos;
156+
} else {
157+
break;
158+
}
159+
}
160+
}
161+
}
162+
}
163+
164+
fn count_energize(grid: &Grid<Tiles>, pos: &Position, dir: Direction) -> usize {
165+
let mut energized = HashMap::new();
166+
for dir in Direction::iter() {
167+
energized.insert(dir, Grid::new(grid.rows(), grid.cols()));
168+
}
169+
path(grid, pos, dir, &mut energized);
170+
171+
let mut total = 0;
172+
for y in 0..grid.rows() {
173+
for x in 0..grid.cols() {
174+
let count = usize::from(energized[&Direction::Up][(y, x)])
175+
+ usize::from(energized[&Direction::Down][(y, x)])
176+
+ usize::from(energized[&Direction::Left][(y, x)])
177+
+ usize::from(energized[&Direction::Right][(y, x)]);
178+
total += usize::from(count > 0);
179+
}
180+
}
181+
total
182+
}
183+
184+
fn compute1(text: &str) -> usize {
185+
let grid = parse(text);
186+
let pos = Position::new(0, 0, grid.rows(), grid.cols());
187+
let dir = Direction::Right;
188+
count_energize(&grid, &pos, dir)
189+
}
190+
191+
fn compute2(text: &str) -> usize {
192+
let grid = parse(text);
193+
let mut max = 0;
194+
for i in 0..(grid.rows()) {
195+
max = max.max(count_energize(
196+
&grid,
197+
&Position::new(i, 0, grid.rows(), grid.cols()),
198+
Direction::Right,
199+
));
200+
max = max.max(count_energize(
201+
&grid,
202+
&Position::new(i, grid.rows() - 1, grid.rows(), grid.cols()),
203+
Direction::Left,
204+
));
205+
}
206+
for i in 0..(grid.cols()) {
207+
max = max.max(count_energize(
208+
&grid,
209+
&Position::new(0, i, grid.rows(), grid.cols()),
210+
Direction::Down,
211+
));
212+
max = max.max(count_energize(
213+
&grid,
214+
&Position::new(grid.cols() - 1, i, grid.rows(), grid.cols()),
215+
Direction::Up,
216+
));
217+
}
218+
max
219+
}
220+
221+
fn main() {
222+
let text = std::fs::read_to_string("input/16.txt").unwrap();
223+
let result = compute1(&text);
224+
println!("First = {result}");
225+
226+
let result = compute2(&text);
227+
println!("Second = {result}");
228+
}
229+
230+
#[cfg(test)]
231+
mod tests {
232+
use super::*;
233+
234+
const INPUT: &str = r".|...\....
235+
|.-.\.....
236+
.....|-...
237+
........|.
238+
..........
239+
.........\
240+
..../.\\..
241+
.-.-/..|..
242+
.|....-|.\
243+
..//.|....";
244+
245+
#[test]
246+
fn test_first() {
247+
let result = compute1(INPUT);
248+
assert_eq!(result, 46);
249+
}
250+
251+
#[test]
252+
fn test_second() {
253+
let result = compute2(INPUT);
254+
assert_eq!(result, 51);
255+
}
256+
}

template/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn main() {
2020
let result = compute1(&text);
2121
println!("First = {result}");
2222

23-
let second_result = compute2(&text);
23+
let result = compute2(&text);
2424
println!("Second = {result}");
2525
}
2626

@@ -38,7 +38,7 @@ mod tests {
3838
}
3939

4040
#[test]
41-
fn test_first() {
41+
fn test_second() {
4242
let result = compute2(INPUT);
4343
assert_eq!(result, 0);
4444
}

0 commit comments

Comments
 (0)