Пример #1
0
 def __init__(self, prog: list[int]) -> None:
     self.painted: set[Coord] = set()
     self.white: set[Coord] = set()
     self.pos: Coord = Coord.origin()
     self.direction: Direction = Direction.UP
     self.machine = Machine(prog)
     self.machine.pause_after_output = True
Пример #2
0
class Scaffolding:
    def __init__(self, code: list[int]) -> None:
        self.m = Machine(code)
        self.scaffold: set[Coord] = set()
        self.intersections: set[Coord] = set()
        self.robot_loc: Coord
        self.robot_dir: Direction
        self.max: Coord = Coord.origin()
        self._scan_image()
        self._walk_scaffold()

    def _scan_image(self) -> None:
        """Find the robot and scaffold locations from IntCode machine output."""
        self.m.run()
        image: str = "".join(chr(c) for c in self.m.output_vals)
        for row, line in enumerate(image.split("\n")):
            for col, char in enumerate(line):
                if char != ".":
                    self.scaffold.add(Coord(row, col))
                if (d := Direction.from_char(char)) is not None:
                    self.robot_loc = Coord(row, col)
                    self.robot_dir = d
                if col > self.max.y:
                    self.max = Coord(self.max.y, col)
            if row > self.max.x:
                self.max = Coord(row, self.max.y)
Пример #3
0
class Player:
    def __init__(self, code: list[int]) -> None:
        self.m = Machine(code)
        self.m.pause_after_output = True
        self.m.pause_before_input = True
        self.ball_position: int = 0
        self.paddle_position: int = 0
        self.animate: bool = False
        self.score: int = 0

    def _run(self, stdscr: Any) -> None:

        if self.animate:
            if curses.can_change_color():
                curses.init_color(0, 0, 0,
                                  0)  # Set background (coloru 0) to rgb black
            stdscr.clear()
            curses.curs_set(0)  # Hide the cursor

        while True:
            self.m.run()
            if self.m.halted:
                break
            elif len(self.m.output_vals) == 0:  # Paused for input
                if self.paddle_position < self.ball_position:
                    self.m.input_vals.append(1)
                elif self.paddle_position > self.ball_position:
                    self.m.input_vals.append(-1)
                else:
                    self.m.input_vals.append(0)
            elif len(self.m.output_vals) == 3:  # Output full
                x, y, tile_id = self.m.output_vals
                self.m.output_vals = []
                if (x, y) == (-1, 0):
                    self.score = tile_id
                else:
                    if self.animate:
                        stdscr.addstr(y, x, tile(tile_id))
                    if tile_id == 4:
                        self.ball_position = x
                        if self.animate:
                            stdscr.refresh()
                            curses.napms(50)
                            # stdscr.getkey()
                    elif tile_id == 3:
                        self.paddle_position = x
            else:  # Wait for more output
                pass

        if self.animate:
            stdscr.refresh()
            stdscr.getkey()

    def run(self) -> Player:
        if self.animate:
            curses.wrapper(self._run)
        else:
            self._run(None)
        return self
Пример #4
0
 def __init__(self, code: list[int]) -> None:
     self.m = Machine(code)
     self.m.pause_after_output = True
     self.m.pause_before_input = True
     self.ball_position: int = 0
     self.paddle_position: int = 0
     self.animate: bool = False
     self.score: int = 0
Пример #5
0
 def __init__(self, code: list[int], debug: bool = False) -> None:
     self.m = Machine(code)
     self.m.pause_after_output = True
     self.m.pause_before_input = True
     self.loc: Coord = Coord.origin()
     self.walls: set[Coord] = set()  # Locations we know are walls
     self.open: set[Coord] = {self.loc}  # Locations we know are space
     self.oxygen: Optional[Coord] = None  # Location of oxygen if known
     self.debug: bool = debug
Пример #6
0
 def __init__(self, code: list[int]) -> None:
     self.m = Machine(code)
     self.scaffold: set[Coord] = set()
     self.intersections: set[Coord] = set()
     self.robot_loc: Coord
     self.robot_dir: Direction
     self.max: Coord = Coord.origin()
     self._scan_image()
     self._walk_scaffold()
