From 9081354ce8389b227142ef1be7059279c613c4aa Mon Sep 17 00:00:00 2001 From: Tyler Hallada Date: Sun, 7 Dec 2025 02:02:43 -0500 Subject: [PATCH] Solve day 6 --- Cargo.toml | 2 +- README.md | 1 + src/day06/input/test1.txt | 4 + src/day06/mod.rs | 212 ++++++++++++++++++++++++++++++++++++++ src/days.rs | 1 + src/lib.rs | 1 + 6 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 src/day06/input/test1.txt create mode 100644 src/day06/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 647dd44..3ed4837 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ clap = { version = "4.5", features = ["derive"] } color-eyre = "0.6" itertools = "0.14" rayon = "1.11" -tracing = "0.1" +tracing = { version = "0.1", features = ["release_max_level_info"] } tracing-error = "0.2" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/README.md b/README.md index 0b38718..d58e9ce 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Timings are given as: [lower-bound **best-estimate** upper-bound] | 03 | [45.711 µs **45.937 µs** 46.177 µs] | [267.18 µs **267.95 µs** 268.75 µs] | | 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] | ## Profiling diff --git a/src/day06/input/test1.txt b/src/day06/input/test1.txt new file mode 100644 index 0000000..337b837 --- /dev/null +++ b/src/day06/input/test1.txt @@ -0,0 +1,4 @@ +123 328 51 64 + 45 64 387 23 + 6 98 215 314 +* + * + diff --git a/src/day06/mod.rs b/src/day06/mod.rs new file mode 100644 index 0000000..07e3ce5 --- /dev/null +++ b/src/day06/mod.rs @@ -0,0 +1,212 @@ +use std::str::FromStr; + +use color_eyre::{ + Result, + eyre::{Context, Error, OptionExt, eyre}, +}; +use itertools::Itertools; +use tracing::{debug, instrument}; + +pub const INPUT: &str = include_str!("input/input.txt"); + +#[derive(Debug, Clone, Copy)] +enum Operation { + Add, + Multiply, +} + +impl FromStr for Operation { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "+" => Ok(Operation::Add), + "*" => Ok(Operation::Multiply), + _ => Err(eyre!("invalid operation: {}", s)), + } + } +} + +impl Operation { + fn from_byte(byte: u8) -> Result { + match byte { + b'+' => Ok(Operation::Add), + b'*' => Ok(Operation::Multiply), + _ => Err(eyre!("invalid operation byte: {}", byte)), + } + } +} + +#[derive(Debug, Clone)] +struct Problem { + numbers: Vec, + operation: Operation, +} + +#[derive(Debug, Clone)] +struct CephalopodProblem { + columns: Vec>, + operation: Operation, + width: u8, +} + +#[instrument(skip(input))] +pub fn part1(input: &str) -> Result { + let mut lines = input.trim().lines(); + let first_numbers: Vec = lines + .next() + .ok_or_eyre("no first line in input")? + .split_whitespace() + .map(|s| s.parse::().context("parsing number")) + .collect::>>()?; + let mut problems = first_numbers + .into_iter() + .map(|n| Problem { + numbers: vec![n], + operation: Operation::Add, + }) + .collect::>(); + for line in lines { + let first_byte = line.bytes().nth(0).ok_or_eyre("empty line in input")?; + if matches!(first_byte, b'*' | b'+') { + for (index, op_result) in line.split_whitespace().map(|s| s.parse()).enumerate() { + let op = op_result?; + problems[index].operation = op; + } + } else { + for (index, num_result) in line.split_whitespace().map(|s| s.parse()).enumerate() { + let num = num_result?; + problems[index].numbers.push(num); + } + } + } + Ok(problems + .into_iter() + .map(|problem| match problem.operation { + Operation::Add => { + let sum = problem.numbers.iter().sum::(); + debug!("{} = {}", problem.numbers.iter().join(" + "), sum); + return sum; + } + Operation::Multiply => { + let product = problem.numbers.iter().product::(); + debug!("{} = {}", problem.numbers.iter().join(" * "), product); + return product; + } + }) + .sum()) +} + +#[instrument(skip(input))] +pub fn part2(input: &str) -> Result { + let mut lines = input.lines(); + let last_line = lines.next_back().ok_or_eyre("no last line in input")?; + let mut problems = Vec::new(); + let mut spaces: u8 = 1; + for byte in last_line.bytes().rev() { + if byte.is_ascii_whitespace() { + spaces += 1; + } else { + problems.push(CephalopodProblem { + columns: (0..spaces).map(|_| Vec::new()).collect(), + operation: Operation::from_byte(byte)?, + width: spaces, + }); + spaces = 0; + } + } + debug!(last_problem = ?problems[0]); + debug!(second_last_problem = ?problems[1]); + problems.reverse(); + for line in lines { + let bytes = line.as_bytes(); + let mut offset = 0; + for i in 0..problems.len() { + if offset + (problems[i].width as usize) > bytes.len() { + return Err(eyre!("line too short for problem columns")); + } + for (cell_col, &byte) in bytes[offset..offset + problems[i].width as usize] + .iter() + .enumerate() + { + if !byte.is_ascii_whitespace() { + problems[i].columns[cell_col].push(byte - b'0'); + } + } + offset += problems[i].width as usize + 1; + } + } + debug!(first_problem = ?problems[0]); + debug!(second_problem = ?problems[1]); + Ok(problems + .iter() + .map(|problem| match problem.operation { + Operation::Add => { + let sum: usize = problem + .columns + .iter() + .map(|col| { + col.iter() + .fold(0usize, |acc, &digit| acc * 10 + (digit as usize)) + }) + .sum(); + debug!( + "{} = {}", + problem + .columns + .iter() + .map(|col| { + col.iter() + .fold(0usize, |acc, &digit| acc * 10 + (digit as usize)) + .to_string() + }) + .join(" + "), + sum + ); + sum + } + Operation::Multiply => { + let product: usize = problem + .columns + .iter() + .map(|col| { + col.iter() + .fold(0usize, |acc, &digit| acc * 10 + (digit as usize)) + }) + .product(); + debug!( + "{} = {}", + problem + .columns + .iter() + .map(|col| { + col.iter() + .fold(0usize, |acc, &digit| acc * 10 + (digit as usize)) + .to_string() + }) + .join(" * "), + product + ); + product + } + }) + .sum()) +} + +#[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!(part1(TEST_INPUT1).unwrap(), 4277556); + } + + #[test] + fn test_part2() { + assert_eq!(part2(TEST_INPUT1).unwrap(), 3263827); + } +} diff --git a/src/days.rs b/src/days.rs index 5e689bc..7048262 100644 --- a/src/days.rs +++ b/src/days.rs @@ -10,6 +10,7 @@ macro_rules! all_days { 3 => day03, 4 => day04, 5 => day05, + 6 => day06, } }; } diff --git a/src/lib.rs b/src/lib.rs index 3393a19..2ba10f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,4 +3,5 @@ pub mod day02; pub mod day03; pub mod day04; pub mod day05; +pub mod day06; pub mod days;