# --- Day 17: Conway Cubes --- # As your flight slowly drifts through the sky, the Elves at the Mythical # Information Bureau at the North Pole contact you. They'd like some help # debugging a malfunctioning experimental energy source aboard one of their # super-secret imaging satellites. # The experimental energy source is based on cutting-edge technology: a set of # Conway Cubes contained in a pocket dimension! When you hear it's having # problems, you can't help but agree to take a look. # The pocket dimension contains an infinite 3-dimensional grid. At every # integer 3-dimensional coordinate (x,y,z), there exists a single cube which is # either active or inactive. # In the initial state of the pocket dimension, almost all cubes start # inactive. The only exception to this is a small flat region of cubes (your # puzzle input); the cubes in this region start in the specified active (#) or # inactive (.) state. # The energy source then proceeds to boot up by executing six cycles. # Each cube only ever considers its neighbors: any of the 26 other cubes where # any of their coordinates differ by at most 1. For example, given the cube at # x=1,y=2,z=3, its neighbors include the cube at x=2,y=2,z=2, the cube at # x=0,y=2,z=3, and so on. # During a cycle, all cubes simultaneously change their state according to the # following rules: # If a cube is active and exactly 2 or 3 of its neighbors are also active, # the cube remains active. Otherwise, the cube becomes inactive. # If a cube is inactive but exactly 3 of its neighbors are active, the cube # becomes active. Otherwise, the cube remains inactive. # The engineers responsible for this experimental energy source would like you # to simulate the pocket dimension and determine what the configuration of # cubes should be at the end of the six-cycle boot process. # For example, consider the following initial state: # .#. # ..# # ### # Even though the pocket dimension is 3-dimensional, this initial state # represents a small 2-dimensional slice of it. (In particular, this initial # state defines a 3x3x1 region of the 3-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 coordinate (and the frame of view follows the active cells in # each cycle): # Before any cycles: # z=0 # .#. # ..# # ### # After 1 cycle: # z=-1 # #.. # ..# # .#. # z=0 # #.# # .## # .#. # z=1 # #.. # ..# # .#. # After 2 cycles: # z=-2 # ..... # ..... # ..#.. # ..... # ..... # z=-1 # ..#.. # .#..# # ....# # .#... # ..... # z=0 # ##... # ##... # #.... # ....# # .###. # z=1 # ..#.. # .#..# # ....# # .#... # ..... # z=2 # ..... # ..... # ..#.. # ..... # ..... # After 3 cycles: # z=-2 # ....... # ....... # ..##... # ..###.. # ....... # ....... # ....... # z=-1 # ..#.... # ...#... # #...... # .....## # .#...#. # ..#.#.. # ...#... # z=0 # ...#... # ....... # #...... # ....... # .....## # .##.#.. # ...#... # z=1 # ..#.... # ...#... # #...... # .....## # .#...#. # ..#.#.. # ...#... # z=2 # ....... # ....... # ..##... # ..###.. # ....... # ....... # ....... # After the full six-cycle boot process completes, 112 cubes are left in the # active state. # Starting with your given initial configuration, simulate six cycles. How many # cubes are left in the active state after the sixth cycle? import itertools as iter with open("files/P17.txt", "r") as f: init_state = [line for line in f.read().strip().split("\n")] def get_active_points(dim: int): ap = set() for y, z in enumerate(init_state): for x, c in enumerate(z): if c == "#": ap.add(tuple([x, y] + [0] * (dim - 2))) return ap def part_1() -> None: active_points = get_active_points(dim=3) for it in range(6): new_active_points = set() # check x,y,z point for x in range(-10 - it, it + 10): for y in range(-10 - it, it + 10): for z in range(-2 - it, it + 2): point = (x, y, z) # for the current point, check the neighbors num_actives = 0 for delta in iter.product(range(-1, 2), repeat=3): if delta != (0,) * 3: if ( tuple([a + b for a, b in zip(point, delta)]) in active_points ): num_actives += 1 # apply rules if point in active_points and ( num_actives == 2 or num_actives == 3 ): new_active_points.add(point) if point not in active_points and num_actives == 3: new_active_points.add(point) # next iteration active_points = new_active_points print(f"After 6 cycles in 3D, we have {len(active_points)} active cubes") # --- 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? def part_2() -> None: active_points = get_active_points(dim=4) for it in range(6): new_active_points = set() # check x,y,z point for x in range(-10 - it, it + 10): for y in range(-10 - it, it + 10): for z in range(-2 - it, it + 2): for w in range(-2 - it, it + 2): point = (x, y, z, w) # for the current point, check the neighbors num_actives = 0 for delta in iter.product(range(-1, 2), repeat=4): if delta != (0,) * 4: if ( tuple( [a + b for a, b in zip(point, delta)] ) in active_points ): num_actives += 1 # apply rules if point in active_points and ( num_actives == 2 or num_actives == 3 ): new_active_points.add(point) if point not in active_points and num_actives == 3: new_active_points.add(point) # next iteration active_points = new_active_points print(f"After 6 cycles in 4D, we have {len(active_points)} active cubes") if __name__ == "__main__": part_1() part_2()