Solve day 7

This commit is contained in:
2025-12-08 21:41:08 -05:00
parent 9081354ce8
commit 6d0abb8f6a
5 changed files with 199 additions and 0 deletions

View File

@@ -38,6 +38,7 @@ Timings are given as: [lower-bound **best-estimate** upper-bound]
| 04 | [143.40 µs **144.00 µs** 144.73 µs] | [1.6165 ms **1.6258 ms** 1.6355 ms] |
| 05 | [187.25 µs **188.93 µs** 190.74 µs] | [63.809 µs **64.204 µs** 64.606 µs] |
| 06 | [128.44 µs **129.44 µs** 130.52 µs] | [165.05 µs **165.70 µs** 166.36 µs] |
| 07 | [83.803 µs **84.601 µs** 85.435 µs] | [81.456 µs **82.360 µs** 83.386 µs] |
## Profiling

16
src/day07/input/test1.txt Normal file
View File

@@ -0,0 +1,16 @@
.......S.......
...............
.......^.......
...............
......^.^......
...............
.....^.^.^.....
...............
....^.^...^....
...............
...^.^...^.^...
...............
..^...^.....^..
...............
.^.^.^.^.^...^.
...............

180
src/day07/mod.rs Normal file
View File

@@ -0,0 +1,180 @@
use std::{
fmt::{Display, Formatter},
str::FromStr,
};
use color_eyre::{Result, eyre::eyre};
use tracing::{debug, instrument};
pub const INPUT: &str = include_str!("input/input.txt");
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Cell {
Source,
Splitter,
Beam(usize),
Empty,
}
impl FromStr for Cell {
type Err = color_eyre::Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"S" => Ok(Cell::Source),
"^" => Ok(Cell::Splitter),
"|" => Ok(Cell::Beam(1)),
"." => Ok(Cell::Empty),
_ => Err(eyre!("Invalid cell character: {}", s)),
}
}
}
impl Cell {
fn from_byte(b: u8) -> Result<Self, color_eyre::Report> {
match b {
b'S' => Ok(Cell::Source),
b'^' => Ok(Cell::Splitter),
b'|' => Ok(Cell::Beam(1)),
b'.' => Ok(Cell::Empty),
_ => Err(eyre!("Invalid cell byte: {}", b)),
}
}
}
struct Grid<const R: usize, const C: usize> {
cells: [[Cell; C]; R],
splits: usize,
}
impl<const R: usize, const C: usize> FromStr for Grid<R, C> {
type Err = color_eyre::Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut cells = [[Cell::Empty; C]; R];
for (row, line) in s.lines().enumerate() {
for (col, byte) in line.bytes().enumerate() {
cells[row][col] = Cell::from_byte(byte)?;
}
}
Ok(Grid { cells, splits: 0 })
}
}
impl<const R: usize, const C: usize> Display for Grid<R, C> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for row in 0..R {
for col in 0..C {
let symbol = match self.cells[row][col] {
Cell::Source => 'S',
Cell::Splitter => '^',
Cell::Beam(t) => (b'0' + t as u8) as char,
Cell::Empty => '.',
};
write!(f, "{}", symbol)?;
}
writeln!(f)?;
}
Ok(())
}
}
impl<const R: usize, const C: usize> Grid<R, C> {
fn emit_beam(&mut self) {
for row in 1..R {
for col in 0..C {
if matches!(self.cells[row][col], Cell::Beam(_)) {
// already filled by a splitter to the left
continue;
}
if matches!(self.cells[row - 1][col], Cell::Source | Cell::Beam(_)) {
let timelines = match self.cells[row - 1][col] {
Cell::Source => 1,
Cell::Beam(t) => t,
_ => unreachable!(),
};
if self.cells[row][col] == Cell::Splitter {
// assumption: splitter always has empty cells on left and right
let left = self.cells[row][col - 1];
let left_timelines = match self.cells[row - 1][col - 1] {
Cell::Source => 1,
Cell::Beam(t) => t,
_ => 0,
};
if let Cell::Beam(left_t) = left {
self.cells[row][col - 1] = Cell::Beam(timelines + left_t);
} else {
self.cells[row][col - 1] = Cell::Beam(timelines + left_timelines);
}
let right = self.cells[row][col + 1];
let right_timelines = match self.cells[row - 1][col + 1] {
Cell::Source => 1,
Cell::Beam(t) => t,
_ => 0,
};
if let Cell::Beam(right_t) = right {
self.cells[row][col + 1] = Cell::Beam(timelines + right_t);
} else {
self.cells[row][col + 1] = Cell::Beam(timelines + right_timelines);
}
self.splits += 1;
} else {
self.cells[row][col] = Cell::Beam(timelines);
}
}
}
debug!("After row {}:\n{}", row, self);
}
}
}
fn solve_part1<const R: usize, const C: usize>(input: &str) -> Result<usize> {
let mut grid = Grid::<R, C>::from_str(input)?;
grid.emit_beam();
Ok(grid.splits)
}
#[instrument(skip(input))]
pub fn part1(input: &str) -> Result<usize> {
solve_part1::<142, 141>(input)
}
fn solve_part2<const R: usize, const C: usize>(input: &str) -> Result<usize> {
let mut grid = Grid::<R, C>::from_str(input)?;
grid.emit_beam();
Ok(grid.cells[R - 1]
.into_iter()
.map(|c| {
if let Cell::Beam(timelines) = c {
timelines
} else {
0
}
})
.sum::<usize>())
}
#[instrument(skip(input))]
pub fn part2(input: &str) -> Result<usize> {
solve_part2::<142, 141>(input)
}
#[cfg(test)]
mod tests {
use super::*;
use test_log::test;
const TEST_INPUT1: &str = include_str!("input/test1.txt");
#[test]
fn test_part1() {
assert_eq!(solve_part1::<16, 15>(TEST_INPUT1).unwrap(), 21);
}
#[test]
fn test_part2() {
assert_eq!(solve_part2::<16, 15>(TEST_INPUT1).unwrap(), 40);
}
}

View File

@@ -11,6 +11,7 @@ macro_rules! all_days {
4 => day04,
5 => day05,
6 => day06,
7 => day07,
}
};
}

View File

@@ -4,4 +4,5 @@ pub mod day03;
pub mod day04;
pub mod day05;
pub mod day06;
pub mod day07;
pub mod days;