Solve day 6

This commit is contained in:
2025-12-07 02:02:43 -05:00
parent 7a3463e8cc
commit 9081354ce8
6 changed files with 220 additions and 1 deletions

View File

@@ -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"] }

View File

@@ -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

View File

@@ -0,0 +1,4 @@
123 328 51 64
45 64 387 23
6 98 215 314
* + * +

212
src/day06/mod.rs Normal file
View File

@@ -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<Self, Self::Err> {
match s {
"+" => Ok(Operation::Add),
"*" => Ok(Operation::Multiply),
_ => Err(eyre!("invalid operation: {}", s)),
}
}
}
impl Operation {
fn from_byte(byte: u8) -> Result<Self> {
match byte {
b'+' => Ok(Operation::Add),
b'*' => Ok(Operation::Multiply),
_ => Err(eyre!("invalid operation byte: {}", byte)),
}
}
}
#[derive(Debug, Clone)]
struct Problem {
numbers: Vec<u64>,
operation: Operation,
}
#[derive(Debug, Clone)]
struct CephalopodProblem {
columns: Vec<Vec<u8>>,
operation: Operation,
width: u8,
}
#[instrument(skip(input))]
pub fn part1(input: &str) -> Result<u64> {
let mut lines = input.trim().lines();
let first_numbers: Vec<u64> = lines
.next()
.ok_or_eyre("no first line in input")?
.split_whitespace()
.map(|s| s.parse::<u64>().context("parsing number"))
.collect::<Result<Vec<u64>>>()?;
let mut problems = first_numbers
.into_iter()
.map(|n| Problem {
numbers: vec![n],
operation: Operation::Add,
})
.collect::<Vec<Problem>>();
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::<u64>();
debug!("{} = {}", problem.numbers.iter().join(" + "), sum);
return sum;
}
Operation::Multiply => {
let product = problem.numbers.iter().product::<u64>();
debug!("{} = {}", problem.numbers.iter().join(" * "), product);
return product;
}
})
.sum())
}
#[instrument(skip(input))]
pub fn part2(input: &str) -> Result<usize> {
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);
}
}

View File

@@ -10,6 +10,7 @@ macro_rules! all_days {
3 => day03,
4 => day04,
5 => day05,
6 => day06,
}
};
}

View File

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