diff --git a/data/examples/05.txt b/data/examples/05.txt new file mode 100644 index 0000000..9d146d6 --- /dev/null +++ b/data/examples/05.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/src/bin/05.rs b/src/bin/05.rs new file mode 100644 index 0000000..e376cbc --- /dev/null +++ b/src/bin/05.rs @@ -0,0 +1,164 @@ +advent_of_code::solution!(5); + +#[derive(Debug)] +struct Rules { + rules: Vec<(u32, u32)> +} + +impl From<&[&str]> for Rules { + fn from(lines: &[&str]) -> Self { + let mut rules: Vec<(u32, u32)> = lines + .into_iter() + .map( + |line| { + let pages = line + .split("|") + .map(|page| page.parse::().expect("invalid numeric in rules")) + .collect::>(); + match pages.as_slice() { + [a, b] => (*a, *b), + _ => panic!("invalid input: invalid rule"), + } + } + ) + .collect(); + rules.sort(); + Self { rules } + } +} + +impl Rules { + fn pages_after(&self, page: u32) -> Vec:: { + self + .rules + .clone() + .into_iter() + .filter_map(|r| match r { + (p1, p2) if p1 == page => Some(p2), + _ => None + }) + .collect() + } + + fn pages_before(&self, page: u32) -> Vec:: { + self + .rules + .clone() + .into_iter() + .filter_map(|r| match r { + (p1, p2) if p2 == page => Some(p1), + _ => None + }) + .collect() + } + + fn get_correct_pages_order(&self, pages: Vec) -> Vec { + let mut pages_with_order: Vec<(u32, Vec, Vec)> = pages + .clone() + .into_iter() + .map(|p| + ( + p, + self.pages_before(p).into_iter().filter(|pp| pages.contains(pp)).collect(), + self.pages_after(p).into_iter().filter(|pp| pages.contains(pp)).collect(), + ) + ) + .collect(); + pages_with_order.sort_by_key( + |(p, before, after)| (before.len(), after.len()) + ); + pages_with_order.into_iter().map(|(p, _, _)| p).collect() + } +} + +#[derive(Debug)] +struct ImpressionOrder { + pages: Vec +} + +impl ImpressionOrder { + fn middle_page(&self) -> Option { + let length = self.pages.len(); + if length % 2 == 0 { + None + } else { + Some(self.pages[ (length / 2) as usize ]) + } + } + + fn fix_order(&self, rules: &Rules) -> Self { + Self { + pages: rules.get_correct_pages_order(self.pages.clone()) + } + } + + fn is_valid(&self, rules: &Rules) -> bool { + let mut invalid_pages: Vec = Vec::new(); + for p in self.pages.clone() { + if invalid_pages.contains(&p) { + return false; + } + invalid_pages.extend(rules.pages_before(p)); + } + true + } +} + +impl TryFrom<&&str> for ImpressionOrder { + type Error = (); + fn try_from(line: &&str) -> Result { + let pages = line.split(",").flat_map(|page| page.parse::()).collect::>(); + Ok(Self { pages }) + } +} + +pub fn part_one(input: &str) -> Option { + let input = input + .lines() + .collect::>(); + let input = input + .split(|s|s.is_empty()) + .collect::>(); + + let rules: Rules = input[0].into(); + input[1] + .into_iter() + .flat_map(ImpressionOrder::try_from) + .filter_map(|o| if o.is_valid(&rules) { o.middle_page() } else { None }) + .sum::() + .into() +} + +pub fn part_two(input: &str) -> Option { + let input = input + .lines() + .collect::>(); + let input = input + .split(|s|s.is_empty()) + .collect::>(); + + let rules: Rules = input[0].into(); + input[1] + .into_iter() + .flat_map(ImpressionOrder::try_from) + .filter_map(|o| if !o.is_valid(&rules) { o.fix_order(&rules).middle_page() } else { None }) + .sum::() + .into() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(143)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(123)); + } +}