use std::str::FromStr; const DATA: &'static str = include_str!("../data/day3.txt"); // const DATA: &'static str = " // vJrwpWtwJgWrhcsFMMfFFhFp // jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL // PmmdzqPrVvPwwTWBwg // wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn // ttgJtRGJQctTZtZT // CrZsJsPPZsGzwwsLwLmpwMDw // "; #[derive(Debug)] enum DataError { Imbalanced, UnknownValue, NoDuplicate, NoTriplicate, ShortGroup, } struct Rucksack { left: Vec, right: Vec, } impl Rucksack { fn find_duplicate(&self) -> Option { // pretty sure this is faster than hash set, given the sizes involved self.left.iter() .find(|c| self.right.contains(c)) .map(|c| *c) } fn iter_all(&self) -> impl Iterator { self.left.iter().chain(self.right.iter()) } fn contains(&self, v: u8) -> bool { self.left.contains(&v) || self.right.contains(&v) } } impl FromStr for Rucksack { type Err = DataError; fn from_str(line: &str) -> Result { let line = line.trim(); if line.len() % 2 != 0 { return Err(DataError::Imbalanced); } let (a, b) = line.as_bytes().split_at(line.len() / 2); Ok(Rucksack { left: a.into(), right: b.into(), }) } } fn priority(c: u8) -> Result { match c { // lowercase 97..=122 => Ok(c as u64 - 96), // uppercase 65..=90 => Ok(c as u64 - 38), _ => Err(DataError::UnknownValue), } } fn main() -> Result<(), DataError> { let mut dup_total = 0; let rucks = DATA.trim() .split("\n") .map(|line| Rucksack::from_str(line)) .collect::, DataError>>()?; for ruck in rucks.iter() { let dup = ruck.find_duplicate() .ok_or(DataError::NoDuplicate)?; dup_total += priority(dup)?; } println!("Part 1: {dup_total}"); let mut groups_total = 0; for (idx, ruck) in rucks.iter().enumerate().step_by(3) { if idx > rucks.len() - 3 { return Err(DataError::ShortGroup); } let badge = *ruck.iter_all() .find(|&v| rucks[idx + 1].contains(*v) && rucks[idx + 2].contains(*v)) .ok_or(DataError::NoTriplicate)?; groups_total += priority(badge)?; } println!("Part 2: {groups_total}"); Ok(()) }