diff --git a/src/bin/day5.rs b/src/bin/day5.rs new file mode 100644 index 0000000..d87ff1a --- /dev/null +++ b/src/bin/day5.rs @@ -0,0 +1,157 @@ +use std::{collections::HashMap, time::Instant}; + +use aoc_2024::{load, print_res}; +use bstr::BString; +use color_eyre::eyre::Context; + +#[derive(Debug)] +pub struct PageGraph { + successors: HashMap>, + predecesors: HashMap>, +} + +type Parsed = (PageGraph, Vec>); + +#[inline(never)] +pub fn parsing(input: &BString) -> color_eyre::Result { + let input = core::str::from_utf8(input)?; + let mut graph = PageGraph { + successors: HashMap::new(), + predecesors: HashMap::new(), + }; + let Some((succesors, sequences)) = input.split_once("\n\n") else { + color_eyre::eyre::bail!("Missing '\\n\\n'"); + }; + + for succesor in succesors.lines() { + let Some((first, second)) = succesor.split_once('|') else { + color_eyre::eyre::bail!("Missing '|' in '{succesor}'"); + }; + + let first: usize = first + .parse() + .with_context(|| format!("Could not parse first ({first})"))?; + + let second: usize = second + .parse() + .with_context(|| format!("Could not parse second ({second})"))?; + + graph.successors.entry(first).or_default().push(second); + graph.predecesors.entry(second).or_default().push(first); + } + + let sequences = sequences + .lines() + .map(|seq| { + seq.split(',') + .map(|n| n.parse::()) + .collect::, _>>() + }) + .collect::, _>>()?; + + Ok((graph, sequences)) +} + +#[inline(never)] +pub fn part1((graph, sequences): Parsed) { + let mut sum = 0; + + 'seq: for seq in sequences { + let mut seen = Vec::new(); + + for n in &seq { + seen.push(n); + + if graph + .successors + .get(n) + .map(|x| -> &[_] { x }) + .unwrap_or_else(|| &[]) + .iter() + .any(|s| seen.contains(&s)) + { + continue 'seq; + } + } + + sum += seq[seq.len() / 2]; + } + + print_res!("Sum of middle pages: {sum}"); +} + +impl PageGraph { + fn reorder_sequence(&self, seq: &mut [usize]) { + seq.sort_by(|a, b| { + let pred_a = self.predecesors.get(a); + let pred_b = self.predecesors.get(b); + + if a == b { + return std::cmp::Ordering::Equal; + } + + if let Some(pa) = pred_a { + if pa.contains(b) { + return std::cmp::Ordering::Greater; + } + } + + if let Some(pb) = pred_b { + if pb.contains(a) { + return std::cmp::Ordering::Less; + } + } + + panic!("No predecessor found"); + }); + } +} + +#[inline(never)] +pub fn part2((graph, sequences): Parsed) { + let mut sum = 0; + + 'seq: for mut seq in sequences { + let mut seen = Vec::new(); + + for n in &seq { + seen.push(n); + + if graph + .successors + .get(n) + .map(|x| -> &[_] { x }) + .unwrap_or_else(|| &[]) + .iter() + .any(|s| seen.contains(&s)) + { + graph.reorder_sequence(&mut seq); + sum += seq[seq.len() / 2]; + continue 'seq; + } + } + } + + print_res!("Sum of incorrect middle pages: {sum}"); +} + +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(()) +}