def part2(self): prog = self.create_program() # Load prog into computer, execute cpu = Computer(self.program, []) cpu.memory[0] = 2 for instruction in prog: cpu.add_input(instruction) cpu.execute() result = [] while cpu.has_output(): result.append(cpu.pop_output()) return result[-1]
class Day21: def __init__(self, program): self.program = program self.cpu = Computer(self.program, []) def reset(self): self.cpu = Computer(self.program, []) def execute(self): self.cpu.execute() result = [] while self.cpu.has_output(): result.append(self.cpu.pop_output()) self.display(result) def execute_silent(self): self.cpu.execute() result = [] while self.cpu.has_output(): result.append(self.cpu.pop_output()) return result def display(self, result): print("\n") for char in result: if char < 255: print(chr(char), end="") else: print(char) def send(self, string): prog = self.prog_to_ascii(string) for instruction in prog: self.cpu.add_input(instruction) def prog_to_ascii(self, string): return [ord(s) for s in string] def run_springscript_interactive(self, prog_string_array): """ Runs springscript and prints to console. """ self.reset() self.execute() self.send("\n".join(prog_string_array) + "\n") self.execute() def run_springscript_headless(self, prog_string_array): """ Runs springscript and returns the last output. """ self.reset() self.execute_silent() self.send("\n".join(prog_string_array) + "\n") output = self.execute_silent() return output[-1] def part1(self): # This program was derived manually (as intended, I suspect) # Jump if (NOT A1) OR (NOT C AND D) part1_prog = ["NOT C J", "AND D J", "NOT A T", "OR T J", "WALK"] # self.run_springscript_interactive(part1_prog) return self.run_springscript_headless(part1_prog) def part2(self): # This program was derived manually (as intended, I suspect) part2_prog = [ # (J) Jump if (C3) is missing and (D4) is filled # - But not if E(5) and H8 are missing # 1. Fill J with E5 present or H5 present "NOT E J", "NOT J J", "OR H J", # 2. Fill T with C3 missing and D4 filled "NOT C T", "AND D T", # 3. Move T to J somehow "AND T J", # Also jump if A(1) is missing "NOT A T", "OR T J", # Also jump if A(2) is missing and D4 is filled (Probably need to add somethign here) "NOT B T", "AND D T", "OR T J", "RUN", ] # self.run_springscript_interactive(part2_prog) return self.run_springscript_headless(part2_prog)
class Day17Droid: def __init__(self, program): self.program = program self.cpu = Computer(self.program, []) self.grid = defaultdict(lambda: 0) self.load_pic() def load_pic(self): result = self.execute() location = complex(0, 0) for num in result: if num == 10: new_imag = int(location.imag + 1) location = complex(0, new_imag) else: char = chr(num) self.grid[location] = char location += complex(1, 0) def execute(self): self.cpu.execute() result = [] while self.cpu.has_output(): result.append(self.cpu.pop_output()) return result def display(self): reals = [c.real for c in self.grid.keys() if self.grid[c] != 0] imags = [c.imag for c in self.grid.keys() if self.grid[c] != 0] system("clear") for y in range(int(min(imags)) - 2, int(max(imags)) + 3): for x in range(int(min(reals)) - 2, int(max(reals)) + 3): char = self.grid[complex(x, y)] print(char, end="") print("") def trace_path(self): # print("Trace") location, direct = self.robot_location() steps = [] steps_taken = 0 while True: if self.grid[location + direct] != "#": # print(f"Need to turn {x} {y}") if self.grid[location + turn_right(direct)] == "#": steps.append(steps_taken) steps_taken = 0 steps.append("R") direct = turn_right(direct) elif self.grid[location + turn_left(direct)] == "#": steps.append(steps_taken) steps_taken = 0 steps.append("L") direct = turn_left(direct) else: steps.append(steps_taken) # print("Done!") break else: location += direct steps_taken += 1 # Drop first 0 steps.pop(0) steps2 = [] while len(steps) > 0: turn = str(steps.pop(0)) how_far = str(steps.pop(0)) steps2.append(turn + how_far) return steps2 # Is there a better way? def is_sublist(self, needle, haystack): return self.to_str_comma(needle) in self.to_str_comma(haystack) def to_str_comma(self, a): return ",".join([str(x) for x in a]) def create_program(self): trace = self.trace_path() # print("==========") # print(trace) # print("==========") patterns = {} pattern_names = ["A", "B", "C"] for n in pattern_names: patterns[n] = [] for i, item in enumerate(trace): if item in pattern_names: if len(patterns[n]) > 0: break continue patterns[n].append(item) pattern_length = sum(len(x) + 2 for x in patterns[n]) - 1 if pattern_length > 20 or not self.is_sublist( patterns[n], trace[i + 1:]): patterns[n].pop() break p_str = self.to_str_comma(patterns[n]) trace_str = self.to_str_comma(trace) trace = trace_str.replace(p_str, n).split(",") prog = ",".join(trace) + "\n" for p in patterns.values(): prog += ",".join(p).replace("R", "R,").replace("L", "L,") + "\n" everything = self.prog_to_ascii(prog) + self.prog_to_ascii("n\n") return everything def prog_to_ascii(self, string): return [ord(s) for s in string] def robot_location(self): location = complex(-1, -1) robot_char = "" for x, y in gen_coords(self.grid): char = self.grid[complex(x, y)] if char == "^" or char == "v" or char == "<" or char == ">": location = complex(x, y) robot_char = char break return location, COMPLEX_OF_ROBOTCHAR[robot_char] def part1(self): intersections = 0 score = 0 for x, y in gen_coords(self.grid): char = self.grid[complex(x, y)] if char != "#": continue char_n = self.grid[complex(x, y - 1)] char_s = self.grid[complex(x, y + 1)] char_w = self.grid[complex(x - 1, y)] char_e = self.grid[complex(x + 1, y)] if char_n == "#" and char_s == "#" and char_w == "#" and char_e == "#": intersections += 1 score += x * y return score def part2(self): prog = self.create_program() # Load prog into computer, execute cpu = Computer(self.program, []) cpu.memory[0] = 2 for instruction in prog: cpu.add_input(instruction) cpu.execute() result = [] while cpu.has_output(): result.append(cpu.pop_output()) return result[-1]
class Day25: def __init__(self, program): self.program = program self.cpu = Computer(self.program, []) def get_state(self): return copy.copy(self.cpu.memory) def set_state(self, state): self.cpu.memory = copy.copy(state) def execute_silent(self): """ Execute CPU and return results as array. """ self.cpu.execute() result = [] while self.cpu.has_output(): result.append(self.cpu.pop_output()) return result def execute_str(self): """ Execute CPU and return results as str. """ result = self.execute_silent() return self.num_to_str(result) def send_msg(self, string): print(f"> {string}") nums = self.string_to_nums(string + "\n") for i in nums: self.cpu.add_input(i) def opposite_dir(self, direction): opposites = {"west": "east", "east": "west", "south": "north", "north": "south"} if direction in opposites: return opposites[direction] raise ValueError(f"Don't know opposite of [{direction}]") def explore(self): self.cpu = Computer(self.program, []) self.loc = None # visited[ 'Hull Breach'] = true # dir_from_to[ ('Hull Breach', 'Holodeck') ] = 'north' # Possibly Delete: dirs_for_loc['Hull Breach'] = ['north', 'west', 'south'] # state_for_loc['Hull Breach'] = (memory state for int comp) # loc_of_item['tambourine'] = 'Holodeck' # G = (networkx graph) self.visited = {} self.dir_from_to = {} self.dirs_for_loc = {} self.state_for_loc = {} self.loc_of_item = {} self.G = nx.Graph() self.explore_dfs(None, None) def explore_dfs(self, command_used, came_from): # Execute, read text and get room name message = self.execute_str() room_name = self.parse_title(message) loc = room_name self.loc = loc print(f"=== ExploreDFS [{loc}] ===") # Save the way we got here if command_used is not None and came_from is not None: self.G.add_edge(came_from, loc) self.dir_from_to[(came_from, loc)] = command_used self.dir_from_to[(loc, came_from)] = self.opposite_dir(command_used) # Been here before? if loc in self.visited: return # Mark as visited and save state self.visited[loc] = True self.state_for_loc[loc] = self.get_state() # Record items here for item in self.parse_items(message): self.loc_of_item[item] = loc directions = self.parse_directions(message) self.dirs_for_loc[loc] = directions for command in directions: # Load state self.set_state(self.state_for_loc[loc]) # Move and recurse self.send_msg(command) self.explore_dfs(command_used=command, came_from=loc) def pick_up_items(self): print("=== Picking up all items") # Reset computer self.cpu = Computer(self.program, []) message = self.execute_str() loc = self.parse_title(message) for item in self.loc_of_item: if self.is_blacklisted(item): continue destination = self.loc_of_item[item] self.move(loc, destination) loc = destination self.loc = loc self.send_msg(f"take {item}") message = self.execute_str() def move(self, loc, destination): path = nx.shortest_path(self.G, loc, destination) path.pop(0) # First item in path is always where we are while len(path) > 0: next_loc = path.pop(0) direction = self.dir_from_to[(loc, next_loc)] self.send_msg(direction) message = self.execute_str() room_name = self.parse_title(message) assert room_name == next_loc loc = next_loc self.loc = loc def try_all_items(self): print("=== Going to Security Checkpoint") destination = "Security Checkpoint" self.move(self.loc, destination) items = self.get_items() for item in items: self.send_msg(f"drop {item}") for n in range(len(items)): for these_items in list(combinations(items, n)): for item in these_items: self.send_msg(f"take {item}") self.send_msg("south") message = self.execute_str() if self.cpu.is_halted(): print("") print(f"Correct combination: {these_items}") print("") print(message) return for item in these_items: self.send_msg(f"drop {item}") def get_items(self): self.send_msg("inv") message = self.execute_str() items = self.parse_list(message) return items def is_blacklisted(self, item): return item in [ "infinite loop", "escape pod", "molten lava", "giant electromagnet", "photons", ] # in: message # out: ["north", "east", "west"] def parse_directions(self, message): dirs = [] if re.search(r"- east\n", message): dirs.append("east") if re.search(r"- north\n", message): dirs.append("north") if re.search(r"- south\n", message): dirs.append("south") if re.search(r"- west\n", message): dirs.append("west") return dirs # in: message # out: [] or ['tambourine'] def parse_list(self, message): return re.findall(r"- (.*?)(?:\n|$)", message) def parse_items(self, message): item_list = re.findall("Items (?:here|in your inventory):\n(.*)\n\n", message) if len(item_list) > 0: return self.parse_list(item_list[0]) return [] # in: message # out: "Hull Breach" def parse_title(self, message): titles = re.findall(f"== (.*?) ==", message) if len(titles) < 1: raise ValueError("Couldn't find title of this room") return titles[0] def part1(self): while True: print(self.execute_str()) ins = input( "command (w, n, e, s, take item, drop item, or 'solve' to automatically solve)> " ) if ins == "w": ins = "west" elif ins == "e": ins = "east" elif ins == "n": ins = "north" elif ins == "s": ins = "south" elif ins == "solve": self.explore() self.pick_up_items() self.try_all_items() return self.send_msg(ins) return None def num_to_str(self, results): return "".join([chr(s) for s in results]) def string_to_nums(self, string): return [ord(s) for s in string]