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))
def _format_move(turn: Player, move: Tuple[Union[Space, Tuple[Space, Space]], Direction], moves: int) -> str: """Formats a player's move as a string with a single line. Args: turn: The `Player` who performs the move move: The move as returned by `abalone.abstract_player.AbstractPlayer.turn` moves: The number of total moves made so far (not including this move) """ marbles = [move[0]] if isinstance(move[0], Space) else line_from_to( *move[0])[0] marbles = map(lambda space: space.name, marbles) return f'{moves + 1}: {turn.name} moves {", ".join(marbles)} in direction {move[1].name}'
def test_line_from_to(self): """Test `abalone.utils.line_from_to`""" self.assertTupleEqual( line_from_to(Space.A1, Space.D4), ([Space.A1, Space.B2, Space.C3, Space.D4], Direction.NORTH_EAST)) self.assertTupleEqual( line_from_to(Space.E6, Space.E9), ([Space.E6, Space.E7, Space.E8, Space.E9], Direction.EAST)) self.assertTupleEqual( line_from_to(Space.C7, Space.C4), ([Space.C7, Space.C6, Space.C5, Space.C4], Direction.WEST)) self.assertTupleEqual(line_from_to(Space.D2, Space.E3), ([Space.D2, Space.E3], Direction.NORTH_EAST)) self.assertEqual(line_from_to(Space.A1, Space.B4), (None, None)) self.assertEqual(line_from_to(Space.F2, Space.F2), (None, None)) self.assertRaises(Exception, lambda: line_from_to(Space.OFF, Space.A1)) self.assertRaises(Exception, lambda: line_from_to(Space.A1, Space.OFF)) self.assertRaises(Exception, lambda: line_from_to(Space.OFF, Space.OFF))