コード例 #1
0
ファイル: game.py プロジェクト: thomasheyenbrock/chess-ai
    def is_dead(self) -> bool:
        white_queens = list(split(self.pieces["Q"]))
        white_rooks = list(split(self.pieces["R"]))
        white_bishops = list(split(self.pieces["B"]))
        white_knights = list(split(self.pieces["N"]))
        white_pawns = list(split(self.pieces["P"]))
        black_queens = list(split(self.pieces["q"]))
        black_rooks = list(split(self.pieces["r"]))
        black_bishops = list(split(self.pieces["b"]))
        black_knights = list(split(self.pieces["n"]))
        black_pawns = list(split(self.pieces["p"]))

        number_of_white_pieces = (len(white_queens) + len(white_rooks) +
                                  len(white_bishops) + len(white_knights) +
                                  len(white_pawns))
        number_of_black_pieces = (len(black_queens) + len(black_rooks) +
                                  len(black_bishops) + len(black_knights) +
                                  len(black_pawns))

        # king against king
        if number_of_white_pieces + number_of_black_pieces == 0:
            return True

        # king against king and bishop
        if (number_of_white_pieces == 0 and number_of_black_pieces == 1
                and len(black_bishops) == 1):
            return True
        if (number_of_black_pieces == 0 and number_of_white_pieces == 1
                and len(white_bishops) == 1):
            return True

        # king against king and knight
        if (number_of_white_pieces == 0 and number_of_black_pieces == 1
                and len(black_knights) == 1):
            return True
        if (number_of_black_pieces == 0 and number_of_white_pieces == 1
                and len(white_knights) == 1):
            return True

        # king and bishop against king and bishop, with both bishops on squares of the same color
        if (number_of_white_pieces == 1 and number_of_black_pieces == 1
                and len(white_bishops) == 1 and len(black_bishops) == 1):
            is_white_bishop_on_white_square = (white_bishops[0]
                                               & 0xAA55_AA55_AA55_AA55) == 0
            is_black_bishop_on_white_square = (black_bishops[0]
                                               & 0xAA55_AA55_AA55_AA55) == 0
            return is_white_bishop_on_white_square == is_black_bishop_on_white_square

        return False
コード例 #2
0
ファイル: game.py プロジェクト: thomasheyenbrock/chess-ai
    def checkers(self, player: bool, king: int) -> Iterable[int]:
        queen = self.pieces["Q" if player else "q"]
        rook = self.pieces["R" if player else "r"]
        bishop = self.pieces["B" if player else "b"]
        knight = self.pieces["N" if player else "n"]
        pawn = self.pieces["P" if player else "p"]

        queen_and_rook = queen | rook
        queen_and_bishop = queen | bishop

        north_pieces = NORTH_RAY[king] & self.all_pieces
        south_pieces = SOUTH_RAY[king] & self.all_pieces
        west_pieces = WEST_RAY[king] & self.all_pieces
        east_pieces = EAST_RAY[king] & self.all_pieces
        north_west_pieces = NORTH_WEST_RAY[king] & self.all_pieces
        south_west_pieces = SOUTH_WEST_RAY[king] & self.all_pieces
        north_east_pieces = NORTH_EAST_RAY[king] & self.all_pieces
        south_east_pieces = SOUTH_EAST_RAY[king] & self.all_pieces

        return split(
            (NORTH_ATTACKS[king][north_pieces] & queen_and_rook)
            | (SOUTH_ATTACKS[king][south_pieces] & queen_and_rook)
            | (WEST_ATTACKS[king][west_pieces] & queen_and_rook)
            | (EAST_ATTACKS[king][east_pieces] & queen_and_rook)
            | (NORTH_WEST_ATTACKS[king][north_west_pieces] & queen_and_bishop)
            | (SOUTH_WEST_ATTACKS[king][south_west_pieces] & queen_and_bishop)
            | (NORTH_EAST_ATTACKS[king][north_east_pieces] & queen_and_bishop)
            | (SOUTH_EAST_ATTACKS[king][south_east_pieces] & queen_and_bishop)
            | (KNIGHT_MOVES[king] & knight)
            | (PAWN_ATTACKS[player][king] & pawn))
