diff --git a/README.md b/README.md index 6ac3a7f..956a56a 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www. | [Day 2](./src/bin/02.rs) | `117.1µs` | `205.3µs` | | [Day 3](./src/bin/03.rs) | `115.1µs` | `148.7µs` | | [Day 4](./src/bin/04.rs) | `81.9µs` | `51.3µs` | +| [Day 5](./src/bin/05.rs) | `379.0µs` | `816.9µs` | -**Total: 0.82ms** +**Total: 2.01ms** 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/04.rs b/src/bin/04.rs index 04cd323..d716cbb 100644 --- a/src/bin/04.rs +++ b/src/bin/04.rs @@ -227,12 +227,12 @@ mod tests { #[test] fn test_part_one() { let result = part_one(&advent_of_code::template::read_file("examples", DAY)); - assert_eq!(result, None); + assert_eq!(result, Some(18)); } #[test] fn test_part_two() { let result = part_two(&advent_of_code::template::read_file("examples", DAY)); - assert_eq!(result, None); + assert_eq!(result, Some(9)); } } diff --git a/src/bin/05.rs b/src/bin/05.rs new file mode 100644 index 0000000..df65973 --- /dev/null +++ b/src/bin/05.rs @@ -0,0 +1,119 @@ +use std::{ + cmp::Ordering, + collections::{HashMap, HashSet, VecDeque}, +}; + +use itertools::Itertools; + +advent_of_code::solution!(5); + +pub fn part_one(input: &str) -> Option { + let (rules, updates) = input.split_once("\n\n").unwrap(); + + let rule_lookup = + rules + .lines() + .fold(HashMap::new(), |mut acc: HashMap<&str, Vec<&str>>, line| { + let (l, r) = line.split_once("|").unwrap(); + acc.entry(l).or_default().push(r); + acc + }); + + let mut result = 0; + for update in updates.lines() { + let mut already_seen: HashSet<&str> = HashSet::new(); + let mut success = true; + for value in update.split(',') { + let Some(must_come_after) = rule_lookup.get(value) else { + already_seen.insert(value); + continue; + }; + let breaks_rule = must_come_after.iter().any(|val| already_seen.contains(val)); + if breaks_rule { + success = false; + break; + } + + already_seen.insert(value); + } + + if success { + let as_strs = update.split(",").collect_vec(); + let middle = as_strs[(as_strs.len() - 1) / 2].parse::().unwrap(); + result += middle; + } + } + + Some(result) +} + +pub fn part_two(input: &str) -> Option { + let (rules, updates) = input.split_once("\n\n").unwrap(); + + let rule_lookup = + rules + .lines() + .fold(HashMap::new(), |mut acc: HashMap<&str, Vec<&str>>, line| { + let (l, r) = line.split_once("|").unwrap(); + acc.entry(l).or_default().push(r); + acc + }); + + let mut incorrect = vec![]; + for update in updates.lines() { + let mut already_seen: HashSet<&str> = HashSet::new(); + for value in update.split(',') { + let Some(must_come_after) = rule_lookup.get(value) else { + already_seen.insert(value); + continue; + }; + let breaks_rule = must_come_after.iter().any(|val| already_seen.contains(val)); + if breaks_rule { + incorrect.push(update); + break; + } + + already_seen.insert(value); + } + } + + let mut res = 0; + for update in incorrect.into_iter() { + let ordered = update + .split(",") + .sorted_by(|a, b| { + if !rule_lookup.contains_key(a) { + return Ordering::Greater; + } + + if rule_lookup[a].contains(b) { + return Ordering::Less; + } + + Ordering::Greater + }) + .collect_vec(); + + let middle = ordered[(ordered.len() - 1) / 2].parse::().unwrap(); + res += middle; + } + + Some(res) +} + +#[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)); + } +}