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