From 178e2e50117b6c5f05c5af1696dcad1376663d1a Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Sat, 17 Dec 2022 22:02:11 -0800 Subject: [PATCH] 2022 Day 18 Part 2 --- 2022/src/bin/day18.rs | 174 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 154 insertions(+), 20 deletions(-) diff --git a/2022/src/bin/day18.rs b/2022/src/bin/day18.rs index 1a34fee..3c8139e 100644 --- a/2022/src/bin/day18.rs +++ b/2022/src/bin/day18.rs @@ -1,20 +1,15 @@ +use std::collections::{HashMap, VecDeque}; + const INPUT: &str = include_str!("../../input/day18.txt"); fn main() { - println!("{}", part1(INPUT)); + let cubes = parse(INPUT); + let open_faces = part1(cubes.clone()); + println!("Part 1: {}", open_faces); + println!("Part 2: {}", part2(cubes, open_faces)); } -fn part1(input: &str) -> usize { - let cubes: Vec = aoc::lines(input) - .map(|line| { - let coords: Vec<&str> = line.split(',').collect(); - let x = coords[0].parse().unwrap(); - let y = coords[1].parse().unwrap(); - let z = coords[2].parse().unwrap(); - Cube::new(x, y, z) - }) - .collect(); - +fn part1(cubes: Vec) -> usize { let mut shared_faces = 0; for (c1, c2) in itertools::iproduct!(&cubes, &cubes) { if c1 == c2 { @@ -29,22 +24,161 @@ fn part1(input: &str) -> usize { 6 * cubes.len() - shared_faces } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +fn part2(cubes: Vec, open_faces: usize) -> usize { + let min = cubes.iter().map(Cube::min_coord).min().unwrap(); + let max = cubes.iter().map(Cube::max_coord).max().unwrap(); + + let mut grid: HashMap = cubes + .iter() + .cloned() + .map(|c| (c, Material::Water)) + .collect(); + + let lo = Cube { + x: min - 1, + y: min - 1, + z: min - 1, + }; + + let hi = Cube { + x: max + 1, + y: max + 1, + z: max + 1, + }; + + grid.insert(lo, Material::Steam); + grid.insert(hi, Material::Steam); + + steam(&mut grid, lo, hi); + air(&mut grid, lo, hi); + + let mut insulated_faces = 0; + for (&cube, &mat) in &grid { + if mat == Material::Air { + for neighbor in cube.neighbors() { + if let Some(Material::Water) = grid.get(&neighbor) { + insulated_faces += 1; + } + } + } + } + + open_faces - insulated_faces +} + +fn steam(grid: &mut HashMap, lo: Cube, hi: Cube) { + let mut queue: VecDeque = grid + .iter() + .filter_map(|(&cube, &mat)| { + if mat == Material::Steam { + Some(cube) + } else { + None + } + }) + .collect(); + + while let Some(src) = queue.pop_front() { + for dst in src.neighbors() { + if dst.out_of_bounds(lo, hi) { + continue; + } + + if let Some(_mat) = grid.get(&dst) { + continue; + } + + let old = grid.insert(dst, Material::Steam); + assert!(old.is_none()); + + queue.push_back(dst); + } + } +} + +fn air(grid: &mut HashMap, lo: Cube, hi: Cube) { + for (x, y, z) in itertools::iproduct!(lo.x..=hi.x, lo.y..=hi.y, lo.z..=hi.z) { + let cube = Cube::new(x, y, z); + grid.entry(cube).or_insert(Material::Air); + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +enum Material { + Water, + Steam, + Air, +} + +fn parse(input: &str) -> Vec { + aoc::lines(input) + .map(|line| { + let coords: Vec<&str> = line.split(',').collect(); + let x = coords[0].parse().unwrap(); + let y = coords[1].parse().unwrap(); + let z = coords[2].parse().unwrap(); + Cube::new(x, y, z) + }) + .collect() +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] struct Cube { - x: u32, - y: u32, - z: u32, + x: i32, + y: i32, + z: i32, } impl Cube { - fn new(x: u32, y: u32, z: u32) -> Self { + fn new(x: i32, y: i32, z: i32) -> Self { Self { x, y, z } } fn touches(&self, other: &Self) -> bool { - let dx = (self.x as i32 - other.x as i32).abs(); - let dy = (self.y as i32 - other.y as i32).abs(); - let dz = (self.z as i32 - other.z as i32).abs(); + let dx = (self.x - other.x).abs(); + let dy = (self.y - other.y).abs(); + let dz = (self.z - other.z).abs(); (dx + dy + dz) == 1 } + + fn max_coord(&self) -> i32 { + (self.x).max(self.y).max(self.z) + } + + fn min_coord(&self) -> i32 { + (self.x).min(self.y).min(self.z) + } + + fn neighbors(&self) -> Vec { + let deltas = vec![ + (-1, 0, 0), + (0, -1, 0), + (0, 0, -1), + (1, 0, 0), + (0, 1, 0), + (0, 0, 1), + ]; + + deltas + .iter() + .map(|(dx, dy, dz)| Self { + x: self.x + dx, + y: self.y + dy, + z: self.z + dz, + }) + .collect() + } + + fn out_of_bounds(&self, lo: Self, hi: Self) -> bool { + vec![ + self.x < lo.x, + self.y < lo.y, + self.z < lo.z, + self.x > hi.x, + self.y > hi.y, + self.z > hi.z, + ] + .iter() + .any(|b| *b) + } }