adventofcode2022/day03/common.rs

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")
}
}