1use crate::util::iter::*;
14use crate::util::parse::*;
15use Kind::*;
16
17#[derive(Clone, Copy, PartialEq, Eq)]
18enum Kind {
19 Sand,
20 Moving,
21 Stopped,
22}
23
24pub struct Scan {
25 width: usize,
26 top: usize,
27 bottom: usize,
28 kind: Vec<Kind>,
29 moving: usize,
30 stopped: usize,
31}
32
33pub fn parse(input: &str) -> Scan {
34 let first = input.lines().map(|line| line.as_bytes()[0]);
35 let second = input.iter_unsigned::<usize>().chunk::<3>();
36 let clay: Vec<_> = first
37 .zip(second)
38 .map(|(d, [a, b, c])| if d == b'x' { (a, a, b, c) } else { (b, c, a, a) })
39 .collect();
40
41 let min_x = clay.iter().map(|c| c.0).min().unwrap();
43 let max_x = clay.iter().map(|c| c.1).max().unwrap();
44 let min_y = clay.iter().map(|c| c.2).min().unwrap();
45 let max_y = clay.iter().map(|c| c.3).max().unwrap();
46
47 let width = max_x - min_x + 3;
49 let top = width * min_y;
50 let bottom = width * (max_y + 1);
51 let mut kind = vec![Sand; bottom];
52
53 for (x1, x2, y1, y2) in clay {
55 if x1 == x2 {
56 for y in y1..y2 + 1 {
57 kind[width * y + x1 - min_x + 1] = Stopped;
58 }
59 } else {
60 for x in x1..x2 + 1 {
61 kind[width * y1 + x - min_x + 1] = Stopped;
62 }
63 }
64 }
65
66 let mut scan = Scan { width, top, bottom, kind, moving: 0, stopped: 0 };
67 flow(&mut scan, 500 - min_x + 1);
68 scan
69}
70
71pub fn part1(input: &Scan) -> usize {
72 input.moving + input.stopped
73}
74
75pub fn part2(input: &Scan) -> usize {
76 input.stopped
77}
78
79fn flow(scan: &mut Scan, index: usize) -> Kind {
81 if index >= scan.bottom {
82 Moving
84 } else if scan.kind[index] != Sand {
85 scan.kind[index]
87 } else if flow(scan, index + scan.width) == Moving {
88 scan.kind[index] = Moving;
90 if index >= scan.top {
91 scan.moving += 1;
92 }
93 Moving
94 } else {
95 let left = (0..index).rev().find(|&i| !spread(scan, i)).unwrap();
97 let right = (index + 1..scan.bottom).find(|&i| !spread(scan, i)).unwrap();
98
99 if scan.kind[left] == Stopped && scan.kind[right] == Stopped {
100 scan.kind[left + 1..right].fill(Stopped);
101 scan.stopped += right - left - 1;
102 Stopped
103 } else {
104 flow(scan, left);
106 flow(scan, right);
107 scan.kind[left + 1..right].fill(Moving);
108 if index >= scan.top {
109 scan.moving += right - left - 1;
110 }
111 Moving
112 }
113 }
114}
115
116fn spread(scan: &mut Scan, index: usize) -> bool {
120 scan.kind[index] == Sand
121 && (scan.kind[index + scan.width] == Stopped || flow(scan, index + scan.width) == Stopped)
122}