summaryrefslogtreecommitdiff
path: root/2022/src/bin/day_4.rs
blob: 5f08d701efff3a28b8435d42afa3c14189de5772 (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
use nom::{
    bytes::complete::tag,
    character::complete::{line_ending, u32 as nom_u32},
    combinator::map,
    multi::separated_list1,
    sequence::tuple,
    IResult,
};
use std::{fs, iter::Iterator, ops::RangeInclusive};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let input = fs::read_to_string("inputs/day_4.txt")?;
    let assignments = Assignments::parser(&input).unwrap().1;
    dbg!(assignments.count_containing_assignments());
    dbg!(assignments.count_overlapping_assignments());
    Ok(())
}

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

#[derive(Debug, PartialEq, Eq, Clone)]
struct AssignmentPair {
    elf_1: Assignment,
    elf_2: Assignment,
}

#[derive(Debug, PartialEq, Eq, Clone)]
struct Assignment(RangeInclusive<u32>);

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

    fn count_containing_assignments(&self) -> usize {
        self.0
            .iter()
            .filter(|pair| pair.one_completely_overlaps_other())
            .count()
    }

    fn count_overlapping_assignments(&self) -> usize {
        self.0
            .iter()
            .filter(|pair| pair.one_overlaps_other())
            .count()
    }
}

impl AssignmentPair {
    fn parser(input: &str) -> IResult<&str, AssignmentPair> {
        map(
            tuple((Assignment::parser, tag(","), Assignment::parser)),
            |(elf_1, _, elf_2)| AssignmentPair { elf_1, elf_2 },
        )(input)
    }

    fn one_completely_overlaps_other(&self) -> bool {
        self.elf_1.contains(&self.elf_2) || self.elf_2.contains(&self.elf_1)
    }

    fn one_overlaps_other(&self) -> bool {
        self.elf_1.overlaps(&self.elf_2)
    }
}

impl Assignment {
    fn parser(input: &str) -> IResult<&str, Assignment> {
        map(tuple((nom_u32, tag("-"), nom_u32)), |(start, _, end)| {
            Assignment(RangeInclusive::new(start, end))
        })(input)
    }

    fn contains(&self, other: &Assignment) -> bool {
        self.0.contains(other.0.start()) && self.0.contains(other.0.end())
    }

    fn overlaps(&self, other: &Assignment) -> bool {
        self.0.contains(other.0.start())
            || self.0.contains(other.0.end())
            || other.0.contains(self.0.start())
            || other.0.contains(self.0.end())
    }
}