Files
AoC2022/d18/src/a2.rs
2022-12-18 14:37:01 +01:00

149 lines
4.2 KiB
Rust

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>) -> 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>) -> 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>) -> 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>) -> 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>) -> 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>) -> 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<Pos>, lava :&HashSet<Pos>, min :&Pos, max :&Pos) {
let mut queue :VecDeque<Pos> = 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<String>) {
// parse input
let tmp :Vec<Vec<i32>> = inp.iter().map(|elem| elem.split(',').map(|num| num.parse::<i32>().unwrap()).collect()).collect();
let droplets :HashSet<Pos> = 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<Pos> = 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<Pos> = 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);
}