Day 17 part 2 solution

This commit is contained in:
Bill Thiede 2020-12-18 11:52:12 -08:00
parent 6d4cdcefe0
commit 069788b2ee

View File

@ -140,6 +140,255 @@
//! //!
//! Starting with your given initial configuration, simulate six cycles. How many cubes are left in the active state after the sixth cycle? //! Starting with your given initial configuration, simulate six cycles. How many cubes are left in the active state after the sixth cycle?
//! --- Part Two ---
//! For some reason, your simulated results don't match what the experimental energy source engineers expected. Apparently, the pocket dimension actually has four spatial dimensions, not three.
//!
//! The pocket dimension contains an infinite 4-dimensional grid. At every integer 4-dimensional coordinate (x,y,z,w), there exists a single cube (really, a hypercube) which is still either active or inactive.
//!
//! Each cube only ever considers its neighbors: any of the 80 other cubes where any of their coordinates differ by at most 1. For example, given the cube at x=1,y=2,z=3,w=4, its neighbors include the cube at x=2,y=2,z=3,w=3, the cube at x=0,y=2,z=3,w=4, and so on.
//!
//! The initial state of the pocket dimension still consists of a small flat region of cubes. Furthermore, the same rules for cycle updating still apply: during each cycle, consider the number of active neighbors of each cube.
//!
//! For example, consider the same initial state as in the example above. Even though the pocket dimension is 4-dimensional, this initial state represents a small 2-dimensional slice of it. (In particular, this initial state defines a 3x3x1x1 region of the 4-dimensional space.)
//!
//! Simulating a few cycles from this initial state produces the following configurations, where the result of each cycle is shown layer-by-layer at each given z and w coordinate:
//!
//! Before any cycles:
//!
//! z=0, w=0
//! .#.
//! ..#
//! ###
//!
//!
//! After 1 cycle:
//!
//! z=-1, w=-1
//! #..
//! ..#
//! .#.
//!
//! z=0, w=-1
//! #..
//! ..#
//! .#.
//!
//! z=1, w=-1
//! #..
//! ..#
//! .#.
//!
//! z=-1, w=0
//! #..
//! ..#
//! .#.
//!
//! z=0, w=0
//! #.#
//! .##
//! .#.
//!
//! z=1, w=0
//! #..
//! ..#
//! .#.
//!
//! z=-1, w=1
//! #..
//! ..#
//! .#.
//!
//! z=0, w=1
//! #..
//! ..#
//! .#.
//!
//! z=1, w=1
//! #..
//! ..#
//! .#.
//!
//!
//! After 2 cycles:
//!
//! z=-2, w=-2
//! .....
//! .....
//! ..#..
//! .....
//! .....
//!
//! z=-1, w=-2
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=0, w=-2
//! ###..
//! ##.##
//! #...#
//! .#..#
//! .###.
//!
//! z=1, w=-2
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=2, w=-2
//! .....
//! .....
//! ..#..
//! .....
//! .....
//!
//! z=-2, w=-1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=-1, w=-1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=0, w=-1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=1, w=-1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=2, w=-1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=-2, w=0
//! ###..
//! ##.##
//! #...#
//! .#..#
//! .###.
//!
//! z=-1, w=0
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=0, w=0
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=1, w=0
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=2, w=0
//! ###..
//! ##.##
//! #...#
//! .#..#
//! .###.
//!
//! z=-2, w=1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=-1, w=1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=0, w=1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=1, w=1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=2, w=1
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=-2, w=2
//! .....
//! .....
//! ..#..
//! .....
//! .....
//!
//! z=-1, w=2
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=0, w=2
//! ###..
//! ##.##
//! #...#
//! .#..#
//! .###.
//!
//! z=1, w=2
//! .....
//! .....
//! .....
//! .....
//! .....
//!
//! z=2, w=2
//! .....
//! .....
//! ..#..
//! .....
//! .....
//! After the full six-cycle boot process completes, 848 cubes are left in the active state.
//!
//! Starting with your given initial configuration, simulate six cycles in a 4-dimensional space. How many cubes are left in the active state after the sixth cycle?
use std::fmt; use std::fmt;
use aoc_runner_derive::{aoc, aoc_generator}; use aoc_runner_derive::{aoc, aoc_generator};
@ -164,86 +413,112 @@ impl fmt::Debug for Cube {
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
struct ThreeSpace<T> { struct Universe<T> {
cells: Vec<T>, cells: Vec<T>,
x_len: usize, x_len: usize,
y_len: usize, y_len: usize,
z_len: usize, z_len: usize,
w_len: usize,
default: T, default: T,
} }
impl<T> ThreeSpace<T> { impl<T> Universe<T> {
fn dimensions(&self) -> String { fn dimensions(&self) -> String {
let u = &self; let u = &self;
format!("{}x{}x{}", u.x_len, u.y_len, u.z_len) format!("{}x{}x{}x{}", u.x_len, u.y_len, u.z_len, u.w_len)
} }
} }
impl<T> fmt::Debug for ThreeSpace<T> impl<T> fmt::Debug for Universe<T>
where where
T: fmt::Debug, T: fmt::Debug,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}\n", self.dimensions())?; write!(f, "{}\n", self.dimensions())?;
let u = &self; let u = &self;
for z in 0..u.z_len { for w in 0..u.w_len {
for z in 0..u.z_len {
let hdr = format!(
"z={}, w={}",
z as isize - u.z_len as isize / 2,
w as isize - u.w_len as isize / 2
);
write!(f, "{:width$} | ", hdr, width = u.x_len)?;
}
write!(f, "\n")?;
for y in 0..u.y_len { for y in 0..u.y_len {
for x in 0..u.x_len { for z in 0..u.z_len {
write!(f, "{:?}", u[(x, y, z)])?; for x in 0..u.x_len {
write!(f, "{:?}", u[(x, y, z, w)])?;
}
write!(f, " | ")?;
} }
write!(f, "\n")?; write!(f, "\n")?;
} }
write!(f, "\n")?; write!(f, "\n")?;
} }
write!(f, "\n")?;
Ok(()) Ok(())
} }
} }
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
impl<T> IndexMut<(usize, usize, usize)> for ThreeSpace<T> {
fn index_mut(&mut self, (x, y, z): (usize, usize, usize)) -> &mut Self::Output { impl<T> IndexMut<(usize, usize, usize, usize)> for Universe<T> {
if x >= self.x_len || y >= self.y_len || z > self.z_len { fn index_mut(&mut self, (x, y, z, w): (usize, usize, usize, usize)) -> &mut Self::Output {
panic!(format!("index_mut outside of bounds ({},{},{})", x, y, z)); if x >= self.x_len || y >= self.y_len || z > self.z_len || w > self.w_len {
panic!(format!(
"index_mut outside of bounds ({},{},{},{})",
x, y, z, w
));
} }
&mut self.cells[x + y * self.y_len + z * self.x_len * self.y_len] &mut self.cells[x
+ y * self.y_len
+ z * self.x_len * self.y_len
+ w * self.x_len * self.y_len * self.z_len]
} }
} }
impl<T> Index<(usize, usize, usize)> for ThreeSpace<T> { impl<T> Index<(usize, usize, usize, usize)> for Universe<T> {
type Output = T; type Output = T;
/// Returns the value in 3-space given by x,y,z. Values outside the active space this ThreeSpace covers will return the default for T; /// Returns the value in 4-space given by x,y,z,w. Values outside the active space this Universe covers will return the default for T;
fn index(&self, (x, y, z): (usize, usize, usize)) -> &Self::Output { fn index(&self, (x, y, z, w): (usize, usize, usize, usize)) -> &Self::Output {
if x >= self.x_len || y >= self.y_len || z > self.z_len { if x >= self.x_len || y >= self.y_len || z > self.z_len || w > self.w_len {
return &self.default; return &self.default;
} }
&self.cells[x + y * self.y_len + z * self.x_len * self.y_len] &self.cells[x
+ y * self.y_len
+ z * self.x_len * self.y_len
+ w * self.x_len * self.y_len * self.z_len]
} }
} }
impl<T> Index<(isize, isize, isize)> for ThreeSpace<T> { impl<T> Index<(isize, isize, isize, isize)> for Universe<T> {
type Output = T; type Output = T;
/// Returns the value in 3-space given by x,y,z. Values outside the active space this ThreeSpace covers will return self.default; /// Returns the value in 4-space given by x,y,z,w. Values outside the active space this Universe covers will return self.default;
fn index(&self, (x, y, z): (isize, isize, isize)) -> &Self::Output { fn index(&self, (x, y, z, w): (isize, isize, isize, isize)) -> &Self::Output {
if x < 0 || y < 0 || z < 0 { if x < 0 || y < 0 || z < 0 || w < 0 {
return &self.default; return &self.default;
} }
let x_len = self.x_len as isize; let x_len = self.x_len as isize;
let y_len = self.y_len as isize; let y_len = self.y_len as isize;
let z_len = self.z_len as isize; let z_len = self.z_len as isize;
let w_len = self.w_len as isize;
if x >= x_len || y >= y_len || z >= z_len { if x >= x_len || y >= y_len || z >= z_len || w >= w_len {
return &self.default; return &self.default;
} }
&self.cells[(x + y * y_len + z * x_len * y_len) as usize] &self.cells[(x + y * y_len + z * x_len * y_len + w * x_len * y_len * z_len) as usize]
} }
} }
#[derive(Clone)] #[derive(Clone)]
struct PocketDimension { struct PocketDimension {
universe: ThreeSpace<Cube>, universe: Universe<Cube>,
} }
impl std::str::FromStr for PocketDimension { impl std::str::FromStr for PocketDimension {
@ -268,11 +543,12 @@ impl std::str::FromStr for PocketDimension {
})); }));
}); });
}); });
let universe = ThreeSpace { let universe = Universe {
cells, cells,
x_len, x_len,
y_len, y_len,
z_len, z_len,
w_len: 1,
default: Cube::Inactive, default: Cube::Inactive,
}; };
@ -289,50 +565,69 @@ impl fmt::Debug for PocketDimension {
impl PocketDimension { impl PocketDimension {
/// Applies the rules of the puzzle one iteration and returns a new PocketDimension /// Applies the rules of the puzzle one iteration and returns a new PocketDimension
/// representing the new state. /// representing the new state.
fn step(&self) -> PocketDimension { fn step(&self, expand_w: bool) -> PocketDimension {
let u = &self.universe; let u = &self.universe;
let x_len = u.x_len as isize; let x_len = u.x_len as isize;
let y_len = u.y_len as isize; let y_len = u.y_len as isize;
let z_len = u.z_len as isize; let z_len = u.z_len as isize;
let mut counts = ThreeSpace::<usize> { let w_len = u.w_len as isize;
let (new_w_len, w_range, w_off) = if expand_w {
(u.w_len + 2, -1..w_len + 1, 1)
} else {
(u.w_len, 0..w_len, 0)
};
let mut counts = Universe::<usize> {
x_len: u.x_len + 2, x_len: u.x_len + 2,
y_len: u.y_len + 2, y_len: u.y_len + 2,
z_len: u.z_len + 2, z_len: u.z_len + 2,
cells: vec![0; (u.x_len + 2) * (u.y_len + 2) * (u.z_len + 2)], w_len: new_w_len,
cells: vec![0; (u.x_len + 2) * (u.y_len + 2) * (u.z_len + 2) * (new_w_len)],
default: 0, default: 0,
}; };
let mut universe = ThreeSpace::<Cube> { let mut universe = Universe::<Cube> {
x_len: u.x_len + 2, x_len: u.x_len + 2,
y_len: u.y_len + 2, y_len: u.y_len + 2,
z_len: u.z_len + 2, z_len: u.z_len + 2,
cells: vec![Cube::Inactive; (u.x_len + 2) * (u.y_len + 2) * (u.z_len + 2)], w_len: new_w_len,
cells: vec![
Cube::Inactive;
(u.x_len + 2) * (u.y_len + 2) * (u.z_len + 2) * (new_w_len)
],
default: Cube::Inactive, default: Cube::Inactive,
}; };
for z in -1..z_len + 1 { for w in w_range {
for y in -1..y_len + 1 { for z in -1..z_len + 1 {
for x in -1..x_len + 1 { for y in -1..y_len + 1 {
let adj = self.adjacency((x, y, z)); for x in -1..x_len + 1 {
counts[((x + 1) as usize, (y + 1) as usize, (z + 1) as usize)] = adj; let adj = self.adjacency((x, y, z, w));
match self.universe[(x, y, z)] { let dst = (
Cube::Active => { (x + 1) as usize,
if adj == 2 || adj == 3 { (y + 1) as usize,
universe[((x + 1) as usize, (y + 1) as usize, (z + 1) as usize)] = (z + 1) as usize,
Cube::Active; (w + w_off) as usize,
} else { );
universe[((x + 1) as usize, (y + 1) as usize, (z + 1) as usize)] = counts[dst] = adj;
Cube::Inactive; match self.universe[(x, y, z, w)] {
Cube::Active => {
if adj == 2 || adj == 3 {
universe[dst] = Cube::Active;
} else {
universe[dst] = Cube::Inactive;
}
} }
} Cube::Inactive => {
Cube::Inactive => { if adj == 3 {
if adj == 3 { universe[dst] = Cube::Active;
universe[((x + 1) as usize, (y + 1) as usize, (z + 1) as usize)] = }
Cube::Active;
} }
} };
}; }
} }
} }
} }
//dbg!(&counts, &universe);
PocketDimension { universe } PocketDimension { universe }
} }
fn active(&self) -> usize { fn active(&self) -> usize {
@ -343,17 +638,21 @@ impl PocketDimension {
.count() .count()
} }
/// Counts active neighbors. /// Counts active neighbors.
fn adjacency(&self, (x, y, z): (isize, isize, isize)) -> usize { fn adjacency(&self, (x, y, z, w): (isize, isize, isize, isize)) -> usize {
let mut sum = 0; let mut sum = 0;
for z_off in -1..=1 { for w_off in -1..=1 {
for y_off in -1..=1 { for z_off in -1..=1 {
for x_off in -1..=1 { for y_off in -1..=1 {
if x_off == 0 && y_off == 0 && z_off == 0 { for x_off in -1..=1 {
// Skip the requested cell if x_off == 0 && y_off == 0 && z_off == 0 && w_off == 0 {
continue; // Skip the requested cell
} continue;
if self.universe[(x + x_off, y + y_off, z + z_off)] == Cube::Active { }
sum += 1; if self.universe[(x + x_off, y + y_off, z + z_off, w + w_off)]
== Cube::Active
{
sum += 1;
}
} }
} }
} }
@ -369,7 +668,12 @@ fn generator(input: &str) -> PocketDimension {
#[aoc(day17, part1)] #[aoc(day17, part1)]
fn solution1(pd: &PocketDimension) -> usize { fn solution1(pd: &PocketDimension) -> usize {
(0..6).fold(pd.clone(), |acc, _| acc.step()).active() (0..6).fold(pd.clone(), |acc, _| acc.step(false)).active()
}
#[aoc(day17, part2)]
fn solution2(pd: &PocketDimension) -> usize {
(0..6).fold(pd.clone(), |acc, _| acc.step(true)).active()
} }
#[cfg(test)] #[cfg(test)]
@ -473,7 +777,7 @@ mod tests {
for (idx, ((input, active), dimensions)) in STEPS1 for (idx, ((input, active), dimensions)) in STEPS1
.split("\n\n\n") .split("\n\n\n")
.zip(vec![5, 11, 21, 38]) .zip(vec![5, 11, 21, 38])
.zip(vec!["3x3x1", "3x3x3", "5x5x5", "7x7x5"]) .zip(vec!["3x3x1x1", "3x3x3x1", "5x5x5x1", "7x7x5x1"])
.enumerate() .enumerate()
{ {
let pd = generator(input); let pd = generator(input);
@ -492,4 +796,18 @@ mod tests {
fn part1() { fn part1() {
assert_eq!(solution1(&generator(INPUT1)), 112); assert_eq!(solution1(&generator(INPUT1)), 112);
} }
#[test]
fn step_exand_w() {
let pd = generator(INPUT1);
assert_eq!(pd.active(), 5);
let pd = pd.step(true);
assert_eq!(pd.active(), 29);
let pd = pd.step(true);
assert_eq!(pd.active(), 60);
}
#[test]
fn part2() {
assert_eq!(solution2(&generator(INPUT1)), 848);
}
} }