use std::collections::HashSet; use std::collections::HashMap; use std::convert::TryInto; #[path = "../lib.rs"] mod lib; fn get_lava() -> HashSet<[usize; 3]> { let mut lava = HashSet::new(); for line in lib::iter_input() { let cube: [usize; 3] = line .split(",") // add 1 so we have space for the steam to flow at 0 .map(|s| s.parse::().unwrap() + 1) .collect::>() .try_into() .unwrap(); lava.insert(cube); } return lava; } fn get_surface(lava: &HashSet<[usize; 3]>) -> usize { let lava_x: Vec<[usize; 3]> = lava.iter().map(|cube| [cube[0] + 1, cube[1], cube[2]]).collect(); let lava_y: Vec<[usize; 3]> = lava.iter().map(|cube| [cube[0], cube[1] + 1, cube[2]]).collect(); let lava_z: Vec<[usize; 3]> = lava.iter().map(|cube| [cube[0], cube[1], cube[2] + 1]).collect(); let mut faces_x = HashMap::new(); let mut faces_y = HashMap::new(); let mut faces_z = HashMap::new(); for cube in lava.iter() { faces_x.entry(cube).and_modify(|n| *n += 1).or_insert(1); faces_y.entry(cube).and_modify(|n| *n += 1).or_insert(1); faces_z.entry(cube).and_modify(|n| *n += 1).or_insert(1); } for cube in lava_x.iter() { faces_x.entry(cube).and_modify(|n| *n += 1).or_insert(1); } for cube in lava_y.iter() { faces_y.entry(cube).and_modify(|n| *n += 1).or_insert(1); } for cube in lava_z.iter() { faces_z.entry(cube).and_modify(|n| *n += 1).or_insert(1); } return faces_x.values().filter(|n| **n == 1).count() + faces_y.values().filter(|n| **n == 1).count() + faces_z.values().filter(|n| **n == 1).count(); } fn part2(lava: &HashSet<[usize; 3]>) -> usize { let mut steam = HashSet::new(); let size_x = lava.iter().map(|c| c[0]).max().unwrap() + 2; let size_y = lava.iter().map(|c| c[1]).max().unwrap() + 2; let size_z = lava.iter().map(|c| c[2]).max().unwrap() + 2; let mut queue = vec![ [0, 0, 0], [0, 0, size_z - 1], [0, size_y - 1, 0], [0, size_y - 1, size_z - 1], [size_x - 1, 0, 0], [size_x - 1, 0, size_z - 1], [size_x - 1, size_y - 1, 0], [size_x - 1, size_y - 1, size_z - 1], ]; while let Some(cube) = queue.pop() { if !steam.contains(&cube) && !lava.contains(&cube) { steam.insert(cube); let [x, y, z] = cube; if x > 0 { queue.push([x - 1, y, z]); } if x + 1 < size_x { queue.push([x + 1, y, z]); } if y > 0 { queue.push([x, y - 1, z]); } if y + 1 < size_y { queue.push([x, y + 1, z]); } if z > 0 { queue.push([x, y, z - 1]); } if z + 1 < size_z { queue.push([x, y, z + 1]); } } } return get_surface(&steam) - ( size_x * size_y + size_x * size_z + size_y * size_z ) * 2; } fn main() { let lava = get_lava(); println!("part1: {}", get_surface(&lava)); println!("part2: {}", part2(&lava)); }