use std::str::FromStr; use aoc_runner_derive::{aoc, aoc_generator}; #[derive(Debug, Default)] struct Rucksack { first_half: Vec, second_half: Vec, } #[derive(Debug, Copy, Clone)] struct NoItemInBothHalves; #[derive(Debug, Copy, Clone)] struct NoCommonItem; impl Rucksack { fn find_item_in_both_halves(&self) -> Result { self.first_half .iter() .copied() .find(|item| self.second_half.contains(item)) .ok_or(NoItemInBothHalves) } fn has_item(&self, item: u8) -> bool { self.first_half.contains(&item) | self.second_half.contains(&item) } fn find_common_item(&self, others: &[Rucksack]) -> Result { self.first_half .iter() .chain(&self.second_half) .copied() .find(|&item| others.iter().all(|rucksack| rucksack.has_item(item))) .ok_or(NoCommonItem) } } #[derive(Debug, Copy, Clone)] struct InvalidRucksack; impl FromStr for Rucksack { type Err = InvalidRucksack; fn from_str(s: &str) -> Result { let mut priorities = s .chars() .map(|ch| { if !ch.is_ascii_alphabetic() { Err(InvalidRucksack) } else if ch.is_ascii_uppercase() { Ok(ch as u8 - b'A' + 27) } else { Ok(ch as u8 - b'a' + 1) } }) .collect::, _>>()?; let second_half = priorities.split_off(priorities.len() / 2); Ok(Rucksack { first_half: priorities, second_half, }) } } #[aoc_generator(day3, part1)] fn input_generator_part1(input: &str) -> Vec { input.lines().map(|l| l.parse().unwrap()).collect() } #[aoc_generator(day3, part2)] fn input_generator_part2(input: &str) -> Vec> { input .lines() .collect::>() .chunks(3) .map(|list| { list.iter() .map(|el| el.parse().unwrap()) .collect::>() }) .collect::>() } #[aoc(day3, part1)] fn solve_part1(input: &[Rucksack]) -> u64 { input .iter() .map(|rucksack| rucksack.find_item_in_both_halves().unwrap() as u64) .sum() } #[aoc(day3, part2)] fn solve_part2(input: &[Vec]) -> u64 { input .iter() .map(|rucksacks| rucksacks[0].find_common_item(&rucksacks[1..]).unwrap() as u64) .sum() }