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?
//! --- 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 aoc_runner_derive::{aoc, aoc_generator};
@ -164,86 +413,112 @@ impl fmt::Debug for Cube {
}
#[derive(Default, Clone)]
struct ThreeSpace<T> {
struct Universe<T> {
cells: Vec<T>,
x_len: usize,
y_len: usize,
z_len: usize,
w_len: usize,
default: T,
}
impl<T> ThreeSpace<T> {
impl<T> Universe<T> {
fn dimensions(&self) -> String {
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
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}\n", self.dimensions())?;
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 x in 0..u.x_len {
write!(f, "{:?}", u[(x, y, z)])?;
for z in 0..u.z_len {
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")?;
Ok(())
}
}
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 {
if x >= self.x_len || y >= self.y_len || z > self.z_len {
panic!(format!("index_mut outside of bounds ({},{},{})", x, y, z));
impl<T> IndexMut<(usize, usize, usize, usize)> for Universe<T> {
fn index_mut(&mut self, (x, y, z, w): (usize, usize, usize, usize)) -> &mut Self::Output {
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;
/// 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;
fn index(&self, (x, y, z): (usize, usize, usize)) -> &Self::Output {
if x >= self.x_len || y >= self.y_len || z > self.z_len {
/// 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, w): (usize, usize, usize, usize)) -> &Self::Output {
if x >= self.x_len || y >= self.y_len || z > self.z_len || w > self.w_len {
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;
/// Returns the value in 3-space given by x,y,z. Values outside the active space this ThreeSpace covers will return self.default;
fn index(&self, (x, y, z): (isize, isize, isize)) -> &Self::Output {
if x < 0 || y < 0 || z < 0 {
/// 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, w): (isize, isize, isize, isize)) -> &Self::Output {
if x < 0 || y < 0 || z < 0 || w < 0 {
return &self.default;
}
let x_len = self.x_len as isize;
let y_len = self.y_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;
}
&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)]
struct PocketDimension {
universe: ThreeSpace<Cube>,
universe: Universe<Cube>,
}
impl std::str::FromStr for PocketDimension {
@ -268,11 +543,12 @@ impl std::str::FromStr for PocketDimension {
}));
});
});
let universe = ThreeSpace {
let universe = Universe {
cells,
x_len,
y_len,
z_len,
w_len: 1,
default: Cube::Inactive,
};
@ -289,50 +565,69 @@ impl fmt::Debug for PocketDimension {
impl PocketDimension {
/// Applies the rules of the puzzle one iteration and returns a new PocketDimension
/// representing the new state.
fn step(&self) -> PocketDimension {
fn step(&self, expand_w: bool) -> PocketDimension {
let u = &self.universe;
let x_len = u.x_len as isize;
let y_len = u.y_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,
y_len: u.y_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,
};
let mut universe = ThreeSpace::<Cube> {
let mut universe = Universe::<Cube> {
x_len: u.x_len + 2,
y_len: u.y_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,
};
for z in -1..z_len + 1 {
for y in -1..y_len + 1 {
for x in -1..x_len + 1 {
let adj = self.adjacency((x, y, z));
counts[((x + 1) as usize, (y + 1) as usize, (z + 1) as usize)] = adj;
match self.universe[(x, y, z)] {
Cube::Active => {
if adj == 2 || adj == 3 {
universe[((x + 1) as usize, (y + 1) as usize, (z + 1) as usize)] =
Cube::Active;
} else {
universe[((x + 1) as usize, (y + 1) as usize, (z + 1) as usize)] =
Cube::Inactive;
for w in w_range {
for z in -1..z_len + 1 {
for y in -1..y_len + 1 {
for x in -1..x_len + 1 {
let adj = self.adjacency((x, y, z, w));
let dst = (
(x + 1) as usize,
(y + 1) as usize,
(z + 1) as usize,
(w + w_off) as usize,
);
counts[dst] = adj;
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 => {
if adj == 3 {
universe[((x + 1) as usize, (y + 1) as usize, (z + 1) as usize)] =
Cube::Active;
Cube::Inactive => {
if adj == 3 {
universe[dst] = Cube::Active;
}
}
}
};
};
}
}
}
}
//dbg!(&counts, &universe);
PocketDimension { universe }
}
fn active(&self) -> usize {
@ -343,17 +638,21 @@ impl PocketDimension {
.count()
}
/// 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;
for z_off in -1..=1 {
for y_off in -1..=1 {
for x_off in -1..=1 {
if x_off == 0 && y_off == 0 && z_off == 0 {
// Skip the requested cell
continue;
}
if self.universe[(x + x_off, y + y_off, z + z_off)] == Cube::Active {
sum += 1;
for w_off in -1..=1 {
for z_off in -1..=1 {
for y_off in -1..=1 {
for x_off in -1..=1 {
if x_off == 0 && y_off == 0 && z_off == 0 && w_off == 0 {
// Skip the requested cell
continue;
}
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)]
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)]
@ -473,7 +777,7 @@ mod tests {
for (idx, ((input, active), dimensions)) in STEPS1
.split("\n\n\n")
.zip(vec![5, 11, 21, 38])
.zip(vec!["3x3x1", "3x3x3", "5x5x5", "7x7x5"])
.zip(vec!["3x3x1x1", "3x3x3x1", "5x5x5x1", "7x7x5x1"])
.enumerate()
{
let pd = generator(input);
@ -492,4 +796,18 @@ mod tests {
fn part1() {
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);
}
}