Solution for 2022/day18-part2
This commit is contained in:
parent
7ffb5c4b11
commit
17b38905fb
8
2022/day18-part2/Cargo.toml
Normal file
8
2022/day18-part2/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "day18-part2"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
200
2022/day18-part2/src/main.rs
Normal file
200
2022/day18-part2/src/main.rs
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
// Use a bespoke data structure for very fast access to the volume elements.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Volume {
|
||||||
|
// First bool indicates lava, second bools indicates `visited`.
|
||||||
|
data: Vec<(bool, bool, usize)>,
|
||||||
|
dim: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Volume {
|
||||||
|
fn new(input: &Vec<[usize; 3]>) -> Volume {
|
||||||
|
// Determine the largest index across all three dimensions.
|
||||||
|
// Add 2 because ...
|
||||||
|
// (1) the maximum is an index, and we're looking to spec the size of the vector
|
||||||
|
// (2) we want some air surrounding the volume to easily spec a starting coordinate for the
|
||||||
|
// recursion and to ensure lava at the border of the coordinate system is counted
|
||||||
|
// correctly.
|
||||||
|
let dim = input.iter().flat_map(|a| a.iter()).max().unwrap() + 3;
|
||||||
|
|
||||||
|
// Reserve the memory.
|
||||||
|
let mut v = Volume {
|
||||||
|
data: Vec::with_capacity(dim * dim * dim),
|
||||||
|
dim,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fill the vector with `false` values.
|
||||||
|
v.data.resize(dim * dim * dim, (false, false, 0));
|
||||||
|
|
||||||
|
// Now fill the volume with all the known data.
|
||||||
|
// Offset it by 1 to ensure it's surrounded by air on all sides.
|
||||||
|
for [x, y, z] in input {
|
||||||
|
*v.at_mut(*x + 1, *y + 1, *z + 1) = (true, false, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the volume.
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn at(&self, x: usize, y: usize, z: usize) -> &(bool, bool, usize) {
|
||||||
|
&self.data[z * self.dim * self.dim + y * self.dim + x]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn at_mut(&mut self, x: usize, y: usize, z: usize) -> &mut (bool, bool, usize) {
|
||||||
|
&mut self.data[z * self.dim * self.dim + y * self.dim + x]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_visited(&mut self, x: usize, y: usize, z: usize) {
|
||||||
|
self.at_mut(x, y, z).1 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_visited(&self, x: usize, y: usize, z: usize) -> bool {
|
||||||
|
self.at(x, y, z).1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper that allows invalid coordinates to simply return false.
|
||||||
|
// This is consistent with the logic of the task.
|
||||||
|
fn is_lava(&self, x: isize, y: isize, z: isize) -> bool {
|
||||||
|
if x < 0
|
||||||
|
|| y < 0
|
||||||
|
|| z < 0
|
||||||
|
|| x >= self.dim as isize
|
||||||
|
|| y >= self.dim as isize
|
||||||
|
|| z >= self.dim as isize
|
||||||
|
{
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
self.at(x as usize, y as usize, z as usize).0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn print(&self) {
|
||||||
|
for z in 0..self.dim {
|
||||||
|
println!("\n\nLayer {}: \n", z);
|
||||||
|
for y in 0..self.dim {
|
||||||
|
if y % 5 == 0 {
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
for x in 0..self.dim {
|
||||||
|
if x % 5 == 0 {
|
||||||
|
print!(" ");
|
||||||
|
}
|
||||||
|
let c = match self.at(x, y, z) {
|
||||||
|
(false, false, _) => '.',
|
||||||
|
(false, true, _) => '+',
|
||||||
|
(true, false, n) => (b'0' + (*n as u8)) as char,
|
||||||
|
(true, true, _) => '-',
|
||||||
|
};
|
||||||
|
if c == '-' {
|
||||||
|
panic!("Oh no.");
|
||||||
|
}
|
||||||
|
print!("{}", c);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Use command line arguments to specify the input filename.
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
if args.len() < 2 {
|
||||||
|
panic!("Usage: ./main <input-file>\nNo input file provided. Exiting.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, read the contents of the input file into a string for easier processing.
|
||||||
|
let input = std::fs::read_to_string(&args[1]).expect("Error opening file");
|
||||||
|
|
||||||
|
// Parse the input into a Vector of 3-tuples.
|
||||||
|
let input = input
|
||||||
|
.lines()
|
||||||
|
.map(|l| {
|
||||||
|
let l = l
|
||||||
|
.split(',')
|
||||||
|
.map(|e| e.parse::<usize>().unwrap())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
[l[0], l[1], l[2]]
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Create the more efficient data structure.
|
||||||
|
let mut v = Volume::new(&input);
|
||||||
|
|
||||||
|
// // PART ONE
|
||||||
|
// // Count the surface-area of each cube.
|
||||||
|
// let mut surface_area = 0;
|
||||||
|
// for [x,y,z] in &input {
|
||||||
|
// // Add 6 to the total for each cube, but ...
|
||||||
|
// surface_area += 6;
|
||||||
|
// // ... remove one for any adjacent cube.
|
||||||
|
// let ix = *x as isize;
|
||||||
|
// let iy = *y as isize;
|
||||||
|
// let iz = *z as isize;
|
||||||
|
// for [dx,dy,dz] in [
|
||||||
|
// [ix+1,iy,iz],
|
||||||
|
// [ix-1,iy,iz],
|
||||||
|
// [ix,iy+1,iz],
|
||||||
|
// [ix,iy-1,iz],
|
||||||
|
// [ix,iy,iz+1],
|
||||||
|
// [ix,iy,iz-1],
|
||||||
|
// ] {
|
||||||
|
// if v.is_lava(dx, dy, dz) {
|
||||||
|
// surface_area -= 1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// println!("Surface area: {}", surface_area);
|
||||||
|
|
||||||
|
// PART TWO
|
||||||
|
// Call recursive evlauator on a point that is guaranteed\
|
||||||
|
// to be air on the outside of the volume.
|
||||||
|
let air_coord = (v.dim - 1) as isize;
|
||||||
|
let ea = exterior_surface_area(&mut v, air_coord, air_coord, air_coord);
|
||||||
|
|
||||||
|
// v.print();
|
||||||
|
|
||||||
|
// println!("Sum: {}", v.data.iter().map(|(_,_,n)| n).sum::<usize>());
|
||||||
|
|
||||||
|
println!("External surface area: {}", ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exterior_surface_area(v: &mut Volume, x: isize, y: isize, z: isize) -> usize {
|
||||||
|
// We assume we're one a valid air-voxel.
|
||||||
|
// Mark this voxel as visited.
|
||||||
|
v.set_visited(x as usize, y as usize, z as usize);
|
||||||
|
// Iterate through all adjacent coordinates.
|
||||||
|
let mut c: usize = 0;
|
||||||
|
for [dx, dy, dz] in [
|
||||||
|
[x + 1, y, z],
|
||||||
|
[x, y + 1, z],
|
||||||
|
[x, y, z + 1],
|
||||||
|
[x - 1, y, z],
|
||||||
|
[x, y - 1, z],
|
||||||
|
[x, y, z - 1],
|
||||||
|
] {
|
||||||
|
// If the coordinates aren't valid, move on to the next voxel.
|
||||||
|
if dx < 0
|
||||||
|
|| dy < 0
|
||||||
|
|| dz < 0
|
||||||
|
|| dx >= v.dim as isize
|
||||||
|
|| dy >= v.dim as isize
|
||||||
|
|| dz >= v.dim as isize
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Is the target lava or air?
|
||||||
|
if v.is_lava(dx, dy, dz) {
|
||||||
|
// Count the surface.
|
||||||
|
c += 1;
|
||||||
|
v.at_mut(dx as usize, dy as usize, dz as usize).2 += 1;
|
||||||
|
} else {
|
||||||
|
// Air? Call recursively if it hasn't already been visited.
|
||||||
|
if !v.is_visited(dx as usize, dy as usize, dz as usize) {
|
||||||
|
c += exterior_surface_area(v, dx, dy, dz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user