use { anyhow::{ anyhow, Context, Result, }, std::{ env::args, fs::File, io::{ BufRead, BufReader, }, path::Path, }, }; fn item_priority(item: char) -> Option { match item { 'a'..='z' => Some((item as u32 - 'a' as u32 + 1) as usize), 'A'..='Z' => Some((item as u32 - 'A' as u32 + 27) as usize), _ => None, } } #[derive(Debug)] struct Rucksack { compartments: Vec, } impl Rucksack { fn new(items: &str) -> Self { let (first, second) = items.split_at(items.len()/2); let compartments = vec![String::from(first), String::from(second)]; Self { compartments, } } fn contains(&self, needle: char) -> bool { self.compartments[0].contains(needle) || self.compartments[1].contains(needle) } fn common_items(&self) -> String { self.compartments[0].chars().filter(|item| self.compartments[1].contains(*item)).collect() } fn find_badge(&self, other: &Rucksack) -> String { self.compartments[0].chars().chain(self.compartments[1].chars()).filter(|item| other.contains(*item)).collect() } } fn read_input>(filename: T) -> Result> { let reader = BufReader::new(File::open(filename)?); reader.lines().map( |v| { let s = v?; Ok(Rucksack::new(&s)) } ).collect() } fn part1(input: &[Rucksack]) -> Result { Ok(input.iter().filter_map(|rucksack| { if let Some(item) = rucksack.common_items().chars().next() { item_priority(item) } else { None } }).sum()) } fn part2(input: &[Rucksack]) -> Result { Ok(input.chunks(3).filter_map(|rucksacks| { let some_candidates = rucksacks[0].find_badge(&rucksacks[1]); let other_candidates = rucksacks[0].find_badge(&rucksacks[2]); let common: String = some_candidates.chars().filter(|item| other_candidates.contains(*item)).collect(); if let Some(item) = common.chars().next() { item_priority(item) } else { None } }).sum()) } fn main() -> Result<()> { let ( do_part_1, do_part_2 ) = aoc::do_parts(); let filename = args().nth(1).ok_or_else(|| anyhow!("Missing input filename"))?; let input = read_input(filename).context("Could not read input")?; if do_part_1 { let solution = part1(&input).context("No solution for part 1")?; println!("Part1, solution found to be: {}", solution); } if do_part_2 { let solution = part2(&input).context("No solution for part 2")?; println!("Part2, solution found to be: {}", solution); } Ok(()) }