You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
2.7 KiB
118 lines
2.7 KiB
|
|
#[macro_use] extern crate lazy_static;
|
|
|
|
use smallmap::{
|
|
Map,
|
|
smallmap,
|
|
};
|
|
use std::{
|
|
collections::BTreeSet,
|
|
};
|
|
use linebuffer::{
|
|
buf::forward,
|
|
TryFromBuf,
|
|
FromBuf,
|
|
ParsedLines,
|
|
};
|
|
|
|
use color_eyre::{
|
|
eyre::{
|
|
self, eyre,
|
|
WrapErr as _,
|
|
},
|
|
Help as _, SectionExt as _,
|
|
};
|
|
|
|
mod ext;
|
|
pub use ext::*;
|
|
|
|
fn init() -> eyre::Result<()>
|
|
{
|
|
color_eyre::install()
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)] //TODO: implement PartialEq, Eq, PartialOrd, Ord via intersection between `full`s
|
|
struct Sack {
|
|
split_by: usize,
|
|
containers: Vec<BTreeSet<char>>,
|
|
full: smallmap::Set<char>, //TODO: When doing the above ^, change to BTreeSet for `intersection()`
|
|
}
|
|
|
|
impl Sack
|
|
{
|
|
/// Compute the intersection of each `N` part of the container
|
|
pub fn intersection(&self) -> impl Iterator<Item = char> + '_
|
|
{
|
|
self.containers.iter().take_two().flat_map(|(a, b)| b.map(move |b| (a, b)))
|
|
.map(|(a, b)| a.intersection(b)).flatten().copied()
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn read_input_from<R: std::io::Read>(input: R) -> ParsedLines<R, forward::FromStr<Sack>>
|
|
{
|
|
ParsedLines::new(input)
|
|
}
|
|
|
|
fn read_input() -> eyre::Result<ParsedLines<impl std::io::Read + 'static, forward::FromStr<Sack>>>
|
|
{
|
|
let reader = std::fs::OpenOptions::new()
|
|
.read(true)
|
|
.open("input")
|
|
.wrap_err("Failed to open input file for reading")?;
|
|
Ok(read_input_from(reader))
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct SackParseError{
|
|
expected_div: usize,
|
|
size: usize,
|
|
}
|
|
|
|
impl std::str::FromStr for Sack
|
|
{
|
|
type Err = SackParseError;
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let (first, second) = s.split_at(s.len()/2);
|
|
Ok(Self {
|
|
split_by: 2,
|
|
containers: vec![
|
|
first.chars().collect(),
|
|
second.chars().collect(),
|
|
],
|
|
full: s.chars().map(|a| (a, ())).collect(),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn prop_swivle<'a>(sigh: impl IntoIterator<Item = char>+ 'a) -> impl Iterator<Item = usize> + 'a
|
|
{
|
|
lazy_static! {
|
|
static ref PMAP: Map<char, usize> = ('a'..='z').enumerate().map(|(i, n)| (i+1, n))
|
|
.chain (('A'..='Z').enumerate().map(|(i, n)| (i + 27, n)))
|
|
.map(|(i, n)| (n, i))
|
|
.collect();
|
|
}
|
|
sigh.into_iter().map(|ref ch| PMAP.get(ch)).flat_map(std::convert::identity).copied()
|
|
}
|
|
|
|
fn main() -> eyre::Result<()> {
|
|
init().wrap_err("Panic hook failed to install")?;
|
|
|
|
let mut input = read_input()?;
|
|
|
|
let mut psum = 0;
|
|
|
|
while let Some(sack) = input.try_next()
|
|
{
|
|
let sack = sack.expect("Failed to parse input line");
|
|
psum += prop_swivle(sack.intersection())
|
|
.sum::<usize>();
|
|
// TODO: part2: find sack.full's intersection through *all other* lines, up to two where the intersection is exactly 1 between all three. This is boring, I give up.
|
|
}
|
|
println!("{psum}");
|
|
|
|
Ok(())
|
|
}
|