25.py
1 from lib import * 2 3 input = read_input(2019, 25) 4 5 6 class IntCode: 7 def __init__(self, mem): 8 self.mem = {i: e for i, e in enumerate(mem)} 9 self.pc = 0 10 self.running = False 11 self.inp = [] 12 self.out = [] 13 self.rel = 0 14 15 def start(self): 16 self.running = True 17 self.cont() 18 19 def get_arg(self, i): 20 mode = self.mem[self.pc] // (10 ** (i + 1)) % 10 21 out = self.mem[self.pc + i] 22 if mode == 1: 23 return out 24 elif mode == 2: 25 out += self.rel 26 return self.mem.get(out, 0) 27 28 def write_arg(self, i, value): 29 mode = self.mem[self.pc] // (10 ** (i + 1)) % 10 30 pos = self.mem[self.pc + i] 31 assert mode != 1 32 if mode == 2: 33 pos += self.rel 34 self.mem[pos] = value 35 36 def cont(self): 37 while True: 38 opcode = self.mem[self.pc] % 100 39 40 if opcode == 1: 41 self.write_arg(3, self.get_arg(1) + self.get_arg(2)) 42 self.pc += 4 43 elif opcode == 2: 44 self.write_arg(3, self.get_arg(1) * self.get_arg(2)) 45 self.pc += 4 46 elif opcode == 3: 47 if not self.inp: 48 return 49 self.write_arg(1, self.inp.pop(0)) 50 self.pc += 2 51 elif opcode == 4: 52 self.out.append(self.get_arg(1)) 53 self.pc += 2 54 elif opcode == 5: 55 if self.get_arg(1): 56 self.pc = self.get_arg(2) 57 else: 58 self.pc += 3 59 elif opcode == 6: 60 if not self.get_arg(1): 61 self.pc = self.get_arg(2) 62 else: 63 self.pc += 3 64 elif opcode == 7: 65 self.write_arg(3, int(self.get_arg(1) < self.get_arg(2))) 66 self.pc += 4 67 elif opcode == 8: 68 self.write_arg(3, int(self.get_arg(1) == self.get_arg(2))) 69 self.pc += 4 70 elif opcode == 9: 71 self.rel += self.get_arg(1) 72 self.pc += 2 73 elif opcode == 99: 74 self.running = False 75 return 76 77 def output(self): 78 out = "".join(map(chr, self.out)) 79 self.out.clear() 80 return out 81 82 def input(self, inp): 83 self.inp += map(ord, inp + "\n") 84 self.cont() 85 86 87 class Bot(IntCode): 88 def __init__(self, mem): 89 super().__init__(mem) 90 91 self.directions = [] 92 self.items = [] 93 self.room = None 94 95 def start(self): 96 super().start() 97 98 self.parse_new_room() 99 100 def parse_new_room(self): 101 out = self.output().split("\n") 102 for line in out: 103 match = re.match(r"^== (.+) ==$", line) 104 if match: 105 self.room = match.group(1) 106 break 107 out = out[out.index("Doors here lead:") + 1 :] 108 self.directions = [d[2:] for d in out[: out.index("")]] 109 if "Items here:" not in out: 110 self.items.clear() 111 return 112 out = out[out.index("Items here:") + 1 :] 113 self.items = [i[2:] for i in out[: out.index("")]] 114 115 def move(self, direction): 116 self.input(direction) 117 self.parse_new_room() 118 119 def take(self, item): 120 self.input("take " + item) 121 self.out.clear() 122 123 def drop(self, item): 124 self.input("drop " + item) 125 self.out.clear() 126 127 def inv(self): 128 self.input("inv") 129 out = self.output().split("\n") 130 if "Items in your inventory:" not in out: 131 return [] 132 out = out[out.index("Items in your inventory:") + 1 :] 133 return [i[2:] for i in out[: out.index("")]] 134 135 def test(self, direction): 136 assert self.room == "Security Checkpoint" 137 self.input(direction) 138 139 orig = out = self.output().split("\n") 140 out = out[out.index("Doors here lead:") + 1 :] 141 out = out[out.index("") + 1 :] 142 res = "ejected back" not in out[0] 143 if res: 144 print(re.match(r"^.*?(\d+).*$", out[2]).group(1)) 145 return res 146 147 148 BACK = {"north": "south", "south": "north", "east": "west", "west": "east"} 149 150 (*mem,) = map(int, input.split(",")) 151 bot = Bot(mem) 152 bot.start() 153 154 items = [] 155 maze = {} 156 157 158 def explore(r=None, came_from=None, indent=0): 159 room = bot.room 160 if r: 161 maze.setdefault(r, {})[BACK[came_from]] = room 162 if room == "Security Checkpoint": 163 for direction in bot.directions: 164 if direction != came_from: 165 maze.setdefault(room, {})[direction] = "TEST" 166 return 167 for item in bot.items: 168 if item not in {"escape pod", "giant electromagnet", "molten lava", "photons", "infinite loop"}: 169 items.append(item) 170 bot.take(item) 171 172 for direction in bot.directions: 173 if came_from == direction: 174 continue 175 bot.move(direction) 176 explore(room, BACK[direction], indent + 1) 177 bot.move(BACK[direction]) 178 179 180 explore() 181 182 183 def make_path(room, dest, f=None): 184 if room == dest: 185 return [] 186 187 for d, r in maze.get(room, {}).items(): 188 if r == f: 189 continue 190 res = make_path(r, dest, room) 191 if res is not None: 192 return [d] + res 193 194 195 for d in make_path(bot.room, "Security Checkpoint"): 196 bot.move(d) 197 (dire,) = maze[bot.room] 198 199 200 def test_combination(i): 201 for e in bot.inv(): 202 bot.drop(e) 203 for j, e in enumerate(items): 204 if i & (1 << j): 205 bot.take(e) 206 return bot.test(dire) 207 208 209 for i in range(1 << len(items)): 210 if test_combination(i): 211 break