use crate::util::iter::*;
use crate::util::parse::*;
use Kind::*;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Kind {
Sand,
Moving,
Stopped,
}
pub struct Scan {
width: usize,
top: usize,
bottom: usize,
kind: Vec<Kind>,
moving: usize,
stopped: usize,
}
pub fn parse(input: &str) -> Scan {
let first = input.lines().map(|line| line.as_bytes()[0]);
let second = input.iter_unsigned::<usize>().chunk::<3>();
let clay: Vec<_> = first.zip(second).collect();
let mut min_x = usize::MAX;
let mut max_x = 0;
let mut min_y = usize::MAX;
let mut max_y = 0;
for &(direction, triple) in &clay {
let (x1, x2, y1, y2) = if direction == b'x' {
let [x, y1, y2] = triple;
(x, x, y1, y2)
} else {
let [y, x1, x2] = triple;
(x1, x2, y, y)
};
min_x = min_x.min(x1);
max_x = max_x.max(x2);
min_y = min_y.min(y1);
max_y = max_y.max(y2);
}
let width = max_x - min_x + 3;
let top = width * min_y;
let bottom = width * (max_y + 1);
let mut kind = vec![Sand; bottom];
for (direction, triple) in clay {
if direction == b'x' {
let [x, y1, y2] = triple;
for y in y1..y2 + 1 {
kind[(width * y) + (x - min_x + 1)] = Stopped;
}
} else {
let [y, x1, x2] = triple;
for x in x1..x2 + 1 {
kind[(width * y) + (x - min_x + 1)] = Stopped;
}
}
}
let mut scan = Scan { width, top, bottom, kind, moving: 0, stopped: 0 };
flow(&mut scan, 500 - min_x + 1);
scan
}
pub fn part1(input: &Scan) -> usize {
input.moving + input.stopped
}
pub fn part2(input: &Scan) -> usize {
input.stopped
}
fn flow(scan: &mut Scan, index: usize) -> Kind {
if index >= scan.bottom {
Moving
} else if scan.kind[index] != Sand {
scan.kind[index]
} else if flow(scan, index + scan.width) == Moving {
scan.kind[index] = Moving;
if index >= scan.top {
scan.moving += 1;
}
Moving
} else {
let mut left = index;
let mut right = index;
while scan.kind[left - 1] == Sand && flow(scan, left + scan.width) == Stopped {
left -= 1;
}
while scan.kind[right + 1] == Sand && flow(scan, right + scan.width) == Stopped {
right += 1;
}
if scan.kind[left - 1] == Stopped && scan.kind[right + 1] == Stopped {
for index in left..right + 1 {
scan.kind[index] = Stopped;
}
if index >= scan.top {
scan.stopped += right + 1 - left;
}
Stopped
} else {
for index in left..right + 1 {
scan.kind[index] = Moving;
}
if index >= scan.top {
scan.moving += right + 1 - left;
}
Moving
}
}
}