コード例 #3
0
def get_queen_moves(from_square: int) -> Iterable[int]:
    moveable_squares = (
        NORTH_RAY[from_square]
        | SOUTH_RAY[from_square]
        | WEST_RAY[from_square]
        | EAST_RAY[from_square]
        | NORTH_WEST_RAY[from_square]
        | NORTH_EAST_RAY[from_square]
        | SOUTH_WEST_RAY[from_square]
        | SOUTH_EAST_RAY[from_square]
    )
    return split(moveable_squares)
コード例 #4
0
ファイル: game.py プロジェクト: thomasheyenbrock/chess-ai
    def legal_moves(self) -> Iterable[Move]:
        friendly_pieces = (self.position.white_pieces
                           if self.player else self.position.black_pieces)
        enemy_pieces = (self.position.black_pieces
                        if self.player else self.position.white_pieces)
        empty_squares = 0xFFFF_FFFF_FFFF_FFFF ^ self.position.all_pieces
        attacked_squares = self.position.attacked_squares(
            player=not self.player, exclude_king=True)

        king = self.position.pieces["K" if self.player else "k"]
        king_moves = KING_MOVES[king] & (0xFFFF_FFFF_FFFF_FFFF
                                         ^ attacked_squares)
        king_moves ^= king_moves & friendly_pieces
        for to_square in split(king_moves):
            yield Move(
                player=self.player,
                piece="K",
                from_square=king,
                to_square=to_square,
            )

        attackers = list(
            self.position.checkers(player=not self.player, king=king))
        number_of_attackers = len(attackers)
        if number_of_attackers > 1:
            # Multiple pieces are giving check, so the king has to move
            return

        capture_mask = 0xFFFF_FFFF_FFFF_FFFF
        push_mask = 0xFFFF_FFFF_FFFF_FFFF
        if number_of_attackers == 1:
            attacker = attackers[0]
            capture_mask = attacker
            if (attacker & self.position.pieces["n" if self.player else "N"] !=
                    0) or (attacker
                           & self.position.pieces["p" if self.player else "P"]
                           != 0):
                # checked by knight or pawn, this can't be blocked
                push_mask = 0
            else:
                # checked by slider, this can be blocked
                push_mask = (NORTH_MOVES[king].get(attacker, 0)
                             | SOUTH_MOVES[king].get(attacker, 0)
                             | WEST_MOVES[king].get(attacker, 0)
                             | EAST_MOVES[king].get(attacker, 0)
                             | NORTH_WEST_MOVES[king].get(attacker, 0)
                             | NORTH_EAST_MOVES[king].get(attacker, 0)
                             | SOUTH_WEST_MOVES[king].get(attacker, 0)
                             | SOUTH_EAST_MOVES[king].get(attacker, 0))

        capture_or_push_mask = capture_mask | push_mask

        enemy_queens = self.position.pieces["q" if self.player else "Q"]
        enemy_queens_and_rooks = (
            enemy_queens | self.position.pieces["r" if self.player else "R"])
        enemy_queens_and_bishops = (
            enemy_queens | self.position.pieces["b" if self.player else "B"])

        for from_square in split(
                self.position.pieces["Q" if self.player else "q"]):
            moveable_squares = (
                capture_or_push_mask
                & (get_rank_and_file_moves(self.position.all_pieces,
                                           enemy_pieces, from_square)
                   | get_diagonal_moves(self.position.all_pieces, enemy_pieces,
                                        from_square))
                & self.position.pinned_movement(
                    square=from_square,
                    king=king,
                    enemy_queens_and_rooks=enemy_queens_and_rooks,
                    enemy_queens_and_bishops=enemy_queens_and_bishops,
                ))
            for to_square in split(moveable_squares):
                yield Move(
                    player=self.player,
                    piece="Q",
                    from_square=from_square,
                    to_square=to_square,
                )

        for from_square in split(
                self.position.pieces["R" if self.player else "r"]):
            moveable_squares = (
                capture_or_push_mask
                & get_rank_and_file_moves(self.position.all_pieces,
                                          enemy_pieces, from_square)
                & self.position.pinned_movement(
                    square=from_square,
                    king=king,
                    enemy_queens_and_rooks=enemy_queens_and_rooks,
                    enemy_queens_and_bishops=enemy_queens_and_bishops,
                ))
            for to_square in split(moveable_squares):
                yield Move(
                    player=self.player,
                    piece="R",
                    from_square=from_square,
                    to_square=to_square,
                )

        for from_square in split(
                self.position.pieces["B" if self.player else "b"]):
            moveable_squares = (
                capture_or_push_mask
                & get_diagonal_moves(self.position.all_pieces, enemy_pieces,
                                     from_square)
                & self.position.pinned_movement(
                    square=from_square,
                    king=king,
                    enemy_queens_and_rooks=enemy_queens_and_rooks,
                    enemy_queens_and_bishops=enemy_queens_and_bishops,
                ))
            for to_square in split(moveable_squares):
                yield Move(
                    player=self.player,
                    piece="B",
                    from_square=from_square,
                    to_square=to_square,
                )

        for from_square in split(
                self.position.pieces["N" if self.player else "n"]):
            moveable_squares = (
                capture_or_push_mask
                & KNIGHT_MOVES[from_square]
                & (KNIGHT_MOVES[from_square] ^ friendly_pieces)
                & self.position.pinned_movement(
                    square=from_square,
                    king=king,
                    enemy_queens_and_rooks=enemy_queens_and_rooks,
                    enemy_queens_and_bishops=enemy_queens_and_bishops,
                ))
            for to_square in split(moveable_squares):
                yield Move(
                    player=self.player,
                    piece="N",
                    from_square=from_square,
                    to_square=to_square,
                )

        for from_square in split(
                self.position.pieces["P" if self.player else "p"]):
            pinned_movement = self.position.pinned_movement(
                square=from_square,
                king=king,
                enemy_queens_and_rooks=enemy_queens_and_rooks,
                enemy_queens_and_bishops=enemy_queens_and_bishops,
            )
            to_square = (PAWN_SINGLE_MOVES[self.player][from_square]
                         & empty_squares
                         & pinned_movement
                         & push_mask)
            if to_square != 0:
                yield Move(
                    player=self.player,
                    piece="P",
                    from_square=from_square,
                    to_square=to_square,
                )

            attacks = [
                p & enemy_pieces & pinned_movement & capture_mask
                for p in PAWN_ATTACK_MOVES[self.player][from_square]
            ]
            for to_square in attacks:
                if to_square == 0:
                    continue
                yield Move(
                    player=self.player,
                    piece="P",
                    from_square=from_square,
                    to_square=to_square,
                )

            to_square = (PAWN_DOUBLE_MOVES[self.player][from_square]
                         & empty_squares
                         & (get_top_square(empty_squares) if self.player else
                            get_bottom_square(empty_squares))
                         & pinned_movement
                         & push_mask)
            if to_square:
                yield Move(
                    player=self.player,
                    piece="P",
                    from_square=from_square,
                    to_square=to_square,
                    en_passant_square=get_bottom_square(to_square)
                    if self.player else get_top_square(to_square),
                )

            to_square = (PAWN_EN_PASSANT_CAPTURES[self.player][from_square]
                         & self.en_passant_square
                         & pinned_movement
                         & (get_top_square(capture_mask) if self.player else
                            get_bottom_square(capture_mask)))
            if to_square:
                move = Move(
                    player=self.player,
                    piece="P",
                    from_square=from_square,
                    to_square=to_square,
                    is_capturing_en_passant=True,
                )
                position = self.position.move(move)[0]
                if not position.is_check(self.player):
                    yield move

            single_move_promotions = [
                p & empty_squares & pinned_movement & push_mask
                for p in PAWN_SINGLE_MOVES_PROMOTION[self.player][from_square]
            ]
            attack_promotions = [
                p & enemy_pieces & pinned_movement & capture_mask
                for p in PAWN_ATTACK_MOVES_PROMOTION[self.player][from_square]
            ]
            for to_square in single_move_promotions + attack_promotions:
                if to_square == 0:
                    continue
                yield Move(
                    player=self.player,
                    piece="P",
                    from_square=from_square,
                    to_square=to_square,
                    is_promoting_to="Q",
                )
                yield Move(
                    player=self.player,
                    piece="P",
                    from_square=from_square,
                    to_square=to_square,
                    is_promoting_to="R",
                )
                yield Move(
                    player=self.player,
                    piece="P",
                    from_square=from_square,
                    to_square=to_square,
                    is_promoting_to="B",
                )
                yield Move(
                    player=self.player,
                    piece="P",
                    from_square=from_square,
                    to_square=to_square,
                    is_promoting_to="N",
                )

        can_castle_kingside = (
            self.possible_castles["K" if self.player else "k"] and
            (self.position.all_pieces
             &
             (0x0000_0000_0000_0006 if self.player else 0x0600_0000_0000_0000))
            == 0 and
            (attacked_squares
             &
             (0x0000_0000_0000_000E if self.player else 0x0E00_0000_0000_0000)
             == 0))

        if can_castle_kingside:
            yield Move(
                player=self.player,
                piece="K",
                from_square=0x0000_0000_0000_0008
                if self.player else 0x0800_0000_0000_0000,
                to_square=0x0000_0000_0000_0002
                if self.player else 0x0200_0000_0000_0000,
                is_castling="K" if self.player else "k",
            )

        can_castle_queenside = (
            self.possible_castles["Q" if self.player else "q"] and
            (self.position.all_pieces
             &
             (0x0000_0000_0000_0070 if self.player else 0x7000_0000_0000_0000))
            == 0 and
            (attacked_squares
             &
             (0x0000_0000_0000_0038 if self.player else 0x3800_0000_0000_0000)
             == 0))

        if can_castle_queenside:
            yield Move(
                player=self.player,
                piece="K",
                from_square=0x0000_0000_0000_0008
                if self.player else 0x0800_0000_0000_0000,
                to_square=0x0000_0000_0000_0020
                if self.player else 0x2000_0000_0000_0000,
                is_castling="Q" if self.player else "q",
            )
