summaryrefslogtreecommitdiff
path: root/2023/src/bin/day_22.rs
blob: 750f9757753226f414fd64bca1b0631c5cebcbff (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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use nalgebra::Point3;
use nom::{
    bytes::complete::tag,
    character::complete::{line_ending, u32},
    combinator::map,
    multi::separated_list1,
    sequence::{separated_pair, tuple},
    IResult,
};
use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let input = fs::read_to_string("inputs/day_22.txt")?;
    let pile = BrickPile::parser(&input).unwrap().1;
    let settled_pile = pile.settle();
    let brickfall_sum = settled_pile.brickfall_sum();
    dbg!(&brickfall_sum.count_disintegratable_blocks());
    dbg!(&brickfall_sum.sum_brickfalls());

    Ok(())
}

#[derive(Debug)]
struct BrickPile(Vec<Brick>);

#[derive(Debug)]
struct SettledBrickPile {
    bricks: Vec<Brick>,
    settled_count: usize,
}

#[derive(Debug, Clone)]
struct Brick {
    bottom: Point3<u32>, // the lowest z will always be here. The top might still have the same z.
    top: Point3<u32>,
}

#[derive(Debug)]
struct BrickfallSum(Vec<usize>);

impl BrickPile {
    fn parser(input: &str) -> IResult<&str, Self> {
        map(separated_list1(line_ending, Brick::parser), BrickPile)(input)
    }

    fn settle(&self) -> SettledBrickPile {
        let mut settled_bricks = self.0.clone();
        let mut has_fallen = vec![false; settled_bricks.len()];

        let mut all_settled = false;

        while !all_settled {
            all_settled = true;
            for self_i in 0..settled_bricks.len() {
                let this_brick_is_resting_on_something = settled_bricks[self_i]
                    .is_resting_on_ground()
                    || (0..settled_bricks.len()).any(|other_i| {
                        self_i != other_i
                            && settled_bricks[self_i].is_resting_on_other(&settled_bricks[other_i])
                    });

                if !this_brick_is_resting_on_something {
                    settled_bricks[self_i].fall_one();
                    has_fallen[self_i] = true;
                    all_settled = false;
                }
            }
        }

        SettledBrickPile {
            bricks: settled_bricks,
            settled_count: has_fallen.iter().filter(|f| **f).count(),
        }
    }
}

impl Brick {
    fn parser(input: &str) -> IResult<&str, Self> {
        map(
            separated_pair(point_parser, tag("~"), point_parser),
            |(a, b)| {
                if a.z < b.z {
                    Brick { bottom: a, top: b }
                } else {
                    Brick { top: a, bottom: b }
                }
            },
        )(input)
    }

    fn min_x(&self) -> u32 {
        self.bottom.x.min(self.top.x)
    }

    fn max_x(&self) -> u32 {
        self.bottom.x.max(self.top.x)
    }

    fn min_y(&self) -> u32 {
        self.bottom.y.min(self.top.y)
    }

    fn max_y(&self) -> u32 {
        self.bottom.y.max(self.top.y)
    }

    fn is_resting_on_ground(&self) -> bool {
        self.bottom.z == 1
    }

    fn is_resting_on_other(&self, other: &Self) -> bool {
        self.bottom.z == other.top.z + 1
            && self.min_x() <= other.max_x()
            && other.min_x() <= self.max_x()
            && self.min_y() <= other.max_y()
            && other.min_y() <= self.max_y()
    }

    fn fall_one(&mut self) {
        self.bottom.z -= 1;
        self.top.z -= 1;
    }
}

fn point_parser(input: &str) -> IResult<&str, Point3<u32>> {
    map(
        tuple((u32, tag(","), u32, tag(","), u32)),
        |(x, _, y, _, z)| Point3::new(x, y, z),
    )(input)
}

impl SettledBrickPile {
    fn brickfall_sum(&self) -> BrickfallSum {
        BrickfallSum(
            (0..self.bricks.len())
                .map(|self_i| {
                    self.count_bricks_that_would_fall_if_this_one_is_disintegrated(self_i)
                })
                .collect(),
        )
    }

    fn count_bricks_that_would_fall_if_this_one_is_disintegrated(&self, i: usize) -> usize {
        let mut unsettled_bricks = self.bricks.clone();
        unsettled_bricks.remove(i);
        let unsettled_bricks = BrickPile(unsettled_bricks);

        let resettled = unsettled_bricks.settle();
        resettled.settled_count
    }
}

impl BrickfallSum {
    fn count_disintegratable_blocks(&self) -> usize {
        self.0.iter().filter(|fallen| **fallen == 0).count()
    }

    fn sum_brickfalls(&self) -> usize {
        self.0.iter().sum()
    }
}