Advent of Code: Day #17 - Conway Cubes

17th of December 2020 |

5 - 6 minutes

Introduction

One thing I love about advent of code is how you improve as a programmer and problem solver over the month. Todays challenge was taking day 11 into three and four dimensions, and yet my code today runs faster in 3/4D then my day 11 code did in 2D!

Task One

Given an initial state of active and inactive cubes perform six iterations and return the amount of active cubes. The rules are as follows:

Reading in the data

The data was read into an array of arrays. The data has a '#' if a cube is active and '.' if it is not, I changed this to a 1 for active and 0 for inactive in my code.

Python Copy
def read_data(path):
dataset = []
with open(path) as f:
for line in f.readlines():
tmp = [1 if i == "#" else 0 for i in list(line.strip())]
dataset.append(tmp)

return dataset

The Solution

To solve this I decided to loop over all of the cubes and then all of their neighbours and increment a dictionary value every time they were seen, regardless if they were active or not. Then I loop through this list of neighbours and counts and add a cube to the active list if it satisfies the rules laid out earlier.

I used the product function from itertools to generate all of the possible offsets for the neighbours, originally I did not have a check for if the offset was 0, 0, 0 (The current cube) this drove me crazy for a while as I couldn't understand why my program was giving the incorrect answer for the example input.

Python Copy
def task_one(initial_state):
activated_cubes = [(i, x, 0) for i, c in enumerate(initial_state) for x in range(len(c)) if c[x] == 1]

for _ in range(6):
neighbors = defaultdict(int)

for x, y, z in activated_cubes:
for dx, dy, dz in product(range(-1, 2), repeat=3):
if (dx, dy, dz) != (0, 0, 0):
neighbors[(x + dx, y + dy, z + dz)] += 1

# Add cubes that are currently active if they have 2 or 3 active neighbours
new_cubes = [cube for cube, count in neighbors.items() if cube in activated_cubes and count in [2, 3]]
# Add previously inactive cubes if they have 3 active neighbours
new_cubes += [cube for cube, count in neighbors.items() if cube not in activated_cubes and count == 3]

activated_cubes = new_cubes
return len(activated_cubes)

Task Two

The Solution

For Task Two we must do the same as task one but in four dimensions, this required only a few small tweaks to the code.

Python Copy
def task_two(initial_state):
activated_cubes = [(i, x, 0, 0) for i, c in enumerate(initial_state) for x in range(len(c)) if c[x] == 1]

for _ in range(6):
neighbors = defaultdict(int)

for x, y, z, t in activated_cubes:
for dx, dy, dz, dt in product(range(-1, 2), repeat=4):
if (dx, dy, dz, dt) != (0, 0, 0, 0):
neighbors[(x + dx, y + dy, z + dz, t + dt)] += 1

# Add cubes that are currently active if they have 2 or 3 active neighbours
new_cubes = [cube for cube, count in neighbors.items() if cube in activated_cubes and count in [2, 3]]
# Add previously inactive cubes if they have 3 active neighbours
new_cubes += [cube for cube, count in neighbors.items() if cube not in activated_cubes and count == 3]

activated_cubes = new_cubes
return len(activated_cubes)

Leaderboard Code: 353270-1fc6ef28

Github Repository A link to the github repo containing all the days.

main.py A direct download of the main.py script

2.2Kb