Beispiel #1
0
def parse_move_string(board, move_string) -> Move:
    if board is None:
        raise ValueError("board cannot be None")
    if move_string is None or move_string.isspace():
        raise ValueError("move_string cannot be None")

    move_string = move_string.strip()

    try:
        # Attempt to parse as an algebraic move
        return Move(move_string=move_string)
    except ValueError:
        pass

    move_string_parts = list(filter(None, move_string.split(' ')))
    moving_piece = EnumUtils.parse_short_name(move_string_parts[0])

    if board.board_state == "NotStarted":
        # First move is on the origin
        return Move(moving_piece, Position.origin)

    target_string = move_string_parts[1].replace('-', '').replace('/',
                                                                  '').replace(
                                                                      '\\', '')
    target_piece = EnumUtils.parse_short_name(target_string)
    separator_idx = index_of_any(move_string_parts[1], ['-', '/', '\\'])

    if separator_idx < 0:
        # Putting a piece on top of another
        return Move(moving_piece,
                    board.get_piece_position(target_piece).get_above())

    separator = move_string_parts[1][separator_idx]
    target_position = board.get_piece_position(target_piece)

    if separator_idx == 0:
        # Moving piece on the left-hand side of the target piece
        if separator == '-':
            return Move(moving_piece,
                        target_position.neighbour_at(directions["UpLeft"]))
        elif separator == '/':
            return Move(moving_piece,
                        target_position.neighbour_at(directions["DownLeft"]))
        elif separator == '\\':
            return Move(moving_piece,
                        target_position.neighbour_at(directions["Up"]))

    elif separator_idx == len(target_string):
        # Moving piece on the right-hand side of the target piece
        if separator == '-':
            return Move(moving_piece,
                        target_position.neighbour_at(directions["DownRight"]))
        elif separator == '/':
            return Move(moving_piece,
                        target_position.neighbour_at(directions["UpRight"]))
        elif separator == '\\':
            return Move(moving_piece,
                        target_position.neighbour_at(directions["Down"]))
    return None
Beispiel #2
0
    def _get_valid_placements(self, target_piece):
        valid_moves = MoveSet()
        target_colour = self.current_turn_colour

        if target_piece.colour != target_colour:
            return valid_moves

        if self._cached_valid_placement_positions is None or len(
                self._cached_valid_placement_positions) == 0:
            self._cached_valid_placement_positions = set()
            self._visited_placements.clear()

            for i in range(EnumUtils.num_piece_names):
                piece = self._pieces[i]

                valid_piece = piece is not None and piece.in_play

                # Piece is in play, on the top and is the right color, look through neighbors
                if valid_piece and self.piece_is_on_top(
                        piece) and piece.colour == target_colour:
                    bottom_position = self.get_piece_on_bottom(piece).position
                    self._visited_placements.add(bottom_position)

                    for j in range(EnumUtils.num_directions):
                        neighbor = bottom_position.neighbour_at(j)

                        # Neighboring position is a potential, verify its neighbors are empty or same color
                        old_len = len(self._visited_placements)
                        self._visited_placements.add(neighbor)

                        if len(self._visited_placements
                               ) > old_len and not self.has_piece_at(neighbor):
                            valid_placement = True
                            for k in range(EnumUtils.num_directions):
                                surrounding_position = neighbor.neighbour_at(k)
                                surrounding_piece = self.get_piece_on_top_internal(
                                    surrounding_position)

                                if surrounding_piece is not None and surrounding_piece.colour != target_colour:
                                    valid_placement = False
                                    break

                            if valid_placement:
                                self._cached_valid_placement_positions.add(
                                    neighbor)

            self.valid_move_cache_metrics_set["ValidPlacements"].miss()
        else:
            self.valid_move_cache_metrics_set["ValidPlacements"].hit()

        for valid_placement in self._cached_valid_placement_positions:
            valid_moves.add(
                Move(piece_name=target_piece.piece_name,
                     position=valid_placement))

        return valid_moves
Beispiel #3
0
    def __init__(self, size=None, move_set_string=None, moves_list=None):
        self.is_locked = None
        self._moves = []

        if moves_list:
            self._moves = [m for m in moves_list]
            return

        if not move_set_string:
            self._moves = [Move()] * size if size else []
            self.is_locked = False
            return

        if move_set_string.isspace():
            raise ValueError("Invalid move_set_string.")

        split = move_set_string.split(';')
        for s in split:
            parse_move = Move(move_string=s)
            self._moves.append(parse_move)
Beispiel #4
0
    def get_valid_beetle_movements(self, target_piece):
        valid_moves = MoveSet()

        # Look in all directions
        for direction in EnumUtils.directions.keys():
            new_position = target_piece.position.neighbour_at(direction)
            top_neighbor = self.get_piece_on_top_internal(new_position)

            # Get positions to left and right or direction we're heading
            left_of_target = EnumUtilsCls.left_of(direction)
            right_of_target = EnumUtilsCls.right_of(direction)
            left_neighbor_position = target_piece.position.neighbour_at(
                left_of_target)
            right_neighbor_position = target_piece.position.neighbour_at(
                right_of_target)

            top_left_neighbor = self.get_piece_on_top_internal(
                left_neighbor_position)
            top_right_neighbor = self.get_piece_on_top_internal(
                right_neighbor_position)

            # At least one neighbor is present
            current_height = target_piece.position.stack + 1
            destination_height = top_neighbor.position.stack + 1 if top_neighbor is not None else 0

            top_left_neighbor_height = top_left_neighbor.position.stack + 1 if top_left_neighbor is not None else 0
            top_right_neighbor_height = top_right_neighbor.position.stack + 1 if top_right_neighbor is not None else 0

            # "Take-off" beetle
            current_height -= 1

            same_tier = current_height == 0 and destination_height == 0
            go_down = destination_height < top_left_neighbor_height and destination_height < top_right_neighbor_height
            are_down = current_height < top_left_neighbor_height and current_height < top_right_neighbor_height

            if not (same_tier and top_left_neighbor_height == 0
                    and top_right_neighbor_height == 0):
                # Logic from http:#boardgamegeek.com/wiki/page/Hive_FAQ#toc9
                if not (go_down and are_down):
                    up_one_tier = new_position.stack == destination_height
                    target_position = new_position if up_one_tier else top_neighbor.position.get_above(
                    )
                    target_move = Move(piece_name=target_piece.piece_name,
                                       position=target_position)
                    valid_moves.add(target_move)

        return valid_moves
