Exemplo n.º 1
0
class Breakout:
    def __init__(self, program_in):
        self.program = program_in
        self.cpu = Computer(self.program, [])
        self.grid = defaultdict(lambda: 0)

    def input(self, val):
        self.cpu.add_input(val)

    def add_quarters(self):
        self.cpu.memory[0] = 2

    def run_and_return_grid(self):
        cpu = self.cpu
        self.cpu.memory[0] = 2
        grid = self.grid

        cpu.execute()
        while cpu.has_output():
            x = cpu.pop_output()
            y = cpu.pop_output()
            what = cpu.pop_output()
            grid[complex(x, y)] = what

        return grid

    def is_halted(self):
        return self.cpu.is_halted()

    def display(self):
        system("clear")
        for y in range(25):
            for x in range(45):
                char = self.grid[complex(x, y)]
                print_char = " "
                if char == 1:
                    print_char = "W"
                elif char == 2:
                    print_char = "B"
                elif char == 3:
                    print_char = "="
                elif char == 4:
                    print_char = "*"
                print(print_char, end="")
            print("")
        print(f"Score {self.grid[complex(-1, 0)]}")

    def score(self):
        return self.grid[complex(-1, 0)]

    def get_move(self):
        grid = self.grid
        ball = list(grid.keys())[list(grid.values()).index(4)]
        ball_x = int(ball.real)
        paddle = list(grid.keys())[list(grid.values()).index(3)]
        paddle_x = int(paddle.real)
        if ball_x > paddle_x:
            return "r"
        if ball_x < paddle_x:
            return "l"
        return "."

    @staticmethod
    def part1(program_in):
        robot = Breakout(program_in)
        grid = robot.run_and_return_grid()
        return list(grid.values()).count(2)

    @staticmethod
    def part2(program_in, *, display_to_screen=False):
        robot = Breakout(program_in)
        grid = robot.run_and_return_grid()
        robot.add_quarters()
        if display_to_screen:
            robot.display()
        while True:
            a = robot.get_move()
            if a == "l":
                robot.input(-1)
            elif a == "r":
                robot.input(1)
            else:
                robot.input(0)
            if robot.is_halted():
                break
            grid = robot.run_and_return_grid()
            if display_to_screen:
                robot.display()
        return robot.score()
Exemplo n.º 2
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]