Rewrote intcode in iterator style
parent
dc91635224
commit
196399df32
13
day05.py
13
day05.py
|
@ -1,13 +1,12 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from intcode import Interpreter, SingletonIO
|
from intcode import preproc
|
||||||
|
from lib import last
|
||||||
|
|
||||||
def preproc(puzzle_input):
|
|
||||||
return list(map(int, puzzle_input.split(',')))
|
|
||||||
|
|
||||||
def partI(program):
|
def partI(prog):
|
||||||
return Interpreter(program, SingletonIO).run(1)
|
return last(prog(1))
|
||||||
|
|
||||||
def partII(program):
|
def partII(prog):
|
||||||
return Interpreter(program, SingletonIO).run(5)
|
return last(prog(5))
|
||||||
|
|
||||||
|
|
35
day07.py
35
day07.py
|
@ -1,28 +1,15 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from intcode import Interpreter, SingletonIO
|
from intcode import preproc
|
||||||
from itertools import permutations
|
from itertools import permutations
|
||||||
from lib import memoize
|
from lib import memoize, last
|
||||||
|
|
||||||
stack_size = 5
|
stack_size = 5
|
||||||
fst_amp_input = 0
|
fst_amp_input = 0
|
||||||
|
|
||||||
|
|
||||||
def preproc(puzzle_input):
|
def partI(amp):
|
||||||
program = list(map(int, puzzle_input.split(',')))
|
run_amp = memoize(lambda *args : last(amp(*args)))
|
||||||
def make_amp(phase):
|
|
||||||
amp = Interpreter(program, SingletonIO)
|
|
||||||
amp.eval(phase)
|
|
||||||
return amp
|
|
||||||
return make_amp
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def partI(make_amp):
|
|
||||||
@memoize
|
|
||||||
def run_amp(phase, amp_in):
|
|
||||||
amp = make_amp(phase)
|
|
||||||
return amp.run(amp_in)
|
|
||||||
|
|
||||||
phase_range = range(stack_size)
|
phase_range = range(stack_size)
|
||||||
best = 0
|
best = 0
|
||||||
|
@ -34,18 +21,18 @@ def partI(make_amp):
|
||||||
best = max(amp_in, best)
|
best = max(amp_in, best)
|
||||||
return best
|
return best
|
||||||
|
|
||||||
def partII(make_amp):
|
def partII(amp):
|
||||||
phase_range = range(5, 10)
|
phase_range = range(5, 10)
|
||||||
best = 0
|
best = 0
|
||||||
|
|
||||||
for perm in permutations(phase_range):
|
for perm in permutations(phase_range):
|
||||||
amps = list(map(make_amp, perm))
|
head, *tail = map(amp, perm)
|
||||||
for prev, amp in zip(amps, amps[1:]):
|
amp_chain = sum(tail, head)
|
||||||
amp.pipe_from(prev)
|
|
||||||
|
amp_chain.append_input(fst_amp_input)
|
||||||
|
|
||||||
amp.write(fst_amp_input)
|
for amp_in in amp_chain:
|
||||||
for amp_in in amp:
|
amp_chain.write_input(amp_in)
|
||||||
amp.write(amp_in)
|
|
||||||
best = max(amp_in, best)
|
best = max(amp_in, best)
|
||||||
return best
|
return best
|
||||||
|
|
||||||
|
|
13
day09.py
13
day09.py
|
@ -1,15 +1,14 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from intcode import Interpreter, SingletonIO
|
from intcode import preproc
|
||||||
|
from lib import last
|
||||||
|
|
||||||
def preproc(puzzle_input):
|
|
||||||
return list(map(int, puzzle_input.split(',')))
|
|
||||||
|
|
||||||
def partI(program):
|
def partI(executable):
|
||||||
return Interpreter(program, SingletonIO).run(1)
|
return last(executable(1))
|
||||||
|
|
||||||
def partII(program):
|
def partII(executable):
|
||||||
return Interpreter(program, SingletonIO).run(2)
|
return last(executable(2))
|
||||||
|
|
||||||
|
|
||||||
from main import Tests
|
from main import Tests
|
||||||
|
|
59
day11.py
59
day11.py
|
@ -1,7 +1,8 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from intcode import Interpreter
|
import intcode
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from itertools import cycle
|
||||||
|
|
||||||
BLACK = 0
|
BLACK = 0
|
||||||
WHITE = 1
|
WHITE = 1
|
||||||
|
@ -41,46 +42,36 @@ class Canvas(defaultdict):
|
||||||
return unicode(self).encode("utf-8")
|
return unicode(self).encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
class robotIO:
|
|
||||||
def __init__(self, canvas=Canvas(color)):
|
|
||||||
self.canvas = canvas
|
|
||||||
self.dir = UP
|
|
||||||
self.pos = 0, 0
|
|
||||||
self.paint = True
|
|
||||||
|
|
||||||
def pop_input(self):
|
|
||||||
return self.canvas[self.pos]
|
|
||||||
|
|
||||||
def append_output(self, x):
|
|
||||||
if self.paint:
|
|
||||||
self.canvas[self.pos] = x
|
|
||||||
else:
|
|
||||||
self.dir = left(self.dir) if x == LEFT else right(self.dir)
|
|
||||||
self.pos = add(self.pos, self.dir)
|
|
||||||
self.paint = not self.paint
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
return self.canvas
|
|
||||||
|
|
||||||
class Robot:
|
class Robot:
|
||||||
def __init__(self, program, canvas=Canvas(color)):
|
def __init__(self, program):
|
||||||
self.canvas = canvas
|
self.pos = (0, 0)
|
||||||
self.program = program
|
self.dir = UP
|
||||||
|
|
||||||
def paint(self):
|
self.comp = intcode.emulator(program)
|
||||||
comp = Interpreter(self.program, robotIO)
|
|
||||||
comp.IO.canvas = self.canvas
|
def paint(self, canvas):
|
||||||
return comp.run()
|
self.comp.write_input(pop_input=lambda : canvas[self.pos])
|
||||||
|
paint_flags = cycle((True, False))
|
||||||
|
|
||||||
|
for pf, x in zip(paint_flags, self.comp):
|
||||||
|
if pf:
|
||||||
|
canvas[self.pos] = x
|
||||||
|
else:
|
||||||
|
self.dir = left(self.dir) if x == LEFT else right(self.dir)
|
||||||
|
self.pos = add(self.pos, self.dir)
|
||||||
|
|
||||||
def preproc(puzzle_input):
|
def preproc(puzzle_input):
|
||||||
return list(map(int, puzzle_input.split(',')))
|
program = intcode.parse(puzzle_input)
|
||||||
|
paint = lambda c : Robot(program).paint(c)
|
||||||
|
return paint
|
||||||
|
|
||||||
def partI(program):
|
def partI(paint):
|
||||||
canvas = Robot(program).paint()
|
canvas = Canvas(color)
|
||||||
|
paint(canvas)
|
||||||
return len(canvas)
|
return len(canvas)
|
||||||
|
|
||||||
def partII(program):
|
def partII(paint):
|
||||||
canvas = Canvas(color)
|
canvas = Canvas(color)
|
||||||
canvas[(0, 0)] = WHITE
|
canvas[(0, 0)] = WHITE
|
||||||
canvas = Robot(program, canvas).paint()
|
paint(canvas)
|
||||||
return '\n' + str(canvas)
|
return '\n' + str(canvas)
|
||||||
|
|
91
day15.py
91
day15.py
|
@ -1,4 +1,6 @@
|
||||||
from intcode import Interpreter, WaitForInput
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import intcode
|
||||||
|
|
||||||
NORTH = 1
|
NORTH = 1
|
||||||
SOUTH = 2
|
SOUTH = 2
|
||||||
|
@ -22,52 +24,29 @@ ENCODING = {WALL : '#',
|
||||||
OXY : 'O'
|
OXY : 'O'
|
||||||
}
|
}
|
||||||
|
|
||||||
def move(pos, dir):
|
def move(pos, d):
|
||||||
if dir == NORTH:
|
if d == NORTH:
|
||||||
return pos[0], pos[1] + 1
|
return pos[0], pos[1] + 1
|
||||||
if dir == SOUTH:
|
if d == SOUTH:
|
||||||
return pos[0], pos[1] - 1
|
return pos[0], pos[1] - 1
|
||||||
if dir == EAST:
|
if d == EAST:
|
||||||
return pos[0] + 1, pos[1]
|
return pos[0] + 1, pos[1]
|
||||||
if dir == WEST:
|
if d == WEST:
|
||||||
return pos[0] - 1, pos[1]
|
return pos[0] - 1, pos[1]
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
class DroidIO:
|
class Droid:
|
||||||
def __init__(self):
|
|
||||||
self._input = None
|
|
||||||
self._output = None
|
|
||||||
|
|
||||||
def pop_input(self):
|
|
||||||
i = self._input
|
|
||||||
if i == None:
|
|
||||||
raise WaitForInput
|
|
||||||
self._input = None
|
|
||||||
return i
|
|
||||||
|
|
||||||
def append_output(self, x):
|
|
||||||
self._output = x
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
o = self._output
|
|
||||||
self._output = None
|
|
||||||
return o
|
|
||||||
|
|
||||||
def write(self, x):
|
|
||||||
self._input = x
|
|
||||||
|
|
||||||
class Droid(Interpreter):
|
|
||||||
def __init__(self, program):
|
def __init__(self, program):
|
||||||
super().__init__(program, DroidIO)
|
|
||||||
self.pos = 0, 0
|
self.pos = 0, 0
|
||||||
|
self.comp = intcode.emulator(program)
|
||||||
|
|
||||||
def move(self, dir):
|
def move(self, d):
|
||||||
out = self.eval(dir)
|
self.comp.write_input(d)
|
||||||
|
out = next(self.comp)
|
||||||
if out != WALL:
|
if out != WALL:
|
||||||
self.pos = move(self.pos, dir)
|
self.pos = move(self.pos, d)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
class Map(dict):
|
class Map(dict):
|
||||||
def __init__(self, encoding):
|
def __init__(self, encoding):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -87,39 +66,39 @@ class Map(dict):
|
||||||
r += '\n'
|
r += '\n'
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def map_space(droid, map=None):
|
def map_space(droid, space_map=None):
|
||||||
if map == None:
|
if space_map == None:
|
||||||
map = Map(ENCODING)
|
space_map = Map(ENCODING)
|
||||||
for dir in DIRS:
|
for d in DIRS:
|
||||||
new_pos = move(droid.pos, dir)
|
new_pos = move(droid.pos, d)
|
||||||
if new_pos in map:
|
if new_pos in space_map:
|
||||||
continue
|
continue
|
||||||
object = droid.move(dir)
|
obj = droid.move(d)
|
||||||
map[new_pos] = object
|
space_map[new_pos] = obj
|
||||||
if object == WALL:
|
if obj == WALL:
|
||||||
continue
|
continue
|
||||||
map_space(droid, map)
|
map_space(droid, space_map)
|
||||||
droid.move(OPPOSITE[dir])
|
droid.move(OPPOSITE[d])
|
||||||
return map
|
return space_map
|
||||||
|
|
||||||
def shortest_paths(map, pos, visited=None):
|
def shortest_paths(space_map, pos, visited=None):
|
||||||
if visited == None:
|
if visited == None:
|
||||||
visited = {pos : 0}
|
visited = {pos : 0}
|
||||||
path = visited[pos] + 1
|
path = visited[pos] + 1
|
||||||
for dir in DIRS:
|
for d in DIRS:
|
||||||
new = move(pos, dir)
|
new = move(pos, d)
|
||||||
if map[new] == WALL or (new in visited and visited[new] <= path):
|
if space_map[new] == WALL or (new in visited and visited[new] <= path):
|
||||||
continue
|
continue
|
||||||
visited[new] = path
|
visited[new] = path
|
||||||
shortest_paths(map, new, visited)
|
shortest_paths(space_map, new, visited)
|
||||||
return visited
|
return visited
|
||||||
|
|
||||||
def preproc(puzzle_input):
|
def preproc(puzzle_input):
|
||||||
program = list(map(int, puzzle_input.split(',')))
|
program = intcode.parse(puzzle_input)
|
||||||
droid = Droid(program)
|
droid = Droid(program)
|
||||||
area_map = map_space(droid)
|
space_map = map_space(droid)
|
||||||
x, y = tuple(filter(lambda x: x[1]==OXY, area_map.items()))[0][0]
|
x, y = tuple(filter(lambda x: x[1]==OXY, space_map.items()))[0][0]
|
||||||
path_map = shortest_paths(area_map, (x, y))
|
path_map = shortest_paths(space_map, (x, y))
|
||||||
return path_map
|
return path_map
|
||||||
|
|
||||||
def partI(path_map):
|
def partI(path_map):
|
||||||
|
|
303
intcode.py
303
intcode.py
|
@ -1,43 +1,74 @@
|
||||||
from collections import defaultdict, deque
|
|
||||||
from enum import Enum
|
|
||||||
from inspect import signature
|
from inspect import signature
|
||||||
|
from lib import defaultlist
|
||||||
import operator
|
import operator
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
|
# Custom Exceptions -------------------------------------------------------
|
||||||
class WaitForInput(Exception):
|
class WaitForInput(Exception):
|
||||||
|
"""Used for interrupting emulator execution."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Halted(Exception):
|
class Halted(Exception):
|
||||||
|
"""Used when calling halted emulator."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class PipeError(Exception):
|
class PipeError(Exception):
|
||||||
|
"""Used when b in pipe a | b needs input and a is halted."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class defaultlist(list):
|
class InputWriteError(Exception):
|
||||||
"""Default list class. Allows writing and reading out of bounds."""
|
"""Raised when overwriting non-empty input buffer"""
|
||||||
def __init__(self, lst, val_factory):
|
|
||||||
super().__init__(lst)
|
|
||||||
self.val_factory = val_factory
|
|
||||||
|
|
||||||
def __setitem__(self, i, x):
|
|
||||||
for _ in range((i - len(self) + 1)):
|
|
||||||
self.append(self.val_factory())
|
|
||||||
super().__setitem__(i, x)
|
|
||||||
|
|
||||||
def __getitem__(self, i):
|
|
||||||
if i >= len(self):
|
|
||||||
return self.val_factory()
|
|
||||||
return super().__getitem__(i)
|
|
||||||
|
|
||||||
|
|
||||||
class Emulator(object):
|
# Emulator ----------------------------------------------------------------
|
||||||
def __init__(self, program, get_input, put_output):
|
def format_input_args(wfunc):
|
||||||
self.in_f = get_input
|
"""
|
||||||
self.out_f = put_output
|
Adds structure to arguments for input functions.
|
||||||
|
Creates an input iterator by chaining args, an optional input iterator
|
||||||
|
and an iterator constructed from a pop_input function.
|
||||||
|
"""
|
||||||
|
def decorated(self, *args, input_iter=iter([]), pop_input=lambda : None):
|
||||||
|
input_iter = chain(iter(args), input_iter, iter(pop_input, None))
|
||||||
|
return wfunc(self, input_iter)
|
||||||
|
return decorated
|
||||||
|
|
||||||
|
|
||||||
|
class emulator():
|
||||||
|
"""
|
||||||
|
Intcode emulator. The emulator is a generator yielding output.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, program):
|
||||||
|
"""
|
||||||
|
The emulator state is composed of memory, current pointer and relative base.
|
||||||
|
Input is fed from an iterator. This iterator is empty by default. Use
|
||||||
|
write/append to add overwrite/append to the input iterator.
|
||||||
|
"""
|
||||||
self.memory = defaultlist(program[:], val_factory = lambda : 0)
|
self.memory = defaultlist(program[:], val_factory = lambda : 0)
|
||||||
self.i = 0
|
self.i = 0
|
||||||
self.rel_base = 0
|
self.rel_base = 0
|
||||||
|
|
||||||
|
self.input_iter = iter([])
|
||||||
|
|
||||||
|
@format_input_args
|
||||||
|
def append_input(self, input_iter):
|
||||||
|
"""Appends input_iter to input iterator"""
|
||||||
|
self.input_iter = chain(self.input_iter, input_iter)
|
||||||
|
|
||||||
|
@format_input_args
|
||||||
|
def write_input(self, input_iter):
|
||||||
|
"""
|
||||||
|
Overwrites input iterator. Raises InputWriteError if input iterator is
|
||||||
|
not exhausted.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
next(self.input_iter)
|
||||||
|
raise InputWriteError
|
||||||
|
except StopIteration:
|
||||||
|
self.input_iter = input_iter
|
||||||
|
|
||||||
|
|
||||||
|
# Opcode functions -----------------------------
|
||||||
def _of_operator(operator):
|
def _of_operator(operator):
|
||||||
"""Make opcode from operator."""
|
"""Make opcode from operator."""
|
||||||
def opcode(self, p1, p2, p3):
|
def opcode(self, p1, p2, p3):
|
||||||
|
@ -47,6 +78,7 @@ class Emulator(object):
|
||||||
return opcode
|
return opcode
|
||||||
|
|
||||||
def _jump_when(flag):
|
def _jump_when(flag):
|
||||||
|
"""Returns jump when flag opcode"""
|
||||||
def opcode(self, p1, p2):
|
def opcode(self, p1, p2):
|
||||||
if (self.memory[p1] > 0) == flag:
|
if (self.memory[p1] > 0) == flag:
|
||||||
self.i = self.memory[p2]
|
self.i = self.memory[p2]
|
||||||
|
@ -55,18 +87,25 @@ class Emulator(object):
|
||||||
return opcode
|
return opcode
|
||||||
|
|
||||||
def _get_input(self, p1):
|
def _get_input(self, p1):
|
||||||
x = self.in_f()
|
"""Calls next on self.input_iterator. Raises WaitForInput if exhausted"""
|
||||||
|
try:
|
||||||
|
x = next(self.input_iter)
|
||||||
|
except StopIteration:
|
||||||
|
raise WaitForInput
|
||||||
self.memory[p1] = x
|
self.memory[p1] = x
|
||||||
self.i += 2
|
self.i += 2
|
||||||
|
|
||||||
def _put_output(self, p1):
|
def _put_output(self, p1):
|
||||||
|
"""Returns output. Only opcode that doesn't return None"""
|
||||||
self.i += 2
|
self.i += 2
|
||||||
self.out_f(self.memory[p1])
|
return self.memory[p1]
|
||||||
|
|
||||||
def _adjust_base(self, p1):
|
def _adjust_base(self, p1):
|
||||||
|
"""Adjusts relative base."""
|
||||||
self.rel_base += self.memory[p1]
|
self.rel_base += self.memory[p1]
|
||||||
self.i += 2
|
self.i += 2
|
||||||
|
|
||||||
|
# Opcode codes ------------------------------
|
||||||
opcodes = {1 : _of_operator(operator.add),
|
opcodes = {1 : _of_operator(operator.add),
|
||||||
2 : _of_operator(operator.mul),
|
2 : _of_operator(operator.mul),
|
||||||
3 : _get_input,
|
3 : _get_input,
|
||||||
|
@ -78,13 +117,19 @@ class Emulator(object):
|
||||||
9 : _adjust_base,
|
9 : _adjust_base,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __next__(self):
|
HALT = 99
|
||||||
state = self
|
|
||||||
op = str(self.memory[state.i])
|
|
||||||
|
|
||||||
if op == '99':
|
|
||||||
raise StopIteration
|
|
||||||
|
|
||||||
|
def next_opcode(self):
|
||||||
|
"""
|
||||||
|
Calls next opcode and returns it's return value. Raises Halted
|
||||||
|
when program halts
|
||||||
|
"""
|
||||||
|
op = self.memory[self.i]
|
||||||
|
if op == self.HALT:
|
||||||
|
raise Halted
|
||||||
|
|
||||||
|
op = str(op)
|
||||||
par_modes, op = op[:-2][::-1], int(op[-2:])
|
par_modes, op = op[:-2][::-1], int(op[-2:])
|
||||||
|
|
||||||
opcode = self.opcodes[op]
|
opcode = self.opcodes[op]
|
||||||
|
@ -94,170 +139,60 @@ class Emulator(object):
|
||||||
|
|
||||||
pars = []
|
pars = []
|
||||||
for pn, mode in enumerate(par_modes, start=1):
|
for pn, mode in enumerate(par_modes, start=1):
|
||||||
p = state.i + pn
|
p = self.i + pn
|
||||||
if mode == 0:
|
if mode == 0:
|
||||||
p = state.memory[p]
|
p = self.memory[p]
|
||||||
elif mode == 2:
|
elif mode == 2:
|
||||||
p = state.rel_base + state.memory[p]
|
p = self.rel_base + self.memory[p]
|
||||||
pars.append(p)
|
pars.append(p)
|
||||||
opcode(state, *pars)
|
return opcode(self, *pars)
|
||||||
|
|
||||||
def __iter__(self):
|
def __next__(self):
|
||||||
return self
|
"""Executes until ouput is returned. Stops iteration when halted."""
|
||||||
|
|
||||||
def run(self):
|
|
||||||
if self.memory[self.i] == 99:
|
|
||||||
raise Halted
|
|
||||||
return deque(self, maxlen=0)
|
|
||||||
|
|
||||||
|
|
||||||
class Singleton(object):
|
|
||||||
def __init__(self, x=None):
|
|
||||||
self.x = x
|
|
||||||
|
|
||||||
def append(self, y):
|
|
||||||
self.x = y
|
|
||||||
|
|
||||||
def pop(self, _):
|
|
||||||
x = self.x
|
|
||||||
if x == None:
|
|
||||||
raise IndexError("pop from empty singleton")
|
|
||||||
self.x = None
|
|
||||||
return x
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self.x == other
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str(self.x)
|
|
||||||
|
|
||||||
def format(self):
|
|
||||||
if self.x == None:
|
|
||||||
return None
|
|
||||||
return int(self.x)
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def makeIO(in_buff_class, out_buff_class):
|
|
||||||
class IO(object):
|
|
||||||
def __init__(self, in_buff = in_buff_class()):
|
|
||||||
self.in_buff = in_buff
|
|
||||||
self.out_buff = out_buff_class()
|
|
||||||
|
|
||||||
def pop_input(self):
|
|
||||||
if self.in_buff == in_buff_class():
|
|
||||||
raise WaitForInput
|
|
||||||
return self.in_buff.pop(0)
|
|
||||||
|
|
||||||
def append_output(self, x):
|
|
||||||
self.out_buff.append(x)
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
new = IO()
|
|
||||||
new.in_buff = self.in_buff.copy()
|
|
||||||
new.out_buff = self.out_buff.copy()
|
|
||||||
return new
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
o = self.out_buff
|
|
||||||
self.out_buff = out_buff_class()
|
|
||||||
try:
|
|
||||||
return o.format()
|
|
||||||
except AttributeError:
|
|
||||||
return o
|
|
||||||
|
|
||||||
def write(self, input):
|
|
||||||
if self.in_buff != in_buff_class():
|
|
||||||
raise IntcodeError("writing to nonempty input")
|
|
||||||
self.in_buff = in_buff_class(input)
|
|
||||||
|
|
||||||
return IO
|
|
||||||
|
|
||||||
StackIO = makeIO(list, list)
|
|
||||||
SingletonIO = makeIO(Singleton, Singleton)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Interpreter(object):
|
|
||||||
def __init__(self, program, IO_class=StackIO):
|
|
||||||
self.IO = IO_class()
|
|
||||||
self.comp = Emulator(program, self.IO.pop_input, self.IO.append_output)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
self.comp.run()
|
|
||||||
break
|
|
||||||
except WaitForInput:
|
|
||||||
yield self.IO.flush()
|
|
||||||
yield self.IO.flush()
|
|
||||||
|
|
||||||
def write(self, in_buff):
|
|
||||||
self.IO.write(in_buff)
|
|
||||||
|
|
||||||
def run(self, in_buff=None):
|
|
||||||
if in_buff != None:
|
|
||||||
self.write(in_buff)
|
|
||||||
self.comp.run()
|
|
||||||
return self.IO.flush()
|
|
||||||
|
|
||||||
def eval(self, in_buff):
|
|
||||||
self.write(in_buff)
|
|
||||||
try:
|
try:
|
||||||
self.comp.run()
|
r = self.next_opcode()
|
||||||
except WaitForInput:
|
while r == None:
|
||||||
pass
|
r = self.next_opcode()
|
||||||
return self.IO.flush()
|
return r
|
||||||
|
except Halted:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Return iterator"""
|
||||||
|
return self
|
||||||
|
|
||||||
def pipe_from(self, other):
|
def pipe_from(self, other):
|
||||||
class Output(Exception):
|
"""Lazy pipe. Chains other to selfs input_iter."""
|
||||||
pass
|
def handle():
|
||||||
def raise_output(o):
|
|
||||||
raise Output(o)
|
|
||||||
other.comp.out_f = raise_output
|
|
||||||
def get_input():
|
|
||||||
try:
|
try:
|
||||||
other.run()
|
return next(other)
|
||||||
|
except StopIteration:
|
||||||
raise PipeError
|
raise PipeError
|
||||||
except Output as o:
|
self.append_input(pop_input = handle)
|
||||||
return int(str(o))
|
self.write_input = other.write_input
|
||||||
self.comp.in_f = get_input
|
self.append_input = other.append_input
|
||||||
self.IO.write = other.IO.write
|
|
||||||
|
def __add__(self, other):
|
||||||
|
"""Pipes self to other and return other."""
|
||||||
|
other.pipe_from(self)
|
||||||
|
return other
|
||||||
|
|
||||||
|
# Useful functions --------------------------------------------------
|
||||||
|
|
||||||
|
def parse(s):
|
||||||
|
"""Parses program from string"""
|
||||||
|
return list(map(int, s.rstrip().split(',')))
|
||||||
|
|
||||||
|
|
||||||
def copy(self):
|
def compile(program):
|
||||||
memory = self.memory.copy()
|
"""Binds program to emulator and returns it as a function taking input."""
|
||||||
IO = IO.copy()
|
def executable(*args, **kwargs):
|
||||||
return Emulator(memory, IO)
|
emul = emulator(program)
|
||||||
|
emul.write_input(*args, **kwargs)
|
||||||
|
return emul
|
||||||
|
return executable
|
||||||
|
|
||||||
|
def preproc(s):
|
||||||
|
"""Prepoccessing function used by most programs using intcode"""
|
||||||
|
return compile(parse(s))
|
||||||
|
|
||||||
class Display(defaultdict):
|
|
||||||
def __init__(self, encoding):
|
|
||||||
super(Display, self).__init__(int)
|
|
||||||
self.encoding = encoding
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
xs, ys = zip(*self.keys())
|
|
||||||
X, Y = max(xs)+1, max(ys)+1
|
|
||||||
return '\n'.join(''.join(self.encoding[self[(x, y)]]
|
|
||||||
for x in range(X))
|
|
||||||
for y in range(Y))
|
|
||||||
|
|
||||||
|
|
||||||
class DisplayIO(object):
|
|
||||||
def __init__(self, display):
|
|
||||||
self.display = display
|
|
||||||
self.block = tuple()
|
|
||||||
|
|
||||||
def input(self):
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def wait_for_input(self):
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def output(self, x):
|
|
||||||
self.block += (x,)
|
|
||||||
if len(self.block) == 3:
|
|
||||||
self.display[self.block[:2]] = self.block[2]
|
|
||||||
self.block = tuple()
|
|
||||||
|
|
24
lib.py
24
lib.py
|
@ -1,5 +1,29 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import itertools
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
# Iterator recipes ----------------------------------------
|
||||||
|
def last(iterator):
|
||||||
|
return deque(iterator, maxlen=1)[0]
|
||||||
|
|
||||||
|
class defaultlist(list):
|
||||||
|
"""Default list class. Allows writing and reading out of bounds."""
|
||||||
|
def __init__(self, lst, val_factory):
|
||||||
|
super().__init__(lst)
|
||||||
|
self.val_factory = val_factory
|
||||||
|
|
||||||
|
def __setitem__(self, i, x):
|
||||||
|
for _ in range((i - len(self) + 1)):
|
||||||
|
self.append(self.val_factory())
|
||||||
|
super().__setitem__(i, x)
|
||||||
|
|
||||||
|
def __getitem__(self, i):
|
||||||
|
if i >= len(self):
|
||||||
|
return self.val_factory()
|
||||||
|
return super().__getitem__(i)
|
||||||
|
|
||||||
|
|
||||||
def memoize(f):
|
def memoize(f):
|
||||||
cache = dict()
|
cache = dict()
|
||||||
def memf(*args):
|
def memf(*args):
|
||||||
|
|
Loading…
Reference in New Issue