-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathday24.py
executable file
·147 lines (108 loc) · 3.44 KB
/
day24.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
#!/usr/bin/env python3
import sys
from copy import deepcopy
from itertools import product
def neighbors4(grid, r, c):
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = r + dr, c + dc
if 0 <= nr < ROWS and 0 <= nc < COLS:
yield nr, nc
def neighbors4_alive(grid, r, c):
return sum(grid[nr][nc] for nr, nc in neighbors4(grid, r, c))
def evolve(grid, r, c):
alive = neighbors4_alive(grid, r, c)
if grid[r][c] == BUG:
if alive == 1:
return BUG
return EMPTY
if alive == 1 or alive == 2:
return BUG
return EMPTY
def nextgen(grid):
new_grid = [[EMPTY] * COLS for _ in range(ROWS)]
for r, c in product(range(ROWS), range(COLS)):
new_grid[r][c] = evolve(grid, r, c)
return new_grid
def recursive_evolve(grid, grid_outer, grid_inner, r, c):
alive = 0
if grid_outer is not None:
if c == 0 and grid_outer[CENTER_ROW][CENTER_COL - 1]: # left
alive += 1
if r == 0 and grid_outer[CENTER_ROW - 1][CENTER_COL]: # up
alive += 1
if c == MAXCOL and grid_outer[CENTER_ROW][CENTER_COL + 1]: # right
alive += 1
if r == MAXROW and grid_outer[CENTER_ROW + 1][CENTER_COL]: # down
alive += 1
if grid_inner is not None:
if (r, c) == (CENTER_ROW, CENTER_COL - 1): # left
alive += sum(grid_inner[i][0] for i in range(ROWS))
elif (r, c) == (CENTER_ROW - 1, CENTER_COL): # up
alive += sum(grid_inner[0][i] for i in range(COLS))
elif (r, c) == (CENTER_ROW, CENTER_COL + 1): # right
alive += sum(grid_inner[i][MAXCOL] for i in range(ROWS))
elif (r, c) == (CENTER_ROW + 1, CENTER_COL): # down
alive += sum(grid_inner[MAXROW][i] for i in range(COLS))
alive += neighbors4_alive(grid, r, c)
if grid[r][c] == BUG:
if alive == 1:
return BUG
return EMPTY
if alive == 1 or alive == 2:
return BUG
return EMPTY
def recursive_nextgen(grids, depth):
if depth in grids:
grid = grids[depth]
else:
grid = [[EMPTY] * COLS for _ in range(ROWS)]
new_grid = [[EMPTY] * COLS for _ in range(ROWS)]
grid_outer = grids.get(depth + 1)
grid_inner = grids.get(depth - 1)
for r, c in product(range(ROWS), range(COLS)):
if (r, c) == (CENTER_ROW, CENTER_COL):
continue
new_grid[r][c] = recursive_evolve(grid, grid_outer, grid_inner, r, c)
return new_grid
# Open the first argument as input or use stdin if no arguments were given
fin = open(sys.argv[1]) if len(sys.argv) > 1 else sys.stdin
orig_grid = list(list(l.strip()) for l in fin)
ROWS = len(orig_grid)
COLS = len(orig_grid[0])
MAXROW = ROWS - 1
MAXCOL = COLS - 1
EMPTY, BUG = 0, 1
for r, c in product(range(ROWS), range(COLS)):
orig_grid[r][c] = BUG if orig_grid[r][c] == '#' else EMPTY
grid = deepcopy(orig_grid)
seen = set()
while True:
state = tuple(map(tuple, grid))
if state in seen:
break
seen.add(state)
grid = nextgen(grid)
total, biodiv = 0, 1
for r, c in product(range(ROWS), range(COLS)):
if grid[r][c] == BUG:
total += biodiv
biodiv <<= 1
print('Part 1:', total)
assert ROWS % 2 == 1 and COLS % 2 == 1
CENTER_ROW, CENTER_COL = ROWS // 2 + 1, COLS // 2 + 1
grid = deepcopy(orig_grid)
grid[CENTER_ROW][CENTER_COL] = EMPTY
grids = {0: grid}
min_depth = 0
max_depth = 0
for _ in range(200):
new_grids = {}
for depth in grids:
new_grids[depth] = recursive_nextgen(grids, depth)
min_depth -= 1
new_grids[min_depth] = recursive_nextgen(grids, min_depth)
max_depth += 1
new_grids[max_depth] = recursive_nextgen(grids, max_depth)
grids = new_grids
bugs = sum(sum(sum(c == BUG for c in row) for row in grid) for grid in grids.values())
print('Part 2:', bugs)