diff --git a/crates/year2024/examples/day05_example0.txt b/crates/year2024/examples/day05_example0.txt new file mode 100644 index 0000000..9d146d6 --- /dev/null +++ b/crates/year2024/examples/day05_example0.txt @@ -0,0 +1,28 @@ +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 diff --git a/crates/year2024/src/day05.rs b/crates/year2024/src/day05.rs new file mode 100644 index 0000000..804a310 --- /dev/null +++ b/crates/year2024/src/day05.rs @@ -0,0 +1,96 @@ +use utils::prelude::*; + +/// Sorting lists using a partial order of constraints. +#[derive(Clone, Debug)] +pub struct Day05 { + rules: Rules, + sorted: Vec>, + unsorted: Vec>, +} + +const MIN_NUM: usize = 10; +const MAX_NUM: usize = 99; +const RANGE: usize = MAX_NUM - MIN_NUM + 1; +type Rules = [bool; RANGE * RANGE]; + +impl Day05 { + pub fn new(input: &str, _: InputType) -> Result { + let num = parser::number_range(MIN_NUM as u32..=MAX_NUM as u32); + let rules_parser = num.then(num.with_prefix(b'|')).repeat(b'\n', 1); + let updates_parser = num.repeat(b',', 1).repeat(b'\n', 1); + + let (rules_list, updates) = rules_parser + .then(updates_parser.with_prefix(b'\n')) + .parse_complete(input)?; + + let mut rules = [false; RANGE * RANGE]; + for (a, b) in rules_list { + rules[(a as usize - MIN_NUM) * RANGE + b as usize] = true; + } + + let (sorted, unsorted) = updates.into_iter().partition(|update| { + for (i, &page1) in update.iter().enumerate() { + for &page2 in update.iter().skip(i + 1) { + if Self::must_be_before(&rules, page2, page1) { + return false; + } + } + } + true + }); + + Ok(Self { + rules, + sorted, + unsorted, + }) + } + + #[must_use] + pub fn part1(&self) -> u32 { + self.sorted + .iter() + .map(|update| update[update.len() / 2]) + .sum() + } + + #[must_use] + pub fn part2(&self) -> u32 { + let mut result = 0; + let mut list = Vec::new(); + for original in &self.unsorted { + original.clone_into(&mut list); + + for _ in 0..(list.len() / 2) { + let i = Self::find_first_index(&self.rules, &list); + list.swap_remove(i); + } + + let i = Self::find_first_index(&self.rules, &list); + result += list[i]; + } + + result + } + + fn must_be_before(rules: &Rules, page1: u32, page2: u32) -> bool { + rules[(page1 as usize - MIN_NUM) * RANGE + page2 as usize] + } + + fn find_first_index(rules: &Rules, list: &[u32]) -> usize { + 'outer: for (i, &page1) in list.iter().enumerate() { + for (j, &page2) in list.iter().enumerate() { + if i != j && Self::must_be_before(rules, page2, page1) { + continue 'outer; + } + } + + return i; + } + panic!("no solution found"); + } +} + +examples!(Day05 -> (u32, u32) [ + {file: "day05_example0.txt", part1: 143, part2: 123}, +]); diff --git a/crates/year2024/src/lib.rs b/crates/year2024/src/lib.rs index d7de9da..7c1fcec 100644 --- a/crates/year2024/src/lib.rs +++ b/crates/year2024/src/lib.rs @@ -6,4 +6,5 @@ utils::year!(2024 => year2024, ${ 2 => day02::Day02, 3 => day03::Day03, 4 => day04::Day04, + 5 => day05::Day05, });