diff --git a/src/day3.rs b/src/day3.rs new file mode 100644 index 0000000..dce28e0 --- /dev/null +++ b/src/day3.rs @@ -0,0 +1,113 @@ +use aoc_runner_derive::{aoc, aoc_generator}; +use itertools::Itertools; +use std::fmt::Display; + +struct Rucksack { + items: Vec, +} + +impl From<&[u8]> for Rucksack { + fn from(s: &[u8]) -> Self { + Self { + items: s.iter().cloned().collect(), + } + } +} + +impl Display for Rucksack { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "{}{}", + String::from_utf8_lossy(&self.compartments()[0]), + String::from_utf8_lossy(&self.compartments()[1]) + )) + } +} + +impl Rucksack { + fn compartments(&self) -> [&[u8]; 2] { + let tup = self.items.split_at(self.items.len() / 2); + [tup.0, tup.1] + } + fn common(&self) -> u8 { + for item in self.compartments()[0] { + if self.compartments()[1].contains(&item) { + return *item; + } + } + panic!("Common item not found"); + } + fn contains(&self, item: &u8) -> bool { + self.compartments().into_iter().flatten().contains(item) + } + fn common_with<'a>(&self, other: &[u8]) -> impl Iterator { + self.compartments() + .into_iter() + .flatten() + .filter(|c| other.contains(c)) + .cloned() + } +} + +fn priority(c: u8) -> u8 { + if c >= b'A' && c <= b'Z' { + c - b'A' + 27 + } else if c >= b'a' && c <= b'z' { + c - b'a' + 1 + } else { + panic!("Invalid item {c}") + } +} + +#[aoc_generator(day3)] +fn parse(input: &[u8]) -> Vec { + input.split(|i| *i == b'\n').map(|x| x.into()).collect() +} + +#[aoc(day3, part1)] +fn part1(input: &Vec) -> u64 { + input.iter().map(|sack| priority(sack.common()) as u64).sum() +} + +#[aoc(day3, part2)] +fn part2(input: &Vec) -> u64 { + let mut total = 0u64; + for sacks in input.chunks(3) { + let commons_1 = sacks[0].common_with(&sacks[1].items).unique().collect_vec(); + let commons = sacks[2].common_with(&commons_1).unique().collect_vec(); + + if commons.len() != 1 { + panic!( + "Wrong number ({}) of commons for sacks {} {} {}", + commons.len(), + sacks[0], + sacks[1], + sacks[2] + ); + } + total += priority(commons[0]) as u64 + } + total +} + +#[cfg(test)] +mod tests { + use super::*; + + const EXAMPLE: &[u8] = b"vJrwpWtwJgWrhcsFMMfFFhFp +jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL +PmmdzqPrVvPwwTWBwg +wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn +ttgJtRGJQctTZtZT +CrZsJsPPZsGzwwsLwLmpwMDw"; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EXAMPLE)), 157); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EXAMPLE)), 70); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9cca356..72cdbc0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,8 @@ -mod day2; mod day1; +mod day2; +mod day3; use aoc_runner_derive::aoc_lib; -pub mod day6; +mod day6; aoc_lib! { year = 2022 }