-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday17b.py
147 lines (121 loc) · 3.07 KB
/
day17b.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
from bisect import *
from collections import *
from functools import *
from heapq import *
import itertools
from math import gcd, lcm
from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable
"""
echo "=== sample ===" ; pypy3 day17.py < sample.in
echo "=== real ===" ; pypy3 day17.py < day17.in
echo "=== sample ===" ; pypy3 day17.py < sample.in ; echo "=== real ===" ; pypy3 day17.py < day17.in
"""
A = open('input_day17.txt', 'r')
A = A.read().strip()
N = len(A)
BLOCKS ='''
####
.#.
###
.#.
..#
..#
###
#
#
#
#
##
##'''.strip().split('\n\n')
WIDTH = 7
# alternate push, fall
grid = set()
# (x, y), y increases with height
ITER = 1000000000000
time = 0
next_block = 0
def parse_block(b):
b = b.splitlines()
height = len(b)
width = len(b[0])
points = []
for i in range(height):
for j in range(width):
if b[i][j] == '#':
points.append((j, height - i - 1))
return points
BP = [parse_block(b) for b in BLOCKS]
top_y = -1
def add(block, dx, dy):
return [(dx + x, dy + y) for (x, y) in block]
def collide(b):
for x, y in b:
if (x, y) in grid:
return True
if x < 0 or x >= WIDTH:
return True
if y < 0:
return True
return False
def keep_top(n):
to_yeet = []
for (x, y) in grid:
if y < top_y - n:
to_yeet.append((x, y))
for (x, y) in to_yeet:
grid.remove((x, y))
def serialize_state():
return tuple(sorted([(x, y - top_y) for (x, y) in grid]))
oct = 0
dp = {}
offset = 0
while True:
keep_top(50)
if next_block == 0:
state = serialize_state()
key = (time, state)
if key in dp:
# print('got repeated state:')
# print('time:', time)
# print('oct:', oct)
(prev_oct, prev_top_y) = dp[key]
# print('prev_oct:', prev_oct)
# print('prev_top_y:', prev_top_y)
# print('top_y:', top_y)
# thx copilot
period = oct - prev_oct
y_diff = top_y - prev_top_y
offset += y_diff * ((ITER - oct) // period)
oct += (ITER - oct) // period * period
else:
dp[key] = (oct, top_y)
assert oct <= ITER
if oct == ITER:
break
b = BP[next_block]
b = add(b, 2, top_y + 4)
while True:
# jet of gas
dir = A[time]
time = (time + 1) % len(A)
if dir == '>':
dx = 1
else:
dx = -1
newb = add(b, dx, 0)
if not collide(newb):
b = newb
# fall down
newb = add(b, 0, -1)
if not collide(newb):
b = newb
else:
break
for x, y in b:
top_y = max(top_y, y)
grid.add((x, y))
next_block = (next_block + 1) % len(BP)
oct += 1
if oct == ITER:
break
print(top_y + offset + 1)