use std::collections::HashSet; use std::str::FromStr; use aoc_2022::*; use color_eyre::eyre::{Report, Result}; use tracing::*; use tracing_subscriber::EnvFilter; struct Backpack(HashSet, HashSet); type Input = Vec; type Output = i32; impl FromStr for Backpack { type Err = Report; fn from_str(s: &str) -> Result { let mut left: HashSet = HashSet::new(); let mut right: HashSet = HashSet::new(); let mid = s.len() / 2; for (i, c) in s.chars().enumerate() { let s = if i < mid { &mut left } else { &mut right }; s.insert( c as i32 + if c.is_ascii_lowercase() { 1 - 'a' as i32 } else { 27 - 'A' as i32 }, ); } Ok(Backpack(left, right)) } } impl Backpack { fn common_items(&self) -> HashSet { let Backpack(left, right) = self; left.intersection(right).cloned().collect() } fn all_items(&self) -> HashSet { let Backpack(left, right) = self; left.union(right).cloned().collect() } } fn part_1(input: &Input) -> Output { input .iter() .map(|b| b.common_items().iter().sum::()) .sum() } fn common_items(backpacks: &[Backpack]) -> HashSet { backpacks .iter() .skip(1) .fold(backpacks[0].all_items(), |u, b| { u.intersection(&b.all_items()).cloned().collect() }) } fn part_2(input: &Input) -> Output { input .chunks(3) .map(|backpacks| common_items(backpacks).iter().sum::()) .sum() } fn parse_input(pathname: &str) -> Input { file_to_structs(pathname) } fn main() -> Result<()> { tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .with_target(false) .with_file(true) .with_line_number(true) .without_time() .compact() .init(); color_eyre::install()?; let input = parse_input("inputs/day03.txt"); info!("Part 1: {}", part_1(&input)); info!("Part 2: {}", part_2(&input)); Ok(()) } #[cfg(test)] mod day_03 { use super::*; #[test] fn test_part_1() { let sample = parse_input("samples/day03.txt"); assert_eq!(part_1(&sample), 157); } #[test] fn test_part_2() { let sample = parse_input("samples/day03.txt"); assert_eq!(part_2(&sample), 70); } }