diff --git a/day25.py b/day25.py new file mode 100644 index 0000000..dc01727 --- /dev/null +++ b/day25.py @@ -0,0 +1,120 @@ +from intcode import parse, emulator, REPL +import re + + +N = 'north' +S = 'south' +E = 'east' +W = 'west' + +back = {N : S, S : N, W : E, E : W} + +def add_vec(xy, d): + x, y = xy + if d == N: + return (x, y+1) + if d == S: + return (x, y-1) + if d == W: + return (x-1, y) + if d == E: + return (x+1, y) + +class Room: + def __init__(self, mssg, path): + blocks = mssg.strip().split("\n\n") + head_str = blocks[0] + door_str = blocks[1] + item_str = '\n'.join(blocks[2:]) + + self.name = re.findall('=+ ([^=]+) =+', head_str)[0] + self.description = re.findall(r'=\n([\s\S]+)', head_str)[-1].rstrip() + self.doors = re.findall(r'- (\w+)\n?', door_str) + self.items = re.findall(r'- (.+)\n?', item_str) + self.path = path + + def __eq__(self, other): + return self.name == other.name + + def __repr__(self): + head = "== {} ==\n{}".format(self.name, self.description) + doors = "Doors here lead:\n- {}".format("\n- ".join(self.doors)) + items = "" if self.items == [] else "\n\nItems here:\n- {}".format("\n- ".join(self.items)) + path = " -> ".join(self.path) + return "{}\n\n{}{}\n{}".format(head, doors, items, path) + + +def encode(s): + return map(ord, s + '\n') + +def decode(lst): + return ''.join(map(chr, lst)) + +def goto_room(repl, room): + for d in room.path: + repl.write(d) + next(repl) + +def try_item(repl, item): + if item in ("infinite loop", "giant electromagnet"): + return False + repl = repl.copy() + repl.write("take " + item) + next(repl) + try: + repl.write('') + next(repl) + return True + except StopIteration: + return False + +def map_ship(repl): + rooms = [] + items = [] + def aux(path): + s = next(repl) + try: + room = Room(s, path) + except: + return + if room in rooms: + return + rooms.append(room) + + for item in room.items: + if not try_item(repl, item): + continue + repl.write("take " + item) + next(repl) + items.append(item) + + for d in room.doors: + repl.write(d) + aux(path + [d]) + repl.write(back[d]) + next(repl) + aux([]) + return rooms, items + +def preproc(puzzle_input): + prog = parse(puzzle_input) + emul = emulator(prog) + repl = REPL(emul, decode=decode, encode=encode) + + rooms, items = map_ship(repl) + + checkpoint = list(filter(lambda room : room.name == "Security Checkpoint", rooms))[0] + goto_room(repl, checkpoint) + return repl + +def partI(repl): + for item in ["coin", "mutex", "astronaut ice cream", "dehydrated water"]: + repl.write("drop " + item) + next(repl) + repl.write("north") + s = next(repl) + return re.findall(r'\d+', s)[0] + +def partII(_): + return "No solution" + diff --git a/intcode.py b/intcode.py index a095547..0513aa4 100644 --- a/intcode.py +++ b/intcode.py @@ -44,7 +44,7 @@ class emulator(): 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.copy(), val_factory = lambda : 0) self.i = 0 self.rel_base = 0 @@ -177,6 +177,56 @@ class emulator(): other.pipe_from(self) return other + @format_input_args + def copy(self, input_iter_copy): + """ + Copies emulator instance. The input iterator must be copied and provided + by the caller. + """ + new = emulator(self.memory) + new.i = self.i + new.rel_base = self.rel_base + + new.input_iter = input_iter_copy + return new + +# Repl class ------------------------------------------------------- +class REPL: + """Read-eval-print loop class.""" + def __init__(self, emul, decode=list, encode=list): + """Takes an emulator and decode/enocde functions""" + self.emul = emul + self.encode = encode + self.decode = decode + + def __next__(self): + """ + Iterates over emulator output until WaitForInput is risen or the + emulator halts. Returns decoded output. + """ + out = [] + try: + done = True + for o in self.emul: + done = False + out.append(o) + if done: + raise StopIteration + except WaitForInput: + pass + return self.decode(out) + + def __iter__(self): + return self + + def write(self, i): + """Encodes input and writes it to emulator""" + self.emul.write_input(input_iter = self.encode(i)) + + def copy(self): + """Copies repl instance.""" + return REPL(self.emul.copy(), self.decode, self.encode) + # Useful functions -------------------------------------------------- def parse(s):