This commit is contained in:
Quentin Boyer 2025-12-06 13:42:04 +01:00
parent fe8a63cd27
commit 36008a5622

195
src/bin/day6.rs Normal file
View file

@ -0,0 +1,195 @@
use std::time::Instant;
use aoc_2025::{load, print_res};
use bstr::{BStr, BString, ByteSlice};
use color_eyre::eyre::ContextCompat;
#[derive(Debug)]
pub enum Op {
Sum,
Product,
}
impl Op {
pub fn parse(v: &[u8]) -> color_eyre::Result<Self> {
match v {
b"+" => Ok(Op::Sum),
b"*" => Ok(Op::Product),
_ => color_eyre::eyre::bail!("Invalid operation: `{}`", v.as_bstr()),
}
}
}
type Parsed<'a> = Vec<&'a BStr>;
#[inline(never)]
pub fn parsing(input: &BString) -> color_eyre::Result<Parsed<'_>> {
Ok(input.lines().map(|a| a.as_bstr()).collect())
}
type Problem = (Op, Vec<u64>);
fn parse_part1(input: Parsed) -> color_eyre::Result<Vec<Problem>> {
let mut values = Vec::new();
for line in input {
if line.starts_with(b"+") || line.starts_with(b"*") {
return line
.fields()
.map(Op::parse)
.zip(values.into_iter())
.map(|(r, v)| Ok((r?, v)))
.collect();
}
let line = str::from_utf8(line)?;
if values.is_empty() {
values = line
.split_whitespace()
.map(|v| Ok::<_, color_eyre::Report>(vec![v.parse()?]))
.collect::<Result<_, _>>()?;
} else {
line.split_whitespace()
.zip(values.iter_mut())
.try_for_each(|(v, col)| {
col.push(v.parse()?);
Ok::<_, color_eyre::Report>(())
})?;
}
}
color_eyre::eyre::bail!("Operations not found");
}
fn grand_total(problems: &[Problem]) -> u64 {
let mut grand_total = 0;
for (op, values) in problems {
match op {
Op::Sum => grand_total += values.iter().sum::<u64>(),
Op::Product => grand_total += values.iter().product::<u64>(),
}
}
grand_total
}
fn parse_part2(input: Parsed) -> color_eyre::Result<Vec<Problem>> {
let num_cols = input
.first()
.with_context(|| "input is empty")?
.fields()
.count();
let mut col_widths = vec![0; num_cols];
for line in &input {
line.fields()
.zip(col_widths.iter_mut())
.for_each(|(v, max)| *max = (*max).max(v.len()));
}
let col_width_sum = col_widths.iter().sum::<usize>() + col_widths.len() - 1;
let mut iter = input.iter();
let mut cols: Vec<_> = col_widths
.iter()
.map(|&w| vec![None::<(u64, bool)>; w])
.collect();
loop {
let line = iter.next().with_context(|| "Unexpected end of input")?;
color_eyre::eyre::ensure!(
line.len() == col_width_sum,
"Line `{line}` is of an incorrect length (expected {col_width_sum})"
);
if line[0] == b'*' || line[0] == b'+' {
return line
.fields()
.map(Op::parse)
.zip(cols)
.map(|(op, v)| {
let op = op?;
let values = v
.into_iter()
.map(|n| match n {
Some((v, _)) => Ok(v),
None => color_eyre::eyre::bail!("No number found"),
})
.collect::<Result<_, _>>()?;
Ok((op, values))
})
.collect();
}
let mut line: &[u8] = line;
for (values, &width) in cols.iter_mut().zip(&col_widths) {
for (value, &digit) in values.iter_mut().zip(line.iter()) {
if digit == b' ' {
match value {
// Mark the number as finished
&mut Some((v, false)) => *value = Some((v, true)),
// Already finished, skip this number
Some((_, true)) => continue,
// Not yet started, skip this number
None => continue,
}
} else {
let digit = (digit - b'0') as u64;
match value {
Some((_, true)) => {
color_eyre::eyre::bail!("Number has re-started after it has ended")
}
Some((v, _)) => *v = *v * 10 + digit,
None => *value = Some((digit, false)),
}
}
}
if line.len() > width {
line = &line[width + 1..];
} else {
line = &[];
}
}
}
}
#[inline(never)]
pub fn part1(input: Parsed) {
let grand_total = grand_total(&parse_part1(input).unwrap());
print_res!("Grand total is: {grand_total}");
}
#[inline(never)]
pub fn part2(input: Parsed) {
let grand_total = grand_total(&parse_part2(input).unwrap());
print_res!("Grand total is: {grand_total}");
}
pub fn main() -> color_eyre::Result<()> {
let context = load()?;
let start = Instant::now();
let parsed = parsing(&context.input)?;
let elapsed = humantime::format_duration(start.elapsed());
let start = Instant::now();
if context.part == 1 {
part1(parsed);
} else {
part2(parsed);
}
let elapsed_part = humantime::format_duration(start.elapsed());
println!(" Parsing: {elapsed}");
println!(" Solving: {elapsed_part}");
Ok(())
}