From 35ba924c1b1b5a8b31c8129b449b08b1993460f2 Mon Sep 17 00:00:00 2001 From: Joe Ardent <code@ardent.nebcorp.com> Date: Mon, 23 Dec 2024 13:00:41 -0800 Subject: [PATCH] day5, part2 --- Cargo.lock | 7 ++ Cargo.toml | 2 +- day05/Cargo.toml | 7 ++ day05/src/main.rs | 179 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 day05/Cargo.toml create mode 100644 day05/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 9f85b3c..0b394f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,13 @@ dependencies = [ name = "day04" version = "0.1.0" +[[package]] +name = "day05" +version = "0.1.0" +dependencies = [ + "winnow", +] + [[package]] name = "memchr" version = "2.7.4" diff --git a/Cargo.toml b/Cargo.toml index 95300e0..766f8ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["day01", "day02", "day03", "day04"] +members = ["day01", "day02", "day03", "day04", "day05"] [workspace.dependencies] winnow = "0.6" diff --git a/day05/Cargo.toml b/day05/Cargo.toml new file mode 100644 index 0000000..19e83f5 --- /dev/null +++ b/day05/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "day05" +version = "0.1.0" +edition = "2024" + +[dependencies] +winnow.workspace = true diff --git a/day05/src/main.rs b/day05/src/main.rs new file mode 100644 index 0000000..3636f2b --- /dev/null +++ b/day05/src/main.rs @@ -0,0 +1,179 @@ +use std::collections::{HashMap, HashSet}; + +use winnow::{ + PResult, Parser, + ascii::{dec_uint, newline}, + combinator::{separated, separated_pair}, + error::ContextError, +}; + +fn main() { + let input = std::fs::read_to_string("input").unwrap(); + println!("{}", pt1(&input)); + println!("{}", pt2(&input)); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct Rule(u32, u32); + +type Rules = Vec<Rule>; +type Updates = Vec<Vec<u32>>; +type Graph = HashMap<u32, HashSet<u32>>; + +fn pt1(input: &str) -> u32 { + let mut input = input; + let (rules, updates) = parse(&mut input).unwrap(); + let mut total = 0; + for update in updates { + if check(&rules, &update) { + let mid = update.len() / 2; + total += update[mid]; + } + } + total +} + +fn check(rules: &Rules, update: &[u32]) -> bool { + let mut pages = HashMap::new(); + for (i, p) in update.iter().enumerate() { + pages.insert(p, i); + } + for rule in rules { + let Rule(before, after) = rule; + if let Some(u) = pages.get(before) { + if let Some(v) = pages.get(after) { + if v <= u { + return false; + } + } + } + } + true +} + +fn pt2(input: &str) -> u32 { + let mut input = input; + let mut total = 0; + + let (rules, updates) = parse(&mut input).unwrap(); + + let mut neighbors = Graph::new(); + for rule in &rules { + let Rule(u, v) = rule; + neighbors.entry(*u).or_default().insert(*v); + } + + for update in updates { + if !check(&rules, &update) { + let sorted = tsort(&neighbors, &update); + let mid = sorted.len() / 2; + total += sorted[mid]; + } + } + + total +} + +fn tsort(graph: &Graph, nodes: &[u32]) -> Vec<u32> { + let mut sorted = Vec::with_capacity(nodes.len()); + let mut processed = HashSet::with_capacity(nodes.len()); + let mut q = Vec::new(); + let nodes: HashSet<u32> = nodes.iter().copied().collect(); + + for node in nodes.iter() { + q.push(*node); + while let Some(current) = q.pop() { + if !processed.contains(¤t) { + q.push(current); + } + + let mut do_top = true; + if let Some(nexts) = graph.get(¤t) { + for n in nexts.iter().filter(|v| nodes.contains(v) && **v != current) { + if !processed.contains(n) { + q.push(*n); + do_top = false; + } + } + } + + if do_top { + let _ = q.pop(); + if processed.insert(current) { + sorted.push(current); + } + } + } + } + + sorted +} + +fn p_rule(input: &mut &str) -> PResult<Rule> { + let (a, b) = separated_pair(dec_uint, '|', dec_uint).parse_next(input)?; + Ok(Rule(a, b)) +} + +fn p_update(input: &mut &str) -> PResult<Vec<u32>> { + separated(1.., dec_uint::<&str, u32, ContextError>, ',').parse_next(input) +} + +fn p_input(input: &mut &str) -> PResult<(Vec<Rule>, Updates)> { + separated_pair( + separated(1.., p_rule, newline), + (newline, newline), + separated(1.., p_update, newline), + ) + .parse_next(input) +} + +fn parse(input: &mut &str) -> PResult<(Rules, Updates)> { + let (rules, updates) = p_input(input)?; + + Ok((rules, updates)) +} + +#[cfg(test)] +mod test { + use super::*; + + static INPUT: &str = "47|53 +97|13 +97|61 +97|47 +75|29 +61|13 +75|53 +29|13 +97|29 +53|29 +61|53 +97|53 +61|29 +47|13 +75|47 +97|75 +47|61 +75|61 +47|29 +75|13 +53|13 + +75,47,61,53,29 +97,61,53,29,13 +75,29,13 +75,97,47,61,53 +61,13,29 +97,13,75,29,47"; + + #[test] + fn p1() { + let v = pt1(INPUT); + assert_eq!(v, 143) + } + + #[test] + fn p2() { + assert_eq!(123, pt2(INPUT)); + } +}