Day 6
This commit is contained in:
parent
fe8a63cd27
commit
36008a5622
1 changed files with 195 additions and 0 deletions
195
src/bin/day6.rs
Normal file
195
src/bin/day6.rs
Normal 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(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue