Browse Source

Refactor intcode into yielding version with helpers

Taddeus Kroes 6 years ago
parent
commit
7f6445c7cc
11 changed files with 61 additions and 149 deletions
  1. 4 40
      2019/05_diag.py
  2. 8 38
      2019/07_amplifiers.py
  3. 3 3
      2019/09_boost.py
  4. 2 2
      2019/11_paintrobot.py
  5. 3 3
      2019/13_arcade.py
  6. 2 2
      2019/15_repair.py
  7. 4 4
      2019/17_ascii.py
  8. 2 2
      2019/19_beam.py
  9. 3 3
      2019/21_springdroid.py
  10. 6 8
      2019/23_network.py
  11. 24 44
      2019/intcode.py

+ 4 - 40
2019/05_diag.py

@@ -1,43 +1,7 @@
 #!/usr/bin/env python3
 import sys
+from intcode import read_program, run_inputs
 
-def run(p, inputs):
-    pc = 0
-    while p[pc] != 99:
-        modes, opcode = divmod(p[pc], 100)
-
-        def modeswitch(offset):
-            value = p[pc + offset]
-            mode = modes // (10 ** (offset - 1)) % 10
-            return value if mode else p[value]
-
-        if opcode in (1, 2):
-            a = modeswitch(1)
-            b = modeswitch(2)
-            out = p[pc + 3]
-            p[out] = a + b if opcode == 1 else a * b
-            pc += 4
-        elif opcode == 3:
-            address = p[pc + 1]
-            p[address] = inputs.pop()
-            pc += 2
-        elif opcode == 4:
-            yield modeswitch(1)
-            pc += 2
-        elif opcode == 5:
-            pc = modeswitch(2) if modeswitch(1) else pc + 3
-        elif opcode == 6:
-            pc = modeswitch(2) if not modeswitch(1) else pc + 3
-        elif opcode in (7, 8):
-            a = modeswitch(1)
-            b = modeswitch(2)
-            out = p[pc + 3]
-            p[out] = int(a < b if opcode == 7 else a == b)
-            pc += 4
-
-def initrun(p, inp):
-    return list(run(list(p), list(inp)))
-
-program = list(map(int, sys.stdin.read().split(',')))
-print(initrun(program, [1])[-1])
-print(initrun(program, [5])[-1])
+program = read_program(sys.stdin)
+print(list(run_inputs(program, [1]))[-1])
+print(list(run_inputs(program, [5]))[-1])

+ 8 - 38
2019/07_amplifiers.py

@@ -1,55 +1,25 @@
 #!/usr/bin/env python3
 import sys
 from itertools import permutations
-
-def run(p, inputs):
-    pc = 0
-    while p[pc] != 99:
-        modes, opcode = divmod(p[pc], 100)
-
-        def modeswitch(offset):
-            value = p[pc + offset]
-            mode = modes // (10 ** (offset - 1)) % 10
-            return value if mode else p[value]
-
-        if opcode in (1, 2):
-            a = modeswitch(1)
-            b = modeswitch(2)
-            out = p[pc + 3]
-            p[out] = a + b if opcode == 1 else a * b
-            pc += 4
-        elif opcode == 3:
-            address = p[pc + 1]
-            p[address] = inputs.pop()
-            pc += 2
-        elif opcode == 4:
-            inputs = yield modeswitch(1)
-            pc += 2
-        elif opcode == 5:
-            pc = modeswitch(2) if modeswitch(1) else pc + 3
-        elif opcode == 6:
-            pc = modeswitch(2) if not modeswitch(1) else pc + 3
-        elif opcode in (7, 8):
-            a = modeswitch(1)
-            b = modeswitch(2)
-            out = p[pc + 3]
-            p[out] = int(a < b if opcode == 7 else a == b)
-            pc += 4
+from intcode import read_program, run
 
 def amplify(p, phases):
     amps = []
     signal = 0
     for phase in phases:
-        amp = run(list(p), [signal, phase])
+        amp = run(p)
+        assert next(amp) is None
+        assert amp.send(phase) is None
+        signal = amp.send(signal)
         amps.append(amp)
-        signal = next(amp)
     try:
         while True:
             for amp in amps:
-                signal = amp.send([signal])
+                assert next(amp) is None
+                signal = amp.send(signal)
     except StopIteration:
         return signal
 
