-
Notifications
You must be signed in to change notification settings - Fork 0
/
day13.py
154 lines (127 loc) · 4.14 KB
/
day13.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
from day11 import run_intcode as run_intcode_old
from collections import defaultdict
def parse_op(op):
sop = str(op)[::-1]
instr, modes = int(sop[:2][::-1]), sop[2:]
return instr, modes + "0" * 4
def get(ops, ip, mode, base):
if mode == "0":
return ops[ops[ip]]
elif mode == "1":
return ops[ip]
elif mode == "2":
return ops[base + ops[ip]]
raise ValueError(mode)
class PositiveDefaultDict(defaultdict):
def __missing__(self, i):
if i < 0:
raise IndexError(i)
return super().__missing__(i)
def run_intcode(program, out_fn, in_fn):
ops = PositiveDefaultDict(int)
ops.update(enumerate(program))
ip = 0
base = 0
while ip < len(ops):
op, modes = parse_op(ops[ip])
if op == 1: # add
i = ops[ip + 3] + int(modes[2] == "2") * base
ops[i] = get(ops, ip + 1, modes[0], base) + get(ops, ip + 2, modes[1], base)
ip += 4
elif op == 2: # multiply
i = ops[ip + 3] + int(modes[2] == "2") * base
ops[i] = get(ops, ip + 1, modes[0], base) * get(ops, ip + 2, modes[1], base)
ip += 4
elif op == 3: # input
i = ops[ip + 1] + int(modes[0] == "2") * base
ops[i] = in_fn()
assert ops[i] is not None
ip += 2
elif op == 4: # output
out_fn(get(ops, ip + 1, modes[0], base))
ip += 2
elif op == 5: # jump-if-true
if get(ops, ip + 1, modes[0], base) != 0:
ip = get(ops, ip + 2, modes[1], base)
else:
ip += 3
elif op == 6: # jump-if-false
if get(ops, ip + 1, modes[0], base) == 0:
ip = get(ops, ip + 2, modes[1], base)
else:
ip += 3
elif op == 7: # less than
i = ops[ip + 3] + int(modes[2] == "2") * base
ops[i] = int(
get(ops, ip + 1, modes[0], base) < get(ops, ip + 2, modes[1], base)
)
ip += 4
elif op == 8: # equals
i = ops[ip + 3] + int(modes[2] == "2") * base
ops[i] = int(
get(ops, ip + 1, modes[0], base) == get(ops, ip + 2, modes[1], base)
)
ip += 4
elif op == 9: # change relative-base
base += get(ops, ip + 1, modes[0], base)
ip += 2
elif op == 99:
break
else:
raise ValueError(op)
def draw(program):
screen = {}
cabinet = run_intcode_old(program)
for x, y, tile_id in zip(cabinet, cabinet, cabinet):
screen[x, y] = tile_id
return screen
def a(program):
screen = draw(program)
return sum(tile_id == 2 for tile_id in screen.values())
def b(program):
program[0] = 2
width, height = 44, 24
screen = [[" " for _ in range(width)] for _ in range(height)]
tiles = [" ", "#", "+", "-", "O"]
score = 0
def get_ball_pos():
b = None
p = None
for x in range(width):
for y in range(height):
if screen[y][x] == "O":
b = x, y
elif screen[y][x] == "-":
p = x, y
assert b and p
return b, p
def in_fn():
print(f"Score: {score}")
print("\n".join("".join(row) for row in screen))
ball, player = get_ball_pos()
if ball[0] < player[0]:
return -1
return int(ball[0] > player[0])
state = {"x": 0, "y": 0, "tile_id": 0, "i": 0}
def out_fn(a):
nonlocal score
i = state["i"]
if i % 3 == 0:
state["x"] = a
elif i % 3 == 1:
state["y"] = a
elif i % 3 == 2:
state["tile_id"] = a
if state["x"] == -1 and state["y"] == 0:
score = a
else:
x, y = state["x"], state["y"]
screen[y][x] = tiles[state["tile_id"]]
state["i"] += 1
run_intcode(program, out_fn, in_fn)
return score
def main():
program = list(map(int, open("input13.txt").read().split(",")))
print(b(program))
if __name__ == "__main__":
main()