Beispiel #5
0
    def get_valid_slides_rec(self,
                             target,
                             current_pos,
                             visited_positions,
                             current_range,
                             valid_moves,
                             max_range=None):
        if max_range is None or current_range < max_range:

            # Optimize loop:
            neighbour_at = current_pos.neighbour_at
            right_of = EnumUtilsCls.right_of
            left_of = EnumUtilsCls.left_of
            has_piece_at = self.has_piece_at
            vm_add = valid_moves.add
            vp_add = visited_positions.add
            get_valid_slides_rec = self.get_valid_slides_rec

            for slide_direction in EnumUtils.directions:
                slide_position = neighbour_at(slide_direction)

                if slide_position not in visited_positions and not has_piece_at(
                        slide_position):
                    # Slide position is open
                    right = right_of(slide_direction)
                    left = left_of(slide_direction)

                    right_occupied = has_piece_at(neighbour_at(right))
                    left_occupied = has_piece_at(neighbour_at(left))

                    if right_occupied != left_occupied:  # Hive is not "tight"
                        # Can slide into slide position
                        move = Move(piece_name=target, position=slide_position)

                        old_len = valid_moves.count
                        vm_add(move)

                        if valid_moves.count > old_len:
                            # Sliding from this position has not been tested yet
                            vp_add(move.position)
                            get_valid_slides_rec(target, slide_position,
                                                 visited_positions,
                                                 current_range + 1,
                                                 valid_moves, max_range)
Beispiel #6
0
    def get_valid_grasshopper_movements(self, target_piece):
        valid_moves = MoveSet()
        starting_position = target_piece.position

        for direction in EnumUtils.directions:
            landing_position = starting_position.neighbour_at(direction)
            distance = 0

            while self.has_piece_at(landing_position):
                # Jump one more in the same direction
                landing_position = landing_position.neighbour_at(direction)
                distance += 1

            if distance > 0:
                # Can only move if there's at least one piece in the way
                move = Move(piece_name=target_piece.piece_name,
                            position=landing_position)
                valid_moves.add(move)

        return valid_moves
Beispiel #7
0
    def get_valid_moves_internal(self, target_piece):
        # Optimize:
        bug_type = target_piece.bug_type
        colour = target_piece.colour
        in_hand = target_piece.in_hand
        in_play = target_piece.in_play
        piece_name = target_piece.piece_name

        if target_piece is not None and self.game_in_progress:
            if colour == self.current_turn_colour and self.placing_piece_in_order(
                    target_piece):

                not_white_queen = in_hand and piece_name != "WhiteQueenBee"
                not_black_queen = in_hand and piece_name != "BlackQueenBee"
                not_last_moved = piece_name != self.last_piece_moved and in_play

                # Optimize:
                valid_moves = MoveSet()
                add = valid_moves.add
                neighbour_at = PositionCls.origin.neighbour_at
                origin = PositionCls.origin

                # First move must be at the origin and not the White Queen Bee
                if self.current_turn == 0 and colour == "White" and not_white_queen:
                    add(Move(piece_name=piece_name, position=origin))
                    return valid_moves

                # Second move must be around the origin and not the Black Queen Bee
                elif self.current_turn == 1 and colour == "Black" and not_black_queen:

                    for i in range(EnumUtils.num_directions):
                        neighbor = neighbour_at(i)
                        add(Move(piece_name=piece_name, position=neighbor))
                    return valid_moves

                elif (
                        in_hand and
                    (self.current_player_turn != 4 or  # Normal turn OR
                     (
                         self.current_player_turn == 4 and  # Turn 4 and AND
                         (
                             self.current_turn_queen_in_play
                             or  # Queen is in play or you're trying to play it
                             (not self.current_turn_queen_in_play
                              and target_piece.bug_type == "QueenBee"))))):
                    # Look for valid new placements
                    return self._get_valid_placements(target_piece)

                elif not_last_moved and self.current_turn_queen_in_play and self.piece_is_on_top(
                        target_piece):

                    if self.can_move_without_breaking_hive(target_piece):
                        # Look for basic valid moves of played pieces who can move
                        if bug_type == "QueenBee":
                            add(
                                self.get_valid_queen_bee_movements(
                                    target_piece))
                        elif bug_type == "Spider":
                            add(self.get_valid_spider_movements(target_piece))
                        elif bug_type == "Beetle":
                            add(self.get_valid_beetle_movements(target_piece))
                        elif bug_type == "Grasshopper":
                            add(
                                self.get_valid_grasshopper_movements(
                                    target_piece))
                        elif bug_type == "SoldierAnt":
                            add(
                                self.get_valid_soldier_ant_movements(
                                    target_piece))
                    return valid_moves
        return MoveSet()