Advent of Code: Day #17 - Conway Cubes
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:
- 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.
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.
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.
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 neighboursnew_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 neighboursnew_cubes += [cube for cube, count in neighbors.items() if cube not in activated_cubes and count == 3]activated_cubes = new_cubesreturn 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.
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 neighboursnew_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 neighboursnew_cubes += [cube for cube, count in neighbors.items() if cube not in activated_cubes and count == 3]activated_cubes = new_cubesreturn len(activated_cubes)
Links
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