My Advent of Code solutions in Python.
kevinyap.ca/2019/12/going-fast-in-advent-of-code/
advent-of-code
python
1#!/usr/bin/env python
2from collections import defaultdict
3
4# Potential debug output
5BREAKPOINTS = set([])
6LAST_SEEN = defaultdict(list)
7
8# True = read, False = write
9INSTRUCTIONS = {
10 1: ('ADD', (True, True, False)),
11 2: ('MUL', (True, True, False)),
12 3: ('INP', (False, None, None)),
13 4: ('OUT', (True, None, None)),
14 5: ('JZ ', (True, True, None)),
15 6: ('JNZ', (True, True, None)),
16 7: ('LT ', (True, True, False)),
17 8: ('EQ ', (True, True, False)),
18 9: ('REL', (True, None, None)),
19 99: ('HLT', (None, None, None)),
20}
21
22
23def debug_param_mode(param, mode, relative_base):
24 if mode == 0:
25 return '%({})'.format(param)
26 elif mode == 1:
27 return str(param)
28 elif mode == 2:
29 return '%({}{}{})'.format(relative_base, '+' if param >= 0 else '', param)
30
31
32def parse_mode(mode_op, a, b, c):
33 op = mode_op % 100
34 mode_a = (mode_op // 100) % 10
35 mode_b = (mode_op // 1000) % 10
36 mode_c = (mode_op // 10000) % 10
37
38 return op, mode_a, mode_b, mode_c
39
40
41def emulate(base_tape, inputs, pc=0, relative_base=0, debug=False):
42 tape = defaultdict(int)
43 for i, v in enumerate(base_tape):
44 tape[i] = v
45
46 def resolve_modes(op, params, modes):
47 res = [a, b, c]
48
49 for i, (is_read, p, m) in enumerate(zip(INSTRUCTIONS[op][1], params, modes)):
50 if is_read is None:
51 continue
52
53 if is_read:
54 if m == 0:
55 res[i] = tape[p]
56 elif m == 1:
57 res[i] = p
58 elif m == 2:
59 res[i] = tape[relative_base + p]
60 else:
61 if m == 0:
62 res[i] = p
63 elif m == 2:
64 res[i] = relative_base + p
65
66 return res
67
68 while True:
69 mode_op = tape[pc]
70 a = tape[pc+1]
71 b = tape[pc+2]
72 c = tape[pc+3]
73 op, mode_a, mode_b, mode_c = parse_mode(mode_op, a, b, c)
74 modes = [mode_a, mode_b, mode_c]
75 last_pc = pc
76
77 if debug:
78 foo = INSTRUCTIONS.get(op, ('???', ()))
79 ins = foo[0]
80 n = sum(i is not None for i in foo[1])
81 params = ' '.join(debug_param_mode(tape[pc+1+i], modes[i], relative_base) for i in range(n))
82 print 'PC: {:3} RB: {:3} |'.format(pc, relative_base), ins, params
83
84 if pc in BREAKPOINTS:
85 if pc in LAST_SEEN:
86 for i, (m, n) in enumerate(zip(LAST_SEEN[pc], tape)):
87 if m != n:
88 print " | > {}: {} -> {}".format(i, m, n)
89
90 LAST_SEEN[last_pc] = tape[:]
91 time.sleep(0.001)
92
93 a, b, c = resolve_modes(op, (a, b, c), (mode_a, mode_b, mode_c))
94
95 # ADD a b c
96 if op == 1:
97 tape[c] = a + b
98 pc += 4
99
100 # MUL a b c
101 elif op == 2:
102 tape[c] = a * b
103 pc += 4
104
105 # INP a
106 elif op == 3:
107 if len(inputs) == 0:
108 tape[a] = -1
109 else:
110 tape[a] = inputs.pop()
111 pc += 2
112
113 # OUT b
114 elif op == 4:
115 yield a
116 pc += 2
117
118 # JZ a b
119 elif op == 5:
120 if a != 0:
121 pc = b
122 else:
123 pc += 3
124
125 # JNZ a b
126 elif op == 6:
127 if a == 0:
128 pc = b
129 else:
130 pc += 3
131
132 # LT a b c
133 elif op == 7:
134 tape[c] = 1 if a < b else 0
135 pc += 4
136
137 # EQ a b c
138 elif op == 8:
139 tape[c] = 1 if a == b else 0
140 pc += 4
141
142 elif op == 9:
143 relative_base += a
144 pc += 2
145
146 # HALT
147 elif op == 99:
148 raise StopIteration()
149
150
151if __name__ == '__main__':
152 import sys
153
154 if len(sys.argv) < 2:
155 print "Usage: intcode.py <intcode_program> [stdin]"
156 sys.exit(2)
157
158 with open(sys.argv[1]) as f:
159 tape = [int(x) for x in f.readlines()[0].split(',')]
160
161 inputs = []
162
163 if len(sys.argv) >= 3:
164 with open(sys.argv[2]) as f:
165 inputs = [int(x) for x in f.readlines()[0].split(',')]
166
167 vm = emulate(tape, inputs)
168
169 try:
170 while True:
171 print(next(vm))
172 except StopIteration:
173 pass