My Advent of Code solutions in Python. kevinyap.ca/2019/12/going-fast-in-advent-of-code/
advent-of-code python
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 173 lines 4.3 kB view raw
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