use std::{collections::{HashSet, VecDeque, vec_deque}, hash::Hash, thread}; #[derive(Hash, PartialEq, Eq, Copy, Clone)] struct Pos { x: i32, y: i32, z: i32, } impl Pos { fn from(x :i32, y :i32, z :i32) -> Self { Pos { x: x, y: y, z: z } } } fn find_zmax(positions :&HashSet) -> Pos { let mut max = Pos::from(0, 0, i32::MIN); positions.iter().for_each(|pos| {if pos.z > max.z { max = *pos; }}); max } fn find_zmin(positions :&HashSet) -> Pos { let mut max = Pos::from(0, 0, i32::MAX); positions.iter().for_each(|pos| {if pos.z < max.z { max = *pos; }}); max } fn find_ymax(positions :&HashSet) -> Pos { let mut max = Pos::from(0, i32::MIN, 0); positions.iter().for_each(|pos| {if pos.y > max.y { max = *pos; }}); max } fn find_ymin(positions :&HashSet) -> Pos { let mut max = Pos::from(0, i32::MAX, 0); positions.iter().for_each(|pos| {if pos.y < max.y { max = *pos; }}); max } fn find_xmax(positions :&HashSet) -> Pos { let mut max = Pos::from(i32::MIN, 0, 0); positions.iter().for_each(|pos| {if pos.x > max.x { max = *pos; }}); max } fn find_xmin(positions :&HashSet) -> Pos { let mut max = Pos::from(i32::MAX, 0, 0); positions.iter().for_each(|pos| {if pos.x < max.x { max = *pos; }}); max } fn flood_fill(start :Pos, map :&mut HashSet, lava :&HashSet, min :&Pos, max :&Pos) { let mut queue :VecDeque = VecDeque::new(); queue.push_back(start); while !queue.is_empty() { let cp = queue.pop_front().unwrap(); if !(cp.x > min.x && cp.x < max.x && cp.y > min.y && cp.y < max.y && cp.z > min.z && cp.z < max.z) { continue; } if map.contains(&cp) { continue; } if lava.contains(&cp) { continue; } map.insert(cp); queue.push_back(Pos::from(cp.x - 1, cp.y, cp.z)); queue.push_back(Pos::from(cp.x + 1, cp.y, cp.z)); queue.push_back(Pos::from(cp.x, cp.y - 1, cp.z)); queue.push_back(Pos::from(cp.x, cp.y + 1, cp.z)); queue.push_back(Pos::from(cp.x, cp.y, cp.z - 1)); queue.push_back(Pos::from(cp.x, cp.y, cp.z + 1)); } } pub fn run(inp :Vec) { // parse input let tmp :Vec> = inp.iter().map(|elem| elem.split(',').map(|num| num.parse::().unwrap()).collect()).collect(); let droplets :HashSet = tmp.iter().map(|drop| Pos::from(drop[0], drop[1], drop[2])).collect(); // highest, lowest, leftmost, rightmost, furthest and nearest droplet // construct a box around the lava at each sides maximum + 2 // floodfill from a starting pooint (for example higest_point+1) // go through all filled pixels and check all 6 sides if there is a pixel // find edges (treat them as WALL) let z_max = find_zmax(&droplets); let z_min = find_zmin(&droplets); let y_max = find_ymax(&droplets); let y_min = find_ymin(&droplets); let x_max = find_xmax(&droplets); let x_min = find_xmin(&droplets); let min_edges = Pos::from(x_min.x - 3, y_min.y - 3, z_min.z - 3); let max_edges = Pos::from(x_max.x + 3, y_max.y + 3, z_max.z + 3); let mut filled :HashSet = HashSet::new(); // FloodFill the rest in a new HashSet flood_fill(Pos::from(z_max.x, z_max.y, z_max.z+1), &mut filled, &droplets, &min_edges, &max_edges); // check all sides of all flooded pixels. If a side has a LAVA pixel next to it count +1 let mut side_counter = 0; for dp in &filled { // check each side if it is free // if it is free then count up by 1 // else dont let sides :Vec = vec![ Pos::from(dp.x-1, dp.y, dp.z), Pos::from(dp.x+1, dp.y, dp.z), Pos::from(dp.x, dp.y-1, dp.z), Pos::from(dp.x, dp.y+1, dp.z), Pos::from(dp.x, dp.y, dp.z-1), Pos::from(dp.x, dp.y, dp.z+1), ]; for side in sides { if droplets.contains(&side) { side_counter += 1; } } } println!("a2: {}", side_counter); }