[TASK] Completed Day 18

This commit is contained in:
2023-01-29 21:36:15 +01:00
parent 5424772424
commit 9c2793324a
4 changed files with 3102 additions and 0 deletions

2881
input/day18.txt Normal file

File diff suppressed because it is too large Load Diff

13
input/day18_example.txt Normal file
View File

@@ -0,0 +1,13 @@
2,2,2
1,2,2
3,2,2
2,1,2
2,3,2
2,2,1
2,2,3
2,2,4
2,2,6
1,2,5
3,2,5
2,1,5
2,3,5

205
src/day18.rs Normal file
View File

@@ -0,0 +1,205 @@
use std::collections::{HashSet};
use crate::day18::VoxelState::{Air, Unknown};
use crate::day_solver::DaySolver;
use super::util;
pub struct Day18 {
voxels: HashSet<Vec3>
}
impl Day18 {
pub fn create() -> Self {
// let lines = util::read_file("input/day18_example.txt");
let lines = util::read_file("input/day18.txt");
let voxels = lines.iter().map(|l| {
let mut c_iter = l.split(",").map(|c| c.parse().unwrap());
Vec3 {
x: c_iter.next().unwrap(),
y: c_iter.next().unwrap(),
z: c_iter.next().unwrap()
}
}).collect::<HashSet<Vec3>>();
// Put the input into the day struct
return Day18 { voxels }
}
}
impl DaySolver for Day18 {
fn solve_part1(&mut self) -> String {
let mut sides = 0;
for voxel in &self.voxels {
// We just scan all sides if there are voxels there
if !self.voxels.contains(&voxel.moved(-1, 0, 0)) { sides += 1 };
if !self.voxels.contains(&voxel.moved(1, 0, 0)) { sides += 1 };
if !self.voxels.contains(&voxel.moved(0, -1, 0)) { sides += 1 };
if !self.voxels.contains(&voxel.moved(0, 1, 0)) { sides += 1 };
if !self.voxels.contains(&voxel.moved(0, 0, -1)) { sides += 1 };
if !self.voxels.contains(&voxel.moved(0, 0, 1)) { sides += 1 };
}
return sides.to_string();
}
fn solve_part2(&mut self) -> String {
// We need to find the cubes that can't be reached
let x_min = self.voxels.iter().map(|v| v.x).min().unwrap();
let x_max = self.voxels.iter().map(|v| v.x).max().unwrap();
let y_min = self.voxels.iter().map(|v| v.y).min().unwrap();
let y_max = self.voxels.iter().map(|v| v.y).max().unwrap();
let z_min = self.voxels.iter().map(|v| v.z).min().unwrap();
let z_max = self.voxels.iter().map(|v| v.z).max().unwrap();
let width = (x_max + 1 - x_min) as usize;
let height = (y_max + 1 - y_min) as usize;
let depth = (z_max + 1 - z_min) as usize;
let mut grid = Grid3D {
data: vec![Unknown; width * depth * height],
width,
height,
depth,
offset: Vec3 { x: x_min, y: y_min, z: z_min },
max: Vec3{ x: x_max, y: y_max, z: z_max}
};
for vox in &self.voxels {
grid.set(&vox.x, &vox.y, &vox.z, VoxelState::Rock);
}
let mut queue = Vec::new();
// Now we mark all voxels on the edge as air (if they're not rock)
for x in grid.offset.x..grid.max.x+1 {
for y in grid.offset.y..grid.max.y+1 {
queue_if_unknown(&mut grid, &mut queue, &x, &y, &z_min);
queue_if_unknown(&mut grid, &mut queue, &x, &y, &z_max);
}
for z in grid.offset.z..grid.max.z+1 {
queue_if_unknown(&mut grid, &mut queue, &x, &y_min, &z);
queue_if_unknown(&mut grid, &mut queue, &x, &y_max, &z);
}
}
for z in z_min..z_max + 1 {
for y in y_min..y_max + 1 {
queue_if_unknown(&mut grid, &mut queue, &x_min, &y, &z);
queue_if_unknown(&mut grid, &mut queue, &x_max, &y, &z);
}
}
// We search one extra cube around the actual grid, so that the search doesn't get blocked
// by rocks hitting the outer edge:
queue.push(grid.offset.moved(-1, 0, 0));
while let Some(pos) = queue.pop() {
// If we got here, it means that "pos" is in air, and we need to check if the neighbors are as well
try_pos(&mut grid, &mut queue, &pos, 1, 0, 0);
try_pos(&mut grid, &mut queue, &pos, -1, 0, 0);
try_pos(&mut grid, &mut queue, &pos, 0, 1, 0);
try_pos(&mut grid, &mut queue, &pos, 0, -1, 0);
try_pos(&mut grid, &mut queue, &pos, 0, 0, 1);
try_pos(&mut grid, &mut queue, &pos, 0, 0, -1);
}
// and finally, we do the same check (basically) as in pt 1:
let mut sides = 0;
for voxel in &self.voxels {
// We just scan all sides if there are voxels there
if grid.get(&(voxel.x + 1), &voxel.y, &voxel.z) == &Air { sides += 1 };
if grid.get(&(voxel.x - 1), &voxel.y, &voxel.z) == &Air { sides += 1 };
if grid.get(&voxel.x, &(voxel.y + 1), &voxel.z) == &Air { sides += 1 };
if grid.get(&voxel.x, &(voxel.y - 1), &voxel.z) == &Air { sides += 1 };
if grid.get(&voxel.x, &voxel.y, &(voxel.z + 1)) == &Air { sides += 1 };
if grid.get(&voxel.x, &voxel.y, &(voxel.z - 1)) == &Air { sides += 1 };
}
// println!("{:?}", grid.data);
// return grid.data.iter().filter(|v| v == &&Unknown).count().to_string();
sides.to_string()
}
}
fn try_pos(grid: &mut Grid3D, queue: &mut Vec<Vec3>, pos: &Vec3, offset_x: i32, offset_y: i32, offset_z: i32) {
let x = pos.x + offset_x;
let y = pos.y + offset_y;
let z = pos.z + offset_z;
queue_if_unknown(grid, queue, &x, &y, &z);
}
fn queue_if_unknown(grid: &mut Grid3D, queue: &mut Vec<Vec3>, x: &i32, y: &i32, z: &i32) {
if grid.get(x, y, z) == &Unknown {
grid.set(x, y, z, Air);
queue.push(Vec3::new(*x, *y, *z))
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
struct Vec3 {
x: i32,
y: i32,
z: i32
}
impl Vec3 {
fn moved(&self, x: i32, y: i32, z: i32) -> Vec3 {
Vec3 {
x: self.x + x,
y: self.y + y,
z: self.z + z
}
}
fn new(x: i32, y: i32, z: i32) -> Vec3 {
return Vec3 {
x, y, z
}
}
}
#[derive(Debug, PartialEq, Eq)]
struct Grid3D {
data: Vec<VoxelState>,
width: usize,
height: usize,
depth: usize,
offset: Vec3,
max: Vec3
}
impl Grid3D {
fn set(&mut self, x: &i32, y: &i32, z: &i32, value: VoxelState) {
if x < &self.offset.x || y < &self.offset.y || z < &self.offset.z {
panic!("Invalid position to update the grid: ({}, {}, {})", x, y, z);
}
let coord = self.grid_coord(&x, &y, &z);
self.data[coord] = value;
}
fn get(&self, x: &i32, y: &i32, z: &i32) -> &VoxelState {
if x < &self.offset.x || y < &self.offset.y || z < &self.offset.z ||
x > &self.max.x || y > &self.max.y || z > &self.max.z {
&VoxelState::Air
} else {
&self.data[self.grid_coord(x, y, z)]
}
}
fn grid_coord(&self, x: &i32, y: &i32, z: &i32) -> usize {
(x - self.offset.x) as usize + ((y - self.offset.y) as usize * self.width) + ((z - self.offset.z) as usize * self.width * self.height)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
enum VoxelState {
Air, Rock, Unknown
}

View File

@@ -18,6 +18,7 @@ use crate::day14::Day14;
use crate::day15::Day15;
use crate::day16::Day16;
use crate::day17::Day17;
use crate::day18::Day18;
use crate::day_solver::DaySolver;
mod util;
@@ -39,6 +40,7 @@ mod day14;
mod day15;
mod day16;
mod day17;
mod day18;
const MAX_DAY: u8 = 17;
const DEFAULT_BENCHMARK_AMOUNT: u32 = 100;
@@ -121,6 +123,7 @@ fn build_day_solver(day: u8) -> Option<Box<dyn DaySolver>> {
15 => Some(Box::new(Day15::create())),
16 => Some(Box::new(Day16::create())),
17 => Some(Box::new(Day17::create())),
18 => Some(Box::new(Day18::create())),
_ => None
}
}