108 lines
3.1 KiB
Rust
108 lines
3.1 KiB
Rust
|
// 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 <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
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<Item>,
|
||
|
c2: BTreeSet<Item>,
|
||
|
}
|
||
|
|
||
|
pub struct Rucksacks {}
|
||
|
|
||
|
pub struct Group {
|
||
|
rucksacks: Vec<Rucksack>,
|
||
|
}
|
||
|
|
||
|
impl Item {
|
||
|
pub fn new(value: char) -> Result<Self, &'static str> {
|
||
|
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<Self, &'static str> {
|
||
|
let collection1: Result<BTreeSet<_>, _> = compartment1.chars().map(Item::new).collect();
|
||
|
let collection2: Result<BTreeSet<_>, _> = compartment2.chars().map(Item::new).collect();
|
||
|
Ok(Rucksack {
|
||
|
c1: collection1?,
|
||
|
c2: collection2?,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
pub fn items(&self) -> BTreeSet<Item> {
|
||
|
self.c1.union(&self.c2).cloned().collect()
|
||
|
}
|
||
|
|
||
|
pub fn duplicates(&self) -> Vec<Item> {
|
||
|
self.c1.intersection(&self.c2).cloned().collect()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Iterator for Rucksacks {
|
||
|
type Item = Result<Rucksack, &'static str>;
|
||
|
|
||
|
fn next(&mut self) -> Option<Self::Item> {
|
||
|
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<Item, &'static str> {
|
||
|
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")
|
||
|
}
|
||
|
}
|