103 lines
2.4 KiB
Rust
103 lines
2.4 KiB
Rust
|
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<u8>,
|
||
|
right: Vec<u8>,
|
||
|
}
|
||
|
|
||
|
impl Rucksack {
|
||
|
fn find_duplicate(&self) -> Option<u8> {
|
||
|
// 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<Item = &u8> {
|
||
|
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<Rucksack, DataError> {
|
||
|
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<u64, DataError> {
|
||
|
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::<Result<Vec<Rucksack>, 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(())
|
||
|
}
|