summaryrefslogtreecommitdiff
path: root/2022/src/bin/day_3.rs
blob: ec2d5e29debc2f01838c39344934b8fab3212d20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use nom::{
    character::complete::{line_ending, satisfy},
    combinator::map,
    multi::{many1, separated_list1},
    IResult,
};
use std::{collections::BTreeSet, fs, iter::Iterator};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let input = fs::read_to_string("inputs/day_3.txt")?;
    let rucksacks = Rucksacks::parser(&input).unwrap().1;
    dbg!(rucksacks.disorder_sum());
    dbg!(rucksacks.groups_sum());
    Ok(())
}

#[derive(Debug, PartialEq, Eq, Clone)]
struct Rucksacks(Vec<Rucksack>);

#[derive(Debug, PartialEq, Eq, Clone)]
struct Rucksack {
    front: BTreeSet<ItemType>,
    back: BTreeSet<ItemType>,
}

#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
struct ItemType(char);

impl Rucksacks {
    fn parser(input: &str) -> IResult<&str, Rucksacks> {
        map(separated_list1(line_ending, Rucksack::parser), Rucksacks)(input)
    }

    fn disorder_sum(&self) -> u32 {
        self.0.iter().map(|r| r.intersection_priority()).sum()
    }

    fn groups_sum(&self) -> u32 {
        self.0
            .chunks(3)
            .map(|group| {
                let mut overlap = group[0].union();
                for m in group.iter().skip(1) {
                    overlap = overlap.intersection(&m.union()).cloned().collect();
                }
                overlap.iter().map(|c| c.priority()).sum::<u32>()
            })
            .sum()
    }
}

impl Rucksack {
    fn parser(input: &str) -> IResult<&str, Rucksack> {
        map(many1(ItemType::parser), Rucksack::new)(input)
    }

    fn new(contents: Vec<ItemType>) -> Rucksack {
        let mid = contents.len() / 2;
        let front_str = &contents[0..mid];
        let back_str = &contents[mid..];

        let mut front = BTreeSet::new();
        for c in front_str {
            front.insert(c.clone());
        }

        let mut back = BTreeSet::new();
        for c in back_str {
            back.insert(c.clone());
        }

        Rucksack { front, back }
    }

    fn intersection(&self) -> BTreeSet<ItemType> {
        self.front.intersection(&self.back).cloned().collect()
    }

    fn union(&self) -> BTreeSet<ItemType> {
        self.front.union(&self.back).cloned().collect()
    }

    fn intersection_priority(&self) -> u32 {
        self.intersection().iter().map(|c| c.priority()).sum()
    }
}

impl ItemType {
    fn parser(input: &str) -> IResult<&str, ItemType> {
        map(satisfy(|c| c.is_alphabetic()), ItemType)(input)
    }

    fn priority(&self) -> u32 {
        if self.0.is_uppercase() {
            (self.0 as u32 - 'A' as u32) + 27
        } else {
            (self.0 as u32 - 'a' as u32) + 1
        }
    }
}