+enum FloodResult {
+ TouchingLimits,
+ InnerSurface(i32),
+}
+
+fn flood(m: &mut Mat3D, x: i32, y: i32, z: i32) -> FloodResult {
+ let mut to_visit = vec![(x, y, z)];
+ let mut surface = 0;
+ let mut touching_limits = false;
+
+ while let Some((x, y, z)) = to_visit.pop() {
+ if let Element::Droplet = m[x as usize][y as usize][z as usize] {
+ continue;
+ }
+
+ m[x as usize][y as usize][z as usize] = Element::Droplet;
+ for (dx, dy, dz) in NEIGHBOURS {
+ let (x, y, z) = (x + dx, y + dy, z + dz);
+ if x < 0
+ || x >= m.len() as i32
+ || y < 0
+ || y >= m[x as usize].len() as i32
+ || z < 0
+ || z >= m[x as usize][y as usize].len() as i32
+ {
+ touching_limits = true;
+ continue;
+ }
+ match m[x as usize][y as usize][z as usize] {
+ Element::Empty => to_visit.push((x, y, z)),
+ Element::Obsidian => surface += 1,
+ Element::Droplet => (),
+ }
+ }
+ }
+
+ if touching_limits {
+ FloodResult::TouchingLimits
+ } else {
+ FloodResult::InnerSurface(surface)
+ }
+}
+
+pub fn surface_without_trapped_air(outer_surface: i32, mut m: Mat3D) -> i32 {
+ let mut inner_surface = 0;
+ for x in 0..m.len() {
+ for y in 0..m[x].len() {
+ for z in 0..m[x][y].len() {
+ if let Element::Empty = m[x][y][z] {
+ if let FloodResult::InnerSurface(s) =
+ flood(&mut m, x as i32, y as i32, z as i32)
+ {
+ inner_surface += s;
+ }
+ }
+ }
+ }
+ }
+
+ outer_surface - inner_surface