-program = list(map(int, sys.stdin.read().split(',')))
+program = read_program(sys.stdin)
 print(max(amplify(program, phases) for phases in permutations(range(5))))
 print(max(amplify(program, phases) for phases in permutations(range(5, 10))))

+ 3 - 3
2019/09_boost.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 import sys
-from intcode import read_program, run
+from intcode import read_program, run_inputs
 
 program = read_program(sys.stdin)
-print(next(run(program, [1].pop, 10000)))
-print(next(run(program, [2].pop, 10000)))
+print(next(run_inputs(program, [1])))
+print(next(run_inputs(program, [2])))

+ 2 - 2
2019/11_paintrobot.py

@@ -1,9 +1,9 @@
 #!/usr/bin/env python3
 import sys
-from intcode import read_program, run
+from intcode import read_program, run_getter
 
 def paint(firmware, color):
-    robot = run(firmware, lambda: color, 1000)
+    robot = run_getter(firmware, lambda: color)
     painted = set()
     white = set()
     x = y = 0

+ 3 - 3
2019/13_arcade.py

@@ -2,7 +2,7 @@
 import sys
 from itertools import islice
 from time import sleep
-from intcode import read_program, run
+from intcode import read_program, run_getter
 
 def makegrid(game):
     x, y, ident = islice(game, 3)
@@ -27,7 +27,7 @@ def draw(grid, score):
     print('score:', score)
 
 def play(code, verbose):
-    game = run(code, lambda: xball - xpaddle, 1000)
+    game = run_getter(code, lambda: xball - xpaddle)
     grid, score = makegrid(game)
     xpaddle = xball = 0
     if verbose:
@@ -51,7 +51,7 @@ def play(code, verbose):
 
 # part 1
 code = read_program(sys.stdin)
-outp = list(run(code, lambda: 0, 1000))
+outp = list(run_getter(code, lambda: 0))
 print(outp[2::3].count(2))
 
 # part 2

+ 2 - 2
2019/15_repair.py

@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 import sys
 from time import sleep
-from intcode import read_program, run
+from intcode import read_program, run_getter
 
 NORTH, SOUTH, EAST, WEST = range(1, 5)
 WALL, SPACE, OXYGEN, UNKOWN = range(4)
@@ -30,7 +30,7 @@ def map_area(program):
 
     pos = 0, 0
     direction = NORTH
-    control = run(program, lambda: direction, 0)
+    control = run_getter(program, lambda: direction)
     mapped = {pos: SPACE}
     path = [(NORTH, pos)]
     oxygen_distance = None

+ 4 - 4
2019/17_ascii.py

@@ -1,10 +1,10 @@
 #!/usr/bin/env python3
 import sys
 from itertools import combinations, islice
-from intcode import read_program, run
+from intcode import read_program, run, run_inputs
 
 def read_grid(program):
-    output = ''.join(map(chr, run(program, None, 10000)))
+    output = ''.join(map(chr, run(program)))
     lines = output.rstrip().split('\n')
     def gen_lines():
         yield '.' * len(lines[0])
@@ -97,8 +97,8 @@ def make_subroutines(path):
 
 def rescue(program, main, subs):
     lines = [main, subs['A'], subs['B'], subs['C'], 'n']
-    inputs = list(map(ord, ('\n'.join(lines) + '\n')[::-1]))
-    for output in run(program, inputs.pop, 10000):
+    inputs = map(ord, ('\n'.join(lines) + '\n'))
+    for output in run_inputs(program, inputs):
         pass
     return output
 

+ 2 - 2
2019/19_beam.py

@@ -2,10 +2,10 @@
 import sys
 from collections import deque
 from itertools import islice
-from intcode import read_program, run
+from intcode import read_program, run_inputs
 
 def deploy_drone(program, x, y):
-    return next(run(program, [y, x].pop, 1000))
+    return next(run_inputs(program, [x, y]))
 
 def scan(program, w, h):
     return [[deploy_drone(program, x, y) for x in range(w)] for y in range(h)]

+ 3 - 3
2019/21_springdroid.py

@@ -1,10 +1,10 @@
 #!/usr/bin/env python3
 import sys
-from intcode import read_program, run
+from intcode import read_program, run_inputs
 
 def run_springscript(program, script):
