113 lines
2.5 KiB
Rust
113 lines
2.5 KiB
Rust
use std::collections::HashSet;
|
|
use std::str::FromStr;
|
|
|
|
use aoc_2022::*;
|
|
|
|
use color_eyre::eyre::{Report, Result};
|
|
use tracing::*;
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
struct Backpack(HashSet<i32>, HashSet<i32>);
|
|
|
|
type Input = Vec<Backpack>;
|
|
type Output = i32;
|
|
|
|
impl FromStr for Backpack {
|
|
type Err = Report;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let mut left: HashSet<i32> = HashSet::new();
|
|
let mut right: HashSet<i32> = HashSet::new();
|
|
|
|
let mid = s.len() / 2;
|
|
for (i, c) in s.chars().enumerate() {
|
|
let s = if i < mid { &mut left } else { &mut right };
|
|
|
|
s.insert(
|
|
c as i32
|
|
+ if c.is_ascii_lowercase() {
|
|
1 - 'a' as i32
|
|
} else {
|
|
27 - 'A' as i32
|
|
},
|
|
);
|
|
}
|
|
|
|
Ok(Backpack(left, right))
|
|
}
|
|
}
|
|
|
|
impl Backpack {
|
|
fn common_items(&self) -> HashSet<i32> {
|
|
let Backpack(left, right) = self;
|
|
left.intersection(right).cloned().collect()
|
|
}
|
|
|
|
fn all_items(&self) -> HashSet<i32> {
|
|
let Backpack(left, right) = self;
|
|
left.union(right).cloned().collect()
|
|
}
|
|
}
|
|
|
|
fn part_1(input: &Input) -> Output {
|
|
input
|
|
.iter()
|
|
.map(|b| b.common_items().iter().sum::<i32>())
|
|
.sum()
|
|
}
|
|
|
|
fn common_items(backpacks: &[Backpack]) -> HashSet<i32> {
|
|
backpacks
|
|
.iter()
|
|
.skip(1)
|
|
.fold(backpacks[0].all_items(), |u, b| {
|
|
u.intersection(&b.all_items()).cloned().collect()
|
|
})
|
|
}
|
|
|
|
fn part_2(input: &Input) -> Output {
|
|
input
|
|
.chunks(3)
|
|
.map(|backpacks| common_items(backpacks).iter().sum::<i32>())
|
|
.sum()
|
|
}
|
|
|
|
fn parse_input(pathname: &str) -> Input {
|
|
file_to_structs(pathname)
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(EnvFilter::from_default_env())
|
|
.with_target(false)
|
|
.with_file(true)
|
|
.with_line_number(true)
|
|
.without_time()
|
|
.compact()
|
|
.init();
|
|
color_eyre::install()?;
|
|
|
|
let input = parse_input("inputs/day03.txt");
|
|
|
|
info!("Part 1: {}", part_1(&input));
|
|
info!("Part 2: {}", part_2(&input));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod day_03 {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_part_1() {
|
|
let sample = parse_input("samples/day03.txt");
|
|
assert_eq!(part_1(&sample), 157);
|
|
}
|
|
|
|
#[test]
|
|
fn test_part_2() {
|
|
let sample = parse_input("samples/day03.txt");
|
|
assert_eq!(part_2(&sample), 70);
|
|
}
|
|
}
|