Example #1
0
 def run_and_return_grid(self, *, initial_color=0):
     cpu = Computer(self.program, [])
     grid = defaultdict(lambda: 0)
     location = complex(0, 0)
     direction = "U"
     grid[location] = initial_color
     # Input to program: 0 if over black (.) , 1 if over white (#)
     # Outputs two values: color to paint 0/black/. 1/white/#, then 0 = turn left, 1 = turn right
     while True:
         current_square = grid[location]
         cpu.add_input(current_square)
         cpu.execute()
         if cpu.state == "halted":
             break
         paint_color = cpu.pop_output()
         turn_dir = cpu.pop_output()
         grid[location] = paint_color
         if turn_dir == 1:
             direction = turn_right(direction)
         elif turn_dir == 0:
             direction = turn_left(direction)
         else:
             raise "Told to turn an invalid direction"
         location += COMPLEX_OF_DIR[direction]
     return grid
Example #2
0
    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]
Example #3
0
class RepairDroid:
    def __init__(self, program):
        self.program = program
        self.cpu = Computer(self.program, [])

    def input(self, command):
        self.cpu.add_input(command)
        self.cpu.execute()
        return self.cpu.pop_output()

    def get_state(self):
        return copy.copy(self.cpu.memory)

    def set_state(self, state):
        self.cpu.memory = copy.copy(state)
Example #4
0
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)
Example #5
0
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]
Example #6
0
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]