-    inputs = list(map(ord, reversed(script.lstrip())))
-    interp = run(program, inputs.pop, 1000)
+    inputs = map(ord, script.lstrip())
+    interp = run_inputs(program, inputs)
     return next(out for out in interp if out > 128)
 
 # Jump if A or C is a hole and D is reachable:

+ 6 - 8
2019/23_network.py

@@ -1,18 +1,16 @@
 #!/usr/bin/env python3
 import sys
-from intcode import read_program, run_iter
+from intcode import read_program, run
 
 def run_nat(program):
-    def boot(i):
-        nic = run_iter(program, 100)
-        assert next(nic) is None
-        return nic
-
-    nics, messages = zip(*[(boot(i), [i]) for i in range(50)])
+    nics, messages = zip(*[(run(program), [i]) for i in range(50)])
     natpack = 0, 0
 
+    for nic in nics:
+        assert next(nic) is None
+
     while True:
-        for i, (nic, msg) in enumerate(zip(nics, messages)):
+        for nic, msg in zip(nics, messages):
             msg.append(-1)
             while msg:
                 dst = nic.send(msg.pop(0))

+ 24 - 44
2019/intcode.py

@@ -3,20 +3,26 @@ from operator import add, mul, lt, eq
 def read_program(f):
     return list(map(int, f.readline().split(',')))
 
-def run(p, get_input, memsize=0):
+def run(p):
     def decode_param(offset):
         return p[pc + offset], modes // (10 ** (offset - 1)) % 10
 
+    def address(param, mode):
+        addr = param + relbase * mode // 2
+        if addr >= len(p):
+            p.extend([0] * (addr - len(p) + 1))
+        return addr
+
     def pload(offset):
         param, mode = decode_param(offset)
-        return param if mode == 1 else p[param + relbase * mode // 2]
+        return param if mode == 1 else p[address(param, mode)]
 
     def pstore(offset, value):
         param, mode = decode_param(offset)
-        p[param + relbase * mode // 2] = value
+        p[address(param, mode)] = value
 
     opmap = {1: add, 2: mul, 7: lt, 8: eq}
-    p = p + [0] * memsize
+    p = list(p)
     pc = relbase = 0
 
     while p[pc] != 99:
@@ -26,7 +32,7 @@ def run(p, get_input, memsize=0):
             pstore(3, opmap[opcode](pload(1), pload(2)))
             pc += 4
         elif opcode == 3:
-            pstore(1, get_input())
+            pstore(1, (yield))
             pc += 2
         elif opcode == 4:
             yield pload(1)
@@ -39,42 +45,16 @@ def run(p, get_input, memsize=0):
             relbase += pload(1)
             pc += 2
 
-def run_iter(p, memsize=0):
-    def decode_param(offset):
-        return p[pc + offset], modes // (10 ** (offset - 1)) % 10
-
-    def pload(offset):
-        param, mode = decode_param(offset)
-        return param if mode == 1 else p[param + relbase * mode // 2]
-
-    def pstore(offset, value):
-        param, mode = decode_param(offset)
-        p[param + relbase * mode // 2] = value
-
-    opmap = {1: add, 2: mul, 7: lt, 8: eq}
-    p = p + [0] * memsize
-    pc = relbase = 0
-
-    while p[pc] != 99:
-        modes, opcode = divmod(p[pc], 100)
-
-        if opcode in (1, 2, 7, 8):
-            pstore(3, opmap[opcode](pload(1), pload(2)))
-            pc += 4
-        elif opcode == 3:
-            inp = (yield)
-            #print('inp', inp)
-            pstore(1, inp)
-            pc += 2
-        elif opcode == 4:
-            outp = pload(1)
-            #print('outp', outp)
-            yield outp
-            pc += 2
-        elif opcode == 5:
-            pc = pload(2) if pload(1) else pc + 3
-        elif opcode == 6:
-            pc = pload(2) if not pload(1) else pc + 3
-        elif opcode == 9:
-            relbase += pload(1)
-            pc += 2
+def run_inputs(p, inputs):
+    inputs = iter(inputs)
+    computer = run(p)
+    for outp in computer:
+        while outp is None:
+            outp = computer.send(next(inputs))
+        yield outp
+
+def run_getter(p, get_input):
+    def gen_inputs():
+        while True:
+            yield get_input()
+    return run_inputs(p, gen_inputs())