|
| 1 | +use utils::prelude::*; |
| 2 | + |
| 3 | +/// Simulating water flow in a 2D grid. |
| 4 | +#[derive(Clone, Debug)] |
| 5 | +pub struct Day17 { |
| 6 | + part1: u32, |
| 7 | + part2: u32, |
| 8 | +} |
| 9 | + |
| 10 | +#[repr(u8)] |
| 11 | +#[derive(Clone, Copy, Debug, PartialEq)] |
| 12 | +enum State { |
| 13 | + Empty, |
| 14 | + Flowing, |
| 15 | + Filled, |
| 16 | +} |
| 17 | + |
| 18 | +impl Day17 { |
| 19 | + pub fn new(input: &str, _: InputType) -> Result<Self, InputError> { |
| 20 | + let segments = parser::byte_range(b'x'..=b'y') |
| 21 | + .with_suffix(b'=') |
| 22 | + .then(parser::number_range(1..=2999).with_suffix(", ")) |
| 23 | + .then(parser::byte_range(b'x'..=b'y').with_suffix(b'=')) |
| 24 | + .then(parser::number_range(1..=2999).with_suffix("..")) |
| 25 | + .then(parser::number_range(1..=2999)) |
| 26 | + .map_res(|(axis1, c1, axis2, c2, c3)| { |
| 27 | + if axis1 == axis2 { |
| 28 | + Err("expected line segment") |
| 29 | + } else if c2 > c3 { |
| 30 | + Err("expected range to be sorted") |
| 31 | + } else { |
| 32 | + Ok((axis1, c1, c2, c3)) |
| 33 | + } |
| 34 | + }) |
| 35 | + .parse_lines(input)?; |
| 36 | + |
| 37 | + if segments.is_empty() { |
| 38 | + return Err(InputError::new(input, 0, "expected at least one line")); |
| 39 | + } |
| 40 | + |
| 41 | + let (mut x_min, mut x_max, mut y_min, mut y_max) = (500, 500, usize::MAX, 0); |
| 42 | + for &(axis, c1, c2, c3) in &segments { |
| 43 | + if axis == b'x' { |
| 44 | + x_min = x_min.min(c1); |
| 45 | + x_max = x_max.max(c1); |
| 46 | + y_min = y_min.min(c2); |
| 47 | + y_max = y_max.max(c3); |
| 48 | + } else { |
| 49 | + x_min = x_min.min(c2); |
| 50 | + x_max = x_max.max(c3); |
| 51 | + y_min = y_min.min(c1); |
| 52 | + y_max = y_max.max(c1); |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + // Reserve the top row for the spring |
| 57 | + y_min -= 1; |
| 58 | + // Padding to avoid wrapping around rows |
| 59 | + x_min -= 1; |
| 60 | + x_max += 1; |
| 61 | + |
| 62 | + let width = x_max - x_min + 1; |
| 63 | + let height = y_max - y_min + 1; |
| 64 | + let mut grid = vec![State::Empty; width * height]; |
| 65 | + for &(axis, c1, c2, c3) in &segments { |
| 66 | + if axis == b'x' { |
| 67 | + let x = c1 - x_min; |
| 68 | + for y in c2 - y_min..=c3 - y_min { |
| 69 | + grid[y * width + x] = State::Filled; |
| 70 | + } |
| 71 | + } else { |
| 72 | + let y = c1 - y_min; |
| 73 | + for x in c2 - x_min..=c3 - x_min { |
| 74 | + grid[y * width + x] = State::Filled; |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + let mut counts = [0; 3]; |
| 80 | + Self::flow(&mut grid, width, &mut counts, 500 - x_min); |
| 81 | + |
| 82 | + Ok(Self { |
| 83 | + part1: counts[State::Filled as usize] + counts[State::Flowing as usize], |
| 84 | + part2: counts[State::Filled as usize], |
| 85 | + }) |
| 86 | + } |
| 87 | + |
| 88 | + fn flow(grid: &mut [State], width: usize, counts: &mut [u32; 3], index: usize) -> State { |
| 89 | + if index >= grid.len() { |
| 90 | + return State::Flowing; |
| 91 | + } |
| 92 | + if grid[index] != State::Empty { |
| 93 | + return grid[index]; |
| 94 | + } |
| 95 | + |
| 96 | + if Self::flow(grid, width, counts, index + width) == State::Flowing { |
| 97 | + grid[index] = State::Flowing; |
| 98 | + if index >= width { |
| 99 | + counts[State::Flowing as usize] += 1; |
| 100 | + } |
| 101 | + return State::Flowing; |
| 102 | + } |
| 103 | + |
| 104 | + let mut left = index; |
| 105 | + while grid[left - 1] == State::Empty { |
| 106 | + left -= 1; |
| 107 | + if Self::flow(grid, width, counts, left + width) == State::Flowing { |
| 108 | + break; |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + let mut right = index; |
| 113 | + while grid[right + 1] == State::Empty { |
| 114 | + right += 1; |
| 115 | + if Self::flow(grid, width, counts, right + width) == State::Flowing { |
| 116 | + break; |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + let state = if grid[left - 1] == State::Filled && grid[right + 1] == State::Filled { |
| 121 | + State::Filled |
| 122 | + } else { |
| 123 | + State::Flowing |
| 124 | + }; |
| 125 | + |
| 126 | + grid[left..=right].fill(state); |
| 127 | + if index >= width { |
| 128 | + counts[state as usize] += (right - left + 1) as u32; |
| 129 | + } |
| 130 | + state |
| 131 | + } |
| 132 | + |
| 133 | + #[must_use] |
| 134 | + pub fn part1(&self) -> u32 { |
| 135 | + self.part1 |
| 136 | + } |
| 137 | + |
| 138 | + #[must_use] |
| 139 | + pub fn part2(&self) -> u32 { |
| 140 | + self.part2 |
| 141 | + } |
| 142 | +} |
| 143 | + |
| 144 | +examples!(Day17 -> (u32, u32) [ |
| 145 | + { |
| 146 | + input: "x=495, y=2..7\n\ |
| 147 | + y=7, x=495..501\n\ |
| 148 | + x=501, y=3..7\n\ |
| 149 | + x=498, y=2..4\n\ |
| 150 | + x=506, y=1..2\n\ |
| 151 | + x=498, y=10..13\n\ |
| 152 | + x=504, y=10..13\n\ |
| 153 | + y=13, x=498..504", |
| 154 | + part1: 57, |
| 155 | + part2: 29, |
| 156 | + }, |
| 157 | +]); |
0 commit comments