コード例 #5
0
ファイル: game.py プロジェクト: thomasheyenbrock/chess-ai
    def attacked_squares(self,
                         player: bool,
                         exclude_king: bool = False) -> int:
        all_pieces = self.all_pieces
        if exclude_king:
            all_pieces ^= self.pieces[
                "k" if player else "K"] if exclude_king else 0

        attacked = KING_MOVES[self.pieces["K" if player else "k"]]

        for queen in split(self.pieces["Q" if player else "q"]):
            north_pieces = NORTH_RAY[queen] & all_pieces
            attacked |= (NORTH_MOVES[queen][north_pieces]
                         | NORTH_ATTACKS[queen][north_pieces])
            south_pieces = SOUTH_RAY[queen] & all_pieces
            attacked |= (SOUTH_MOVES[queen][south_pieces]
                         | SOUTH_ATTACKS[queen][south_pieces])
            west_pieces = WEST_RAY[queen] & all_pieces
            attacked |= (WEST_MOVES[queen][west_pieces]
                         | WEST_ATTACKS[queen][west_pieces])
            east_pieces = EAST_RAY[queen] & all_pieces
            attacked |= (EAST_MOVES[queen][east_pieces]
                         | EAST_ATTACKS[queen][east_pieces])
            north_west_pieces = NORTH_WEST_RAY[queen] & all_pieces
            attacked |= (NORTH_WEST_MOVES[queen][north_west_pieces]
                         | NORTH_WEST_ATTACKS[queen][north_west_pieces])
            north_east_pieces = NORTH_EAST_RAY[queen] & all_pieces
            attacked |= (NORTH_EAST_MOVES[queen][north_east_pieces]
                         | NORTH_EAST_ATTACKS[queen][north_east_pieces])
            south_west_pieces = SOUTH_WEST_RAY[queen] & all_pieces
            attacked |= (SOUTH_WEST_MOVES[queen][south_west_pieces]
                         | SOUTH_WEST_ATTACKS[queen][south_west_pieces])
            south_east_pieces = SOUTH_EAST_RAY[queen] & all_pieces
            attacked |= (SOUTH_EAST_MOVES[queen][south_east_pieces]
                         | SOUTH_EAST_ATTACKS[queen][south_east_pieces])

        for rook in split(self.pieces["R" if player else "r"]):
            north_pieces = NORTH_RAY[rook] & all_pieces
            attacked |= (NORTH_MOVES[rook][north_pieces]
                         | NORTH_ATTACKS[rook][north_pieces])
            south_pieces = SOUTH_RAY[rook] & all_pieces
            attacked |= (SOUTH_MOVES[rook][south_pieces]
                         | SOUTH_ATTACKS[rook][south_pieces])
            west_pieces = WEST_RAY[rook] & all_pieces
            attacked |= WEST_MOVES[rook][west_pieces] | WEST_ATTACKS[rook][
                west_pieces]
            east_pieces = EAST_RAY[rook] & all_pieces
            attacked |= EAST_MOVES[rook][east_pieces] | EAST_ATTACKS[rook][
                east_pieces]

        for bishop in split(self.pieces["B" if player else "b"]):
            north_west_pieces = NORTH_WEST_RAY[bishop] & all_pieces
            attacked |= (NORTH_WEST_MOVES[bishop][north_west_pieces]
                         | NORTH_WEST_ATTACKS[bishop][north_west_pieces])
            north_east_pieces = NORTH_EAST_RAY[bishop] & all_pieces
            attacked |= (NORTH_EAST_MOVES[bishop][north_east_pieces]
                         | NORTH_EAST_ATTACKS[bishop][north_east_pieces])
            south_west_pieces = SOUTH_WEST_RAY[bishop] & all_pieces
            attacked |= (SOUTH_WEST_MOVES[bishop][south_west_pieces]
                         | SOUTH_WEST_ATTACKS[bishop][south_west_pieces])
            south_east_pieces = SOUTH_EAST_RAY[bishop] & all_pieces
            attacked |= (SOUTH_EAST_MOVES[bishop][south_east_pieces]
                         | SOUTH_EAST_ATTACKS[bishop][south_east_pieces])

        for knight in split(self.pieces["N" if player else "n"]):
            attacked |= KNIGHT_MOVES[knight]

        for pawn in split(self.pieces["P" if player else "p"]):
            for s in PAWN_ATTACK_MOVES[player][pawn]:
                attacked |= s
            for s in PAWN_ATTACK_MOVES_PROMOTION[player][pawn]:
                attacked |= s

        return attacked
コード例 #6
0
    (0x0000_0000_0000_8000, 0x0000_0000_0000_0040),
    (0x0000_0000_0000_8000, 0x0000_0000_0000_0080),
]

move_for_output_index = []

for from_square in split(0xFFFF_FFFF_FFFF_FFFF):
    for to_square in get_queen_moves(from_square):
        move_for_output_index += [
            Move(
                player=True, piece="Q", from_square=from_square, to_square=to_square
            ).id()
        ]

for from_square in split(0xFFFF_FFFF_FFFF_FFFF):
    for to_square in split(KNIGHT_MOVES[from_square]):
        move_for_output_index += [
            Move(
                player=True, piece="N", from_square=from_square, to_square=to_square
            ).id()
        ]

for (from_square, to_square) in white_promotion_moves:
    move_for_output_index += [
        Move(
            player=True,
            piece="P",
            from_square=from_square,
            to_square=to_square,
            is_promoting_to="Q",
        ).id(),