Пример #7
0
class Robot:
    def __init__(self, prog: list[int]) -> None:
        self.painted: set[Coord] = set()
        self.white: set[Coord] = set()
        self.pos: Coord = Coord.origin()
        self.direction: Direction = Direction.UP
        self.machine = Machine(prog)
        self.machine.pause_after_output = True

    def paint(self, colour: Colour) -> Robot:
        self.painted.add(self.pos)
        if colour == Colour.WHITE:
            self.white.add(self.pos)
        else:
            if self.pos in self.white:
                self.white.remove(self.pos)
        return self

    def run(self) -> Robot:
        while True:
            if self.machine.input_vals:
                raise RuntimeError("Expected empty input")
            self.machine.input_vals = [
                Colour.WHITE.value
                if self.pos in self.white else Colour.BLACK.value
            ]
            if self.machine.output_vals:
                raise RuntimeError("Expected empty outputs")
            while len(self.machine.output_vals) < 2:
                self.machine.run()
                if self.machine.halted:
                    return self
            colour, rot = self.machine.output_vals
            self.machine.output_vals = []
            self.paint(Colour(colour))
            self.direction = self.direction.rotate(rot)
            self.pos = self.pos.move(self.direction)

    def show(self) -> Robot:
        xs = [pt.x for pt in self.white]
        ys = [pt.y for pt in self.white]
        x_min = min(xs)
        x_max = max(xs)
        y_min = min(ys)
        y_max = max(ys)
        for y in range(y_max, y_min - 1, -1):
            for x in range(x_min, x_max + 1):
                print("#" if Coord(x, y) in self.white else " ", end="")
            print()
        return self
Пример #8
0
def run_phase_settings_with_feedback(
    code: list[int], phase_settings: PhaseSettings, print_instructions: bool = False
) -> int:
    """Run a series machines with feedback from input 0 with given phase settings.

    >>> run_phase_settings_with_feedback(test_code_4, [9, 8, 7, 6, 5])
    139629729
    >>> run_phase_settings_with_feedback(test_code_5, [9, 7, 8, 5, 6])
    18216
    """
    machines: list[Machine] = [Machine(code, [p]) for p in phase_settings]
    for m in machines:
        m.pause_after_output = True

    input_val: int = 0
    machine_index: int = 0

    while True:
        machines[machine_index].input_vals.append(input_val)
        machines[machine_index].run()
        if machines[machine_index].halted and machine_index == len(machines) - 1:
            return machines[machine_index].output_vals[-1]
        input_val = machines[machine_index].output_vals[-1]
        machine_index += 1
        if machine_index >= len(machines):
            machine_index = 0
Пример #9
0
def run_machine(
    code: list[int], phase: int, input_val: int, print_instructions: bool = False
) -> int:
    if print_instructions:
        print(f"Running machine with phase setting {phase} and input {input_val}")
    m: Machine = Machine(code, [phase, input_val])
    m.run(print_instructions)
    [output_value] = m.output_vals
    if print_instructions:
        print(f"Output {output_value}")
    return output_value
Пример #10
0
                pass

        if self.animate:
            stdscr.refresh()
            stdscr.getkey()

    def run(self) -> Player:
        if self.animate:
            curses.wrapper(self._run)
        else:
            self._run(None)
        return self


if __name__ == "__main__":
    # Turning this on, switches to curses animation (can't rely on redirecting stdin
    # as need to take key inputs)
    show_animation: bool = False
    if show_animation:
        with open("../aoc-data/input/2019_13.txt") as f:
            code: list[int] = [int(x) for x in f.read().split(",")]
    else:
        code = [int(x) for x in stdin.read().split(",")]
    blocks, _score = count_blocks(Machine(code).run().output_vals)
    print(blocks)
    code[0] = 2
    p = Player(code)
    p.animate = show_animation
    p.run()
    print(p.score)
