-
Notifications
You must be signed in to change notification settings - Fork 1
/
day17_cubes.py
171 lines (134 loc) · 4.82 KB
/
day17_cubes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
from collections import defaultdict
from itertools import permutations
import pytest
@pytest.fixture
def test_input():
return """.#.
..#
###"""
def generate_initial_state_3d(config: str):
state = {}
for x_idx, row in enumerate(config.split("\n")):
for y_idx, value in enumerate(row.strip()):
state[(x_idx, y_idx, 0)] = True if value == "#" else False
return state
def step_3d(state):
# get all coordinates for next step_3d
all_coordinates = set()
for current_coordinate in state.keys():
for adj_coordinate in adjacent_coordinates_3d(current_coordinate):
all_coordinates.add(adj_coordinate)
# count up all adjacent
adj_active = defaultdict(int)
for coord in all_coordinates:
for neighbour in adjacent_coordinates_3d(coord):
if state.get(neighbour, False):
adj_active[coord] += 1
new_state = {}
for coord in all_coordinates:
num_active = adj_active[coord]
prev_state_is_active = state.get(coord, False)
if prev_state_is_active:
if 2 <= num_active <= 3:
new_state[coord] = True
else:
new_state[coord] = False
else:
if num_active == 3:
new_state[coord] = True
else:
new_state[coord] = False
return new_state
def count_active(state):
count = 0
for k, v in state.items():
if v:
count += 1
return count
def adjacent_coordinates_3d(coordinate):
x, y, z = coordinate
for x_diff in [-1, 0, 1]:
for y_diff in [-1, 0, 1]:
for z_diff in [-1, 0, 1]:
# skip current coordinate
if x_diff == y_diff == z_diff == 0:
continue
yield (x + x_diff, y + y_diff, z + z_diff)
def test_generate_initial_state_3d(test_input):
state = generate_initial_state_3d(test_input)
assert len(state) == 9
def test_part1(test_input):
state = generate_initial_state_3d(test_input)
for _ in range(6):
state = step_3d(state)
assert count_active(state) == 112
def print_state_3d(state):
z_min = min(z for x, y, z in state.keys())
z_max = max(z for x, y, z in state.keys())
x_min = min(x for x, y, z in state.keys())
x_max = max(x for x, y, z in state.keys())
for curr_z in range(z_min, z_max + 1):
coordinates = {
(x, y, z): value for (x, y, z), value in state.items() if z == curr_z
}
print(f"z={curr_z}")
output = ""
for curr_x in range(x_min, x_max + 1):
for curr_y in range(x_min, x_max + 1):
output += "#" if state[(curr_x, curr_y, curr_z)] else "."
output += "\n"
print(output)
def generate_initial_state_4d(config: str):
state = {}
for x_idx, row in enumerate(config.split("\n")):
for y_idx, value in enumerate(row.strip()):
state[(x_idx, y_idx, 0, 0)] = True if value == "#" else False
return state
def step_4d(state):
# get all coordinates for next step_4d
all_coordinates = set()
for current_coordinate in state.keys():
for adj_coordinate in adjacent_coordinates_4d(current_coordinate):
all_coordinates.add(adj_coordinate)
# count up all adjacent
adj_active = defaultdict(int)
for coord in all_coordinates:
for neighbour in adjacent_coordinates_4d(coord):
if state.get(neighbour, False):
adj_active[coord] += 1
new_state = {}
for coord in all_coordinates:
num_active = adj_active[coord]
prev_state_is_active = state.get(coord, False)
if prev_state_is_active:
if 2 <= num_active <= 3:
new_state[coord] = True
else:
new_state[coord] = False
else:
if num_active == 3:
new_state[coord] = True
else:
new_state[coord] = False
return new_state
def adjacent_coordinates_4d(coordinate):
x, y, z, w = coordinate
for x_diff in [-1, 0, 1]:
for y_diff in [-1, 0, 1]:
for z_diff in [-1, 0, 1]:
for w_diff in [-1, 0, 1]:
# skip current coordinate
if x_diff == y_diff == z_diff == w_diff == 0:
continue
yield (x + x_diff, y + y_diff, z + z_diff, w + w_diff)
if __name__ == "__main__":
with open('2020/data/day17_input.txt', 'r') as f:
initial_state = f.read()
state = generate_initial_state_3d(initial_state)
for _ in range(6):
state = step_3d(state)
print(f"Result for part 1 is {count_active(state)}")
state = generate_initial_state_4d(initial_state)
for _ in range(6):
state = step_4d(state)
print(f"Result for part 2 is {count_active(state)}")