Exemple #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
Exemple #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]
Exemple #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)
Exemple #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)
Exemple #5
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()
Exemple #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]