// Copyright 2022 Christian Ulrich // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . use std::collections::BTreeSet; use std::io; #[derive(Debug, PartialOrd, PartialEq, Eq, Ord, Clone)] pub struct Item(u8); #[derive(Clone)] pub struct Rucksack { c1: BTreeSet, c2: BTreeSet, } pub struct Rucksacks {} pub struct Group { rucksacks: Vec, } impl Item { pub fn new(value: char) -> Result { match value { 'A'..='z' => Ok(Item(value as u8)), _ => Err("item value out of range"), } } pub fn priority(&self) -> usize { match self.0 as char { 'a'..='z' => (self.0 - 96).into(), 'A'..='Z' => (self.0 - 38).into(), _ => panic!("not reachable"), } } } impl Rucksack { pub fn new(compartment1: &str, compartment2: &str) -> Result { let collection1: Result, _> = compartment1.chars().map(Item::new).collect(); let collection2: Result, _> = compartment2.chars().map(Item::new).collect(); Ok(Rucksack { c1: collection1?, c2: collection2?, }) } pub fn items(&self) -> BTreeSet { self.c1.union(&self.c2).cloned().collect() } pub fn duplicates(&self) -> Vec { self.c1.intersection(&self.c2).cloned().collect() } } impl Iterator for Rucksacks { type Item = Result; fn next(&mut self) -> Option { if let Some(line) = io::stdin().lines().next() { let line = line.unwrap(); if line.len() > 0 { if line.len() % 2 == 0 { let (c1, c2) = line.split_at(line.len() / 2); return Some(Rucksack::new(c1, c2)); } return Some(Err("input file contains invalid line")); } } None } } impl Group { pub fn new(rucksacks: &[Rucksack]) -> Self { Self { rucksacks: rucksacks.into(), } } pub fn badge(&self) -> Result { if let Some(first) = self.rucksacks.first() { let mut items = first.items(); for next_items in self.rucksacks[1..].iter().map(Rucksack::items) { items = items.intersection(&next_items).cloned().collect(); } if items.len() == 1 { return Ok(items.iter().next().cloned().unwrap()); } } Err("cannot determine badge") } }