Пример #11
0
class Controller:
    def __init__(self, code: list[int], debug: bool = False) -> None:
        self.m = Machine(code)
        self.m.pause_after_output = True
        self.m.pause_before_input = True
        self.loc: Coord = Coord.origin()
        self.walls: set[Coord] = set()  # Locations we know are walls
        self.open: set[Coord] = {self.loc}  # Locations we know are space
        self.oxygen: Optional[Coord] = None  # Location of oxygen if known
        self.debug: bool = debug

    def try_move(self, d: Direction) -> Response:
        """Try and move to an adjacent cell following the given direction."""
        self.m.input_vals = [d.value]
        self.m.run()
        return Response(self.m.output_vals.pop())

    def try_move_to_adj(self, c: Coord) -> Response:
        """Try and move to a given adjacent cell."""
        for d in Direction:
            if self.loc.move(d) == c:
                return self.try_move(d)
        raise RuntimeError("Adjacent move not found", self.loc, c)

    def _path_to(self, target: Coord) -> list[Coord]:
        """Generate a path to the given cell through open cells."""
        if self.debug:
            print("Routing from", self.loc, "to", target)
        # Flood fill with backlinks
        frontier: dict[Coord, Coord] = {self.loc: self.loc}
        visited: dict[Coord, Coord] = {}
        while target not in frontier:
            visited.update(frontier)
            new_frontier: dict[Coord, Coord] = {}
            for f in frontier.keys():
                for n in f.neighbours():
                    if n == target or (n in self.open and n not in visited):
                        new_frontier[n] = f
            frontier = new_frontier

        # Build back tracking path
        x = target
        path: list[Coord] = []
        while x != self.loc:
            path.append(x)
            if x in frontier:
                x = frontier[x]
            else:
                x = visited[x]
        if self.debug:
            print("Path", path)
        return path

    def route_to(self, target: Coord) -> Response:
        """Try and move to a given cell going through known open cells."""
        path = self._path_to(target)
        for x in reversed(path[1:]):
            if self.debug:
                print("Move to", x)
            response = self.try_move_to_adj(x)
            if response == Response.WALL:
                raise RuntimeError("Ran into wall during route")
            self.loc = x

        return self.try_move_to_adj(target)

    def steps_to_oxygen(self) -> int:
        """Find the number of steps from the origin to oxygen."""
        self.loc = Coord.origin()
        if self.oxygen is None:
            raise RuntimeError("No oxygen found")
        return len(self._path_to(self.oxygen))

    def oxygen_fill(self) -> int:
        """Find the number of minutes to fill all locations with oxygen."""
        if self.oxygen is None:
            raise RuntimeError("No oxygen found to fill from")
        filled: set[Coord] = {self.oxygen}
        minutes: int = 0
        while self.open - filled:
            additional: set[Coord] = set()
            for f in filled:
                for n in f.neighbours():
                    if n in self.open and n not in filled:
                        additional.add(n)
            filled |= additional
            minutes += 1
        return minutes

    def explore(self) -> Controller:
        """Explore the grid, completing the controllers knowledge of it."""
        # Frontier is all unknown locations adjacent to places we have visited
        frontier: set[Coord] = self.loc.neighbours()

        while frontier:
            if self.debug:
                print("Loc:", self.loc)
                print("Fr:", "  ".join([f"{c.x},{c.y}" for c in frontier]))
            target = self.loc.closest(frontier)
            frontier.discard(target)
            if self.debug:
                print("Going to", target)
            response = self.route_to(target)
            if response == Response.WALL:
                if self.debug:
                    print("Wall at", target)
                self.walls.add(target)
            else:
                if self.debug:
                    print("Open at", target)
                self.loc = target
                self.open.add(target)
                frontier |= target.neighbours() - (self.open | self.walls)
                if response == Response.MOVED_OXYGEN:
                    self.oxygen = target

        return self

    def show(self) -> Controller:
        xs = [w.x for w in self.walls]
        ys = [w.y for w in self.walls]
        x_min = min(xs)
        x_max = max(xs)
        y_min = min(ys)
        y_max = max(ys)
        for y in range(y_max, y_min - 1, -1):
            for x in range(x_min, x_max + 1):
                if Coord(x, y) == Coord.origin():
                    print("X", end="")
                elif Coord(x, y) == self.oxygen:
                    print("O", end="")
                elif Coord(x, y) in self.walls:
                    print("#", end="")
                elif Coord(x, y) in self.open:
                    print(".", end="")
                else:
                    print(" ", end="")
            print()
        return self