Example #1
0
class Droid:
    def __init__(self) -> None:
        with open_fixture("day15") as fp:
            data = decode(fp.readline())
        self.vm = IntcodeVM(data)
        self.grid = Grid()
        self.pos = Position(0, 0)
        self.oxygen_system_pos = Position(0, 0)

    @property
    def cell(self) -> Cell:
        return self.grid[self.pos]

    def move(self, direction: Direction) -> bool:
        self.vm.io_push(direction.value)
        while not self.vm.stdout:
            self.vm.step()
        status = Status(self.vm.io_pop())
        pos = self.pos.move(direction)
        self.grid.set_status(pos, status)
        if status.passable:
            self.pos = pos
            if status == Status.OXYGENATED:
                self.oxygen_system_pos = pos
            return True
        return False

    def explore(self) -> bool:
        # first explore unexplored cells
        for direction in Direction.all():
            npos = self.pos.move(direction)
            if npos not in self.grid:
                if self.move(direction):
                    return True

        # invariant: all neighbors are known

        # quit if home again
        if not self.pos:
            return False

        # move back towards home
        options = {}
        for direction in Direction.all():
            npos = self.pos.move(direction)
            cell = self.grid[npos]
            if cell.status == Status.OKAY:
                options[cell.distance] = direction
                continue
        assert options
        best = sorted(options.keys())[0]
        assert best < self.cell.distance
        direction = options[best]
        assert self.move(direction)
        return True
Example #2
0
class Robot:
    def __init__(self, program: Data) -> None:
        self.vm = IntcodeVM(program)
        self.grid = Grid()
        self.orientation = Orientation.UP
        self.position = (0, 0)

    def step(self) -> bool:
        # get current color
        self.vm.io_push(self.grid[self.position].value)

        # run the program until output is availble
        while len(self.vm.stdout) < 2:
            if not self.vm.step():
                return False

        # paint
        color = Color(self.vm.io_pop())
        self.grid[self.position] = color

        # turn
        direction = self.vm.io_pop()
        if direction:
            self.orientation = self.orientation.turn_right()
        else:
            self.orientation = self.orientation.turn_left()

        # move
        if self.orientation == Orientation.UP:
            self.position = (self.position[0], self.position[1] - 1)
        if self.orientation == Orientation.RIGHT:
            self.position = (self.position[0] + 1, self.position[1])
        if self.orientation == Orientation.DOWN:
            self.position = (self.position[0], self.position[1] + 1)
        if self.orientation == Orientation.LEFT:
            self.position = (self.position[0] - 1, self.position[1])

        return not self.vm.halted

    def run(self) -> None:
        while self.step():
            pass
Example #3
0
class ArcadeCabinet:
    def __init__(self, data: Data) -> None:
        self.vm = IntcodeVM(data, trap_input=self._trap_input)
        self.score = 0
        self.frame = 0
        self.init_block_count = 0
        self.last_good_frame = 0
        self.last_good_state = data.copy()
        self.last_miss_x = 0
        self.last_paddle_x = 0
        self.failed = False
        self.moves: Optional[List[int]] = None
        self.stdscr: Any = None

    def step(self) -> bool:
        if not self.vm.step():
            return False
        if len(self.vm.stdout) == 3:
            self._trap_draw_tile()
        return True

    def _trap_draw_tile(self) -> None:
        x = self.vm.io_pop()
        y = self.vm.io_pop()
        if x == -1 and y == 0:
            # update score
            self.score = self.vm.io_pop()
            return

        # paint a tile
        tile = Tile(self.vm.io_pop())
        if tile == Tile.BLOCK:
            # track count for part 1
            self.init_block_count += 1

        if tile == Tile.PADDLE:
            self.last_paddle_x = x

        if tile == Tile.BALL:
            # did the ball hit the paddle
            if y == 20 and x in [
                    self.last_paddle_x - 1,
                    self.last_paddle_x,
                    self.last_paddle_x + 1,
            ]:
                self.last_good_frame = self.frame
                self.last_good_state = self.vm.data.copy()

            # did the ball go past the paddle
            if y == 21:
                self.failed = True
                self.last_miss_x = x

        # update curses window
        if self.stdscr is None:
            return
        self.stdscr.addstr(y, x, str(tile))

    def _trap_input(self) -> int:
        """
        Called whenever the vm is starved for input, between each iteration of the program state.
        """
        self.frame += 1

        v = 0  # neutral paddle movement
        if self.moves:
            v = self.moves[0]
            self.moves = self.moves[1:]

        stdscr = self.stdscr
        if stdscr is None:
            return v

        # paint curses window
        stdscr.addstr(24, 0, f"Score: {self.score} Frame: {self.frame}")
        stdscr.refresh()
        sleep(0.01)
        return v

    def run(self, stdscr=None, moves: Optional[List[int]] = None) -> bool:
        # if stdscr:
        self.stdscr = stdscr
        self.moves = moves
        while not self.vm.halted:
            self.step()
        return not self.failed
Example #4
0
class Ascii:
    def __init__(
        self,
        wake_up: bool = False,
    ):
        with open_fixture("day17") as fp:
            data = decode(fp.readline())
        self.vm = IntcodeVM(data)
        if wake_up:
            self.vm.data[0] = 2

    def putc(self, c: int) -> None:
        """
        Block until the character is written to the vm input buffer.
        """
        assert not self.vm.stdin
        assert not self.vm.stdout
        self.vm.io_push(c)
        while self.vm.step() and self.vm.stdin:
            assert not self.vm.stdout

    def write(self, s: str) -> None:
        """
        Block until the whole string is written to the vm input buffer.
        """
        for c in s:
            self.putc(ord(c))

    def getc(self) -> str:
        """
        Block until a character is read from the vm output buffer.
        """
        assert not self.vm.stdin
        assert not self.vm.stdout
        while self.vm.step():
            if self.vm.stdout:
                return chr(self.vm.io_pop())
        return ""

    def readline(self) -> str:
        """
        Block until a line is read from the vm output buffer.
        """
        with StringIO() as s:
            while True:
                c = self.getc()
                if not c:
                    break
                s.write(c)
                if c == "\n":
                    break
            return s.getvalue()

    def readgrid(self) -> str:
        """
        Block until a whole grid is read from the vm output buffer.
        """
        with StringIO() as s:
            for _ in range((GRID_WIDTH + 1) * GRID_HEIGHT):
                c = self.getc()
                s.write(c)
                if not c:
                    break
            assert not self.vm.stdout
            return s.getvalue()

    def get_dust_count(self) -> int:
        """
        Run the program to completion and return the sum of dust collected.
        """
        while self.vm.step():
            continue
        return self.vm.stdout[len(self.vm.stdout) - 1]