Ejemplo n.º 1
0
    def move_broadside(self, boundaries: Tuple[Space, Space],
                       direction: Direction) -> None:
        """Performs a broadside move. With a broadside move a line of adjacent marbles is moved sideways into empty\
        spaces. However, it is not possible to push the opponent's marbles. A broadside move is denoted by the two\
        outermost `abalone.enums.Space`s of the line to be moved and the `abalone.enums.Direction` of movement. With a\
        broadside move two or three marbles can be moved, i.e. the two boundary marbles are either direct neighbors or\
        there is exactly one marble in between.

        Args:
            boundaries: A tuple of the two outermost `abalone.enums.Space`s of a line of two or three marbles.
            direction: The `abalone.enums.Direction` of movement.

        Raises:
            IllegalMoveException: Elements of boundaries must not be `abalone.enums.Space.OFF`
            IllegalMoveException: Only two or three neighboring marbles may be moved with a broadside move
            IllegalMoveException: The direction of a broadside move must be sideways
            IllegalMoveException: Only own marbles may be moved
            IllegalMoveException: With a broadside move, marbles can only be moved to empty spaces
        """
        if boundaries[0] is Space.OFF or boundaries[1] is Space.OFF:
            raise IllegalMoveException(
                'Elements of boundaries must not be `Space.OFF`')
        marbles, direction1 = line_from_to(boundaries[0], boundaries[1])
        if marbles is None or not (len(marbles) == 2 or len(marbles) == 3):
            raise IllegalMoveException(
                'Only two or three neighboring marbles may be moved with a broadside move'
            )
        _, direction2 = line_from_to(boundaries[1], boundaries[0])
        if direction is direction1 or direction is direction2:
            raise IllegalMoveException(
                'The direction of a broadside move must be sideways')
        for marble in marbles:
            if self.get_marble(marble) is not _marble_of_player(self.turn):
                raise IllegalMoveException('Only own marbles may be moved')
            destination_space = neighbor(marble, direction)
            if destination_space is Space.OFF or self.get_marble(
                    destination_space) is not Marble.BLANK:
                raise IllegalMoveException(
                    'With a broadside move, marbles can only be moved to empty spaces'
                )
        for marble in marbles:
            self.set_marble(marble, Marble.BLANK)
            self.set_marble(neighbor(marble, direction),
                            _marble_of_player(self.turn))
Ejemplo n.º 2
0
    def generate_own_marble_lines(
            self) -> Generator[Union[Space, Tuple[Space, Space]], None, None]:
        """Generates all adjacent straight lines with up to three marbles of the player whose turn it is.

        Yields:
            Either one or two `abalone.enums.Space`s according to the first parameter of `abalone.game.Game.move`.
        """
        for space in Space:
            if space is Space.OFF or self.get_marble(
                    space) is not _marble_of_player(self.turn):
                continue
            yield space
            for direction in [
                    Direction.NORTH_WEST, Direction.NORTH_EAST, Direction.EAST
            ]:
                neighbor1 = neighbor(space, direction)
                if neighbor1 is not Space.OFF and self.get_marble(
                        neighbor1) is _marble_of_player(self.turn):
                    yield space, neighbor1
                    neighbor2 = neighbor(neighbor1, direction)
                    if neighbor2 is not Space.OFF and self.get_marble(
                            neighbor2) is _marble_of_player(self.turn):
                        yield space, neighbor2
Ejemplo n.º 3
0
    def move_inline(self, caboose: Space, direction: Direction) -> None:
        """Performs an inline move. An inline move is denoted by the trailing marble ("caboose") of a straight line of\
        marbles. Marbles of the opponent can only be pushed with an inline move (as opposed to a broadside move). This\
        is possible if the opponent's marbles are directly in front of the line of the player's own marbles, and only\
        if the opponent's marbles are outnumbered ("sumito") and are moved to an empty space or off the board.

        Args:
            caboose: The `abalone.enums.Space` of the trailing marble of a straight line of up to three marbles.
            direction: The `abalone.enums.Direction` of movement.

        Raises:
            IllegalMoveException: Only own marbles may be moved
            IllegalMoveException: Only lines of up to three marbles may be moved
            IllegalMoveException: Own marbles must not be moved off the board
            IllegalMoveException: Only lines that are shorter than the player's line can be pushed
            IllegalMoveException: Marbles must be pushed to an empty space or off the board
        """

        if self.get_marble(caboose) is not _marble_of_player(self.turn):
            raise IllegalMoveException('Only own marbles may be moved')

        line = line_to_edge(caboose, direction)
        own_marbles_num, opp_marbles_num = self._inline_marbles_nums(line)

        if own_marbles_num > 3:
            raise IllegalMoveException(
                'Only lines of up to three marbles may be moved')

        if own_marbles_num == len(line):
            raise IllegalMoveException(
                'Own marbles must not be moved off the board')

        # sumito
        if opp_marbles_num > 0:
            if opp_marbles_num >= own_marbles_num:
                raise IllegalMoveException(
                    'Only lines that are shorter than the player\'s line can be pushed'
                )
            push_to = neighbor(line[own_marbles_num + opp_marbles_num - 1],
                               direction)
            if push_to is not Space.OFF:
                if self.get_marble(push_to) is _marble_of_player(self.turn):
                    raise IllegalMoveException(
                        'Marbles must be pushed to an empty space or off the board'
                    )
                self.set_marble(push_to,
                                _marble_of_player(self.not_in_turn_player()))

        self.set_marble(line[own_marbles_num], _marble_of_player(self.turn))
        self.set_marble(caboose, Marble.BLANK)
Ejemplo n.º 4
0
 def test_neighbor(self):
     """Test `abalone.utils.neighbor`"""
     self.assertIs(neighbor(Space.OFF, Direction.NORTH_EAST), Space.OFF)
     self.assertIs(neighbor(Space.B2, Direction.NORTH_EAST), Space.C3)
     self.assertIs(neighbor(Space.B2, Direction.EAST), Space.B3)
     self.assertIs(neighbor(Space.B2, Direction.SOUTH_EAST), Space.A2)
     self.assertIs(neighbor(Space.B2, Direction.SOUTH_WEST), Space.A1)
     self.assertIs(neighbor(Space.B2, Direction.WEST), Space.B1)
     self.assertIs(neighbor(Space.B2, Direction.NORTH_WEST), Space.C2)
     self.assertIs(neighbor(Space.A1, Direction.NORTH_EAST), Space.B2)
     self.assertIs(neighbor(Space.A1, Direction.SOUTH_EAST), Space.OFF)
     self.assertIs(neighbor(Space.I5, Direction.NORTH_EAST), Space.OFF)
     self.assertIs(neighbor(Space.H4, Direction.NORTH_WEST), Space.OFF)
     self.assertIs(neighbor(Space.G3, Direction.WEST), Space.OFF)
     self.assertIs(neighbor(Space.A5, Direction.EAST), Space.OFF)
     self.assertIs(neighbor(Space.A1, Direction.WEST), Space.OFF)