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.

Refactor later Intcode puzzles

Make use of the complete intcode VM past day 9.

+63 -363
+3 -116
2019/day09.py
··· 1 1 import fileinput 2 2 3 - 4 - # True = read, False = write 5 - INSTRUCTIONS = { 6 - 1: (True, True, False), 7 - 2: (True, True, False), 8 - 3: (False, None, None), 9 - 4: (True, None, None), 10 - 5: (True, True, None), 11 - 6: (True, True, None), 12 - 7: (True, True, False), 13 - 8: (True, True, False), 14 - 9: (True, None, None), 15 - 99: (None, None, None) 16 - } 17 - 18 - 19 - GLOBAL_INPUTS = [0] 20 - 21 - 22 - def parse_mode(tape, mode_op, a, b, c): 23 - op = mode_op % 100 24 - mode_a = (mode_op // 100) % 10 25 - mode_b = (mode_op // 1000) % 10 26 - mode_c = (mode_op // 10000) % 10 27 - 28 - return op, mode_a, mode_b, mode_c 29 - 30 - 31 - def emulate(pid, pc=0): 32 - tape = TAPE[:] 33 - outs = [] 34 - relative_base = 0 35 - 36 - def resolve_modes(op, params, modes): 37 - res = [a, b, c] 38 - 39 - for i, (is_read, p, m) in enumerate(zip(INSTRUCTIONS[op], params, modes)): 40 - if is_read is None: 41 - continue 42 - 43 - if is_read: 44 - if m == 0: 45 - res[i] = tape[p] 46 - elif m == 1: 47 - res[i] = p 48 - elif m == 2: 49 - res[i] = tape[relative_base + p] 50 - else: 51 - if m == 0: 52 - res[i] = p 53 - elif m == 2: 54 - res[i] = relative_base + p 55 - 56 - return res 57 - 58 - while pc < len(tape): 59 - mode_op, a, b, c = tape[pc:pc+4] 60 - op, mode_a, mode_b, mode_c = parse_mode(tape, mode_op, a, b, c) 61 - a, b, c = resolve_modes(op, (a, b, c), (mode_a, mode_b, mode_c)) 62 - 63 - # ADD a b c 64 - if op == 1: 65 - tape[c] = a + b 66 - pc += 4 67 - 68 - # MUL a b c 69 - elif op == 2: 70 - tape[c] = a * b 71 - pc += 4 72 - 73 - # INP a 74 - elif op == 3: 75 - tape[a] = GLOBAL_INPUTS[pid] 76 - pc += 2 77 - 78 - # OUT b 79 - elif op == 4: 80 - outs.append(a) 81 - pc += 2 82 - 83 - # JZ a b 84 - elif op == 5: 85 - if a != 0: 86 - pc = b 87 - else: 88 - pc += 3 89 - 90 - # JNZ a b 91 - elif op == 6: 92 - if a == 0: 93 - pc = b 94 - else: 95 - pc += 3 96 - 97 - # LT a b c 98 - elif op == 7: 99 - tape[c] = 1 if a < b else 0 100 - pc += 4 101 - 102 - # EQ a b c 103 - elif op == 8: 104 - tape[c] = 1 if a == b else 0 105 - pc += 4 106 - 107 - elif op == 9: 108 - relative_base += a 109 - pc += 2 110 - 111 - # HALT 112 - elif op == 99: 113 - return outs 3 + from intcode import emulate 114 4 115 5 # Read input 116 6 TAPE = [int(x) for x in fileinput.input()[0].split(',')] 117 7 TAPE += [0] * 100000 # Pad memory 118 8 119 - GLOBAL_INPUTS[0] = 1 120 - print "BOOST keycode:", emulate(0)[0] 121 - 122 - GLOBAL_INPUTS[0] = 2 123 - print "Coordinates of distress signal:", emulate(0)[0] 9 + print "BOOST keycode:", next(emulate(TAPE, [1])) 10 + print "Coordinates of distress signal:", next(emulate(TAPE, [2]))
+7 -116
2019/day11.py
··· 2 2 import fileinput 3 3 from collections import defaultdict 4 4 5 - 6 - GLOBAL_INPUTS = [0] 7 - 8 - # True = read, False = write 9 - INSTRUCTIONS = { 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 - 23 - def parse_mode(tape, mode_op, a, b, c): 24 - op = mode_op % 100 25 - mode_a = (mode_op // 100) % 10 26 - mode_b = (mode_op // 1000) % 10 27 - mode_c = (mode_op // 10000) % 10 28 - 29 - return op, mode_a, mode_b, mode_c 30 - 31 - 32 - def emulate(pid, pc=0): 33 - tape = TAPE[:] 34 - outs = [] 35 - relative_base = 0 5 + from intcode import emulate 36 6 37 - def resolve_modes(op, params, modes): 38 - res = [a, b, c] 39 7 40 - for i, (is_read, p, m) in enumerate(zip(INSTRUCTIONS[op][1], params, modes)): 41 - if is_read is None: 42 - continue 43 - 44 - if is_read: 45 - if m == 0: 46 - res[i] = tape[p] 47 - elif m == 1: 48 - res[i] = p 49 - elif m == 2: 50 - res[i] = tape[relative_base + p] 51 - else: 52 - if m == 0: 53 - res[i] = p 54 - elif m == 2: 55 - res[i] = relative_base + p 56 - 57 - return res 58 - 59 - while pc < len(tape): 60 - mode_op, a, b, c = tape[pc:pc+4] 61 - op, mode_a, mode_b, mode_c = parse_mode(tape, mode_op, a, b, c) 62 - a, b, c = resolve_modes(op, (a, b, c), (mode_a, mode_b, mode_c)) 63 - 64 - # ADD a b c 65 - if op == 1: 66 - tape[c] = a + b 67 - pc += 4 68 - 69 - # MUL a b c 70 - elif op == 2: 71 - tape[c] = a * b 72 - pc += 4 73 - 74 - # INP a 75 - elif op == 3: 76 - tape[a] = GLOBAL_INPUTS[pid] 77 - pc += 2 78 - 79 - # OUT b 80 - elif op == 4: 81 - yield a 82 - pc += 2 83 - 84 - # JZ a b 85 - elif op == 5: 86 - if a != 0: 87 - pc = b 88 - else: 89 - pc += 3 90 - 91 - # JNZ a b 92 - elif op == 6: 93 - if a == 0: 94 - pc = b 95 - else: 96 - pc += 3 97 - 98 - # LT a b c 99 - elif op == 7: 100 - tape[c] = 1 if a < b else 0 101 - pc += 4 102 - 103 - # EQ a b c 104 - elif op == 8: 105 - tape[c] = 1 if a == b else 0 106 - pc += 4 107 - 108 - elif op == 9: 109 - relative_base += a 110 - pc += 2 111 - 112 - # HALT 113 - elif op == 99: 114 - raise StopIteration() 115 - 116 - 117 - def simulate_robot(start=0): 8 + def simulate_robot(tape, start=0): 118 9 grid = {} 119 10 grid[0, 0] = start 120 11 121 12 facing = 0 122 13 x, y = 0, 0 123 14 124 - robot = emulate(0) 15 + inputs = [start] 16 + robot = emulate(tape[:], inputs) 125 17 126 18 try: 127 19 while True: ··· 140 32 elif facing == 3: 141 33 x -= 1 142 34 143 - GLOBAL_INPUTS[0] = grid.get((x, y), 0) 35 + inputs[0] = grid.get((x, y), 0) 144 36 except StopIteration: 145 37 return grid 146 38 ··· 149 41 TAPE = [int(x) for x in fileinput.input()[0].split(',')] 150 42 TAPE += [0] * 100000 151 43 152 - 153 44 # Part 1 154 - grid = simulate_robot(0) 45 + grid = simulate_robot(TAPE, 0) 155 46 print "Number of painted tiles:", len(grid.keys()) 156 47 157 48 158 49 # Part 2 159 - grid = simulate_robot(1) 50 + grid = simulate_robot(TAPE, 1) 160 51 161 52 print "Registration ID:\n" 162 53 xs, ys = zip(*grid.keys())
+6 -114
2019/day13.py
··· 1 1 import fileinput 2 2 from collections import Counter 3 3 4 - 5 - GLOBAL_INPUTS = [0] 6 - 7 - 8 - # True = read, False = write 9 - INSTRUCTIONS = { 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 - 23 - def parse_mode(tape, mode_op, a, b, c): 24 - op = mode_op % 100 25 - mode_a = (mode_op // 100) % 10 26 - mode_b = (mode_op // 1000) % 10 27 - mode_c = (mode_op // 10000) % 10 28 - 29 - return op, mode_a, mode_b, mode_c 30 - 31 - 32 - def emulate(pid, pc=0): 33 - tape = TAPE[:] 34 - relative_base = 0 35 - 36 - def resolve_modes(op, params, modes): 37 - res = [a, b, c] 38 - 39 - for i, (is_read, p, m) in enumerate(zip(INSTRUCTIONS[op][1], params, modes)): 40 - if is_read is None: 41 - continue 42 - 43 - if is_read: 44 - if m == 0: 45 - res[i] = tape[p] 46 - elif m == 1: 47 - res[i] = p 48 - elif m == 2: 49 - res[i] = tape[relative_base + p] 50 - else: 51 - if m == 0: 52 - res[i] = p 53 - elif m == 2: 54 - res[i] = relative_base + p 55 - 56 - return res 57 - 58 - while pc < len(tape): 59 - mode_op, a, b, c = tape[pc:pc+4] 60 - op, mode_a, mode_b, mode_c = parse_mode(tape, mode_op, a, b, c) 61 - a, b, c = resolve_modes(op, (a, b, c), (mode_a, mode_b, mode_c)) 62 - 63 - # ADD a b c 64 - if op == 1: 65 - tape[c] = a + b 66 - pc += 4 67 - 68 - # MUL a b c 69 - elif op == 2: 70 - tape[c] = a * b 71 - pc += 4 72 - 73 - # INP a 74 - elif op == 3: 75 - tape[a] = GLOBAL_INPUTS[pid] 76 - pc += 2 77 - 78 - # OUT b 79 - elif op == 4: 80 - yield a 81 - pc += 2 82 - 83 - # JZ a b 84 - elif op == 5: 85 - if a != 0: 86 - pc = b 87 - else: 88 - pc += 3 89 - 90 - # JNZ a b 91 - elif op == 6: 92 - if a == 0: 93 - pc = b 94 - else: 95 - pc += 3 96 - 97 - # LT a b c 98 - elif op == 7: 99 - tape[c] = 1 if a < b else 0 100 - pc += 4 101 - 102 - # EQ a b c 103 - elif op == 8: 104 - tape[c] = 1 if a == b else 0 105 - pc += 4 106 - 107 - elif op == 9: 108 - relative_base += a 109 - pc += 2 110 - 111 - # HALT 112 - elif op == 99: 113 - raise StopIteration() 4 + from intcode import emulate 114 5 115 6 116 7 # Read input ··· 118 9 TAPE += [0] * 100000 119 10 TAPE[0] = 2 120 11 121 - game = emulate(0) 12 + inputs = [0] 13 + game = emulate(TAPE, inputs) 122 14 grid = {} 123 15 ball_x = 0 124 16 padd_x = 0 ··· 143 35 ball_x = x 144 36 145 37 if ball_x < padd_x: 146 - GLOBAL_INPUTS[0] = -1 38 + inputs[0] = -1 147 39 elif ball_x > padd_x: 148 - GLOBAL_INPUTS[0] = 1 40 + inputs[0] = 1 149 41 else: 150 - GLOBAL_INPUTS[0] = 0 42 + inputs[0] = 0 151 43 152 44 except StopIteration: 153 45 print "Score after last block is broken:", score
+7 -7
2019/day15.py
··· 21 21 } 22 22 23 23 24 - def robot_dfs(vm, graph, curr, approach_d): 24 + def robot_dfs(vm, instructions, graph, curr, approach_d): 25 25 for d in range(4): 26 26 np = curr + ROBOT_DIRS[d] 27 27 if np in graph: 28 28 continue 29 29 30 30 # Attempt to move to next tile 31 - GLOBAL_INPUTS[0] = d + 1 31 + instructions[0] = d + 1 32 32 resp = next(vm) 33 33 34 34 if resp == 0: ··· 38 38 global OXYGEN 39 39 OXYGEN = np 40 40 BOARD[np] = resp 41 - robot_dfs(vm, graph, np, d) 41 + robot_dfs(vm, instructions, graph, np, d) 42 42 43 43 # Can't rely on call stack alone to backtrack 44 - GLOBAL_INPUTS[0] = INVERSE_DIRS[approach_d] + 1 44 + instructions[0] = INVERSE_DIRS[approach_d] + 1 45 45 next(vm) 46 46 47 47 ··· 74 74 TAPE = [int(x) for x in fileinput.input()[0].split(',')] 75 75 TAPE += [0] * 100000 76 76 77 - GLOBAL_INPUTS = [0] 78 77 BOARD = {} 79 78 OXYGEN = None 80 79 81 - vm = emulate(TAPE, 0, GLOBAL_INPUTS) 82 - robot_dfs(vm, BOARD, Point(0, 0), 0) 80 + instructions = [0] 81 + vm = emulate(TAPE, instructions) 82 + robot_dfs(vm, instructions, BOARD, Point(0, 0), 0) 83 83 84 84 print "Optimal movement to oxygen:", bfs(BOARD, Point(0, 0), OXYGEN) 85 85 print "Minutes taken to fill up:", bfs(BOARD, OXYGEN) - 1
+2 -2
2019/day17.py
··· 10 10 11 11 12 12 # Part 1 13 - vm = emulate(TAPE[:], 0, []) 13 + vm = emulate(TAPE[:], []) 14 14 board = [] 15 15 16 16 try: ··· 61 61 instructions.append(ord('\n')) 62 62 63 63 TAPE[0] = 2 64 - for c in emulate(TAPE[:], 0, instructions, seq_input=True): 64 + for c in emulate(TAPE[:], instructions): 65 65 try: 66 66 print chr(c), 67 67 except Exception:
+1 -1
2019/day19.py
··· 10 10 if x < 0 or y < 0: 11 11 return 0 12 12 13 - vm = emulate(TAPE, 0, [x, y], seq_input=True) 13 + vm = emulate(TAPE, [x, y]) 14 14 return next(vm) 15 15 16 16
+37 -7
2019/intcode.py
··· 1 + from collections import defaultdict 2 + 3 + # Potential debug output 4 + BREAKPOINTS = set([]) 5 + LAST_SEEN = defaultdict(list) 6 + 1 7 # True = read, False = write 2 8 INSTRUCTIONS = { 3 9 1: ('ADD', (True, True, False)), ··· 13 19 } 14 20 15 21 22 + def debug_param_mode(param, mode, relative_base): 23 + if mode == 0: 24 + return '%({})'.format(param) 25 + elif mode == 1: 26 + return str(param) 27 + elif mode == 2: 28 + return '%({}{}{})'.format(relative_base, '+' if param >= 0 else '', param) 29 + 30 + 16 31 def parse_mode(tape, mode_op, a, b, c): 17 32 op = mode_op % 100 18 33 mode_a = (mode_op // 100) % 10 ··· 22 37 return op, mode_a, mode_b, mode_c 23 38 24 39 25 - def emulate(tape, pid, inputs, pc=0, seq_input=True): 40 + def emulate(tape, inputs, pc=0, relative_base=0, debug=False): 26 41 tape = tape[:] 27 - relative_base = 0 28 42 ipc = 0 29 43 30 44 def resolve_modes(op, params, modes): ··· 52 66 while pc < len(tape): 53 67 mode_op, a, b, c = tape[pc:pc+4] 54 68 op, mode_a, mode_b, mode_c = parse_mode(tape, mode_op, a, b, c) 69 + modes = [mode_a, mode_b, mode_c] 70 + last_pc = pc 71 + 72 + if debug: 73 + foo = INSTRUCTIONS.get(op, ('???', ())) 74 + ins = foo[0] 75 + n = sum(i is not None for i in foo[1]) 76 + params = ' '.join(debug_param_mode(tape[pc+1+i], modes[i], relative_base) for i in range(n)) 77 + print 'PC: {:3} RB: {:3} |'.format(pc, relative_base), ins, params 78 + 79 + if pc in BREAKPOINTS: 80 + if pc in LAST_SEEN: 81 + for i, (m, n) in enumerate(zip(LAST_SEEN[pc], tape)): 82 + if m != n: 83 + print " | > {}: {} -> {}".format(i, m, n) 84 + 85 + LAST_SEEN[last_pc] = tape[:] 86 + time.sleep(0.001) 87 + 55 88 a, b, c = resolve_modes(op, (a, b, c), (mode_a, mode_b, mode_c)) 56 89 57 90 # ADD a b c ··· 66 99 67 100 # INP a 68 101 elif op == 3: 69 - if seq_input: 70 - tape[a] = inputs[ipc % len(inputs)] 71 - ipc += 1 72 - else: 73 - tape[a] = inputs[pid] 102 + tape[a] = inputs[ipc % len(inputs)] 103 + ipc += 1 74 104 pc += 2 75 105 76 106 # OUT b