예제 #1
0
    def has_insufficient_material(self, color: chess.Color) -> bool:
        # Remaining material does not matter if opponent's king is already
        # exploded.
        if not (self.occupied_co[not color] & self.kings):
            return False

        # Bare king can not mate.
        if not (self.occupied_co[color] & ~self.kings):
            return True

        # As long as the opponent's king is not alone, there is always a chance
        # their own pieces explode next to it.
        if self.occupied_co[not color] & ~self.kings:
            # Unless there are only bishops that cannot explode each other.
            if self.occupied == self.bishops | self.kings:
                if not (self.bishops & self.occupied_co[chess.WHITE] & chess.BB_DARK_SQUARES):
                    return not (self.bishops & self.occupied_co[chess.BLACK] & chess.BB_LIGHT_SQUARES)
                if not (self.bishops & self.occupied_co[chess.WHITE] & chess.BB_LIGHT_SQUARES):
                    return not (self.bishops & self.occupied_co[chess.BLACK] & chess.BB_DARK_SQUARES)
            return False

        # Queen or pawn (future queen) can give mate against bare king.
        if self.queens or self.pawns:
            return False

        # Single knight, bishop or rook cannot mate against bare king.
        if chess.popcount(self.knights | self.bishops | self.rooks) == 1:
            return True

        # Two knights cannot mate against bare king.
        if self.occupied == self.knights | self.kings:
            return chess.popcount(self.knights) <= 2

        return False
예제 #2
0
    def status(self):
        status = super(CrazyhouseBoard, self).status()

        if chess.popcount(self.pawns) + self.pockets[chess.WHITE].count(chess.PAWN) + self.pockets[chess.BLACK].count(chess.PAWN) <= 16:
            status &= ~chess.STATUS_TOO_MANY_BLACK_PAWNS
            status &= ~chess.STATUS_TOO_MANY_WHITE_PAWNS

        if chess.popcount(self.occupied) + len(self.pockets[chess.WHITE]) + len(self.pockets[chess.BLACK]) <= 32:
            status &= ~chess.STATUS_TOO_MANY_BLACK_PIECES
            status &= ~chess.STATUS_TOO_MANY_WHITE_PIECES

        return status
예제 #3
0
 def status(self):
     status = super(RacingKingsBoard, self).status()
     if self.is_check():
         status |= chess.STATUS_RACE_CHECK
     if self.turn == chess.BLACK and all(self.occupied_co[co] & self.kings & chess.BB_RANK_8 for co in chess.COLORS):
         status |= chess.STATUS_RACE_OVER
     if self.pawns:
         status |= chess.STATUS_RACE_MATERIAL
     for color in chess.COLORS:
         if chess.popcount(self.occupied_co[color] & self.knights) > 2:
             status |= chess.STATUS_RACE_MATERIAL
         if chess.popcount(self.occupied_co[color] & self.bishops) > 2:
             status |= chess.STATUS_RACE_MATERIAL
         if chess.popcount(self.occupied_co[color] & self.rooks) > 2:
             status |= chess.STATUS_RACE_MATERIAL
         if chess.popcount(self.occupied_co[color] & self.queens) > 1:
             status |= chess.STATUS_RACE_MATERIAL
     return status
예제 #4
0
 def has_insufficient_material(self, color: chess.Color) -> bool:
     # In practise no material can leave the game, but this is easy to
     # implement anyway. Note that bishops can be captured and put onto
     # a different color complex.
     return (
         chess.popcount(self.occupied) + sum(len(pocket) for pocket in self.pockets) <= 3 and
         not self.pawns and
         not self.rooks and
         not self.queens and
         not any(pocket.count(chess.PAWN) for pocket in self.pockets) and
         not any(pocket.count(chess.ROOK) for pocket in self.pockets) and
         not any(pocket.count(chess.QUEEN) for pocket in self.pockets))
예제 #5
0
    def legal_drop_squares_mask(self):
        king = self.king(self.turn)
        if king is None:
            return ~self.occupied

        king_attackers = self.attackers_mask(not self.turn, king)

        if not king_attackers:
            return ~self.occupied
        elif chess.popcount(king_attackers) == 1:
            return chess.BB_BETWEEN[king][chess.msb(king_attackers)] & ~self.occupied
        else:
            return chess.BB_VOID
예제 #6
0
    def is_insufficient_material(self):
        if self.is_variant_loss() or self.is_variant_win():
            return False

        if self.pawns or self.queens:
            return False

        if chess.popcount(self.knights | self.bishops | self.rooks) == 1:
            return True

        # Only knights.
        if self.occupied == (self.kings | self.knights):
            return chess.popcount(self.knights) <= 2

        # Only bishops.
        if self.occupied == (self.kings | self.bishops):
            # All bishops on opposite colors.
            if not self.pieces_mask(chess.BISHOP, chess.WHITE) & chess.BB_DARK_SQUARES:
                return not self.pieces_mask(chess.BISHOP, chess.BLACK) & chess.BB_LIGHT_SQUARES
            if not self.pieces_mask(chess.BISHOP, chess.WHITE) & chess.BB_LIGHT_SQUARES:
                return not self.pieces_mask(chess.BISHOP, chess.BLACK) & chess.BB_DARK_SQUARES

        return False
예제 #7
0
    def status(self):
        status = super().status()
        status &= ~chess.STATUS_NO_WHITE_KING

        if chess.popcount(self.occupied_co[chess.WHITE]) <= 36:
            status &= ~chess.STATUS_TOO_MANY_WHITE_PIECES
            status &= ~chess.STATUS_TOO_MANY_WHITE_PAWNS

        if not self.pawns & chess.BB_RANK_8 and not self.occupied_co[chess.BLACK] & chess.BB_RANK_1:
            status &= ~chess.STATUS_PAWNS_ON_BACKRANK

        if self.occupied_co[chess.WHITE] & self.kings:
            status |= chess.STATUS_TOO_MANY_KINGS

        return status
예제 #8
0
    def status(self):
        status = super(HordeBoard, self).status()
        status &= ~chess.STATUS_NO_WHITE_KING

        if chess.popcount(self.occupied_co[chess.WHITE]) <= 36:
            status &= ~chess.STATUS_TOO_MANY_WHITE_PIECES
            status &= ~chess.STATUS_TOO_MANY_WHITE_PAWNS

        if not self.pawns & chess.BB_RANK_8 and not self.occupied_co[chess.BLACK] & chess.BB_RANK_1:
            status &= ~chess.STATUS_PAWNS_ON_BACKRANK

        if self.occupied_co[chess.WHITE] & self.kings:
            status |= chess.STATUS_TOO_MANY_KINGS

        return status
예제 #9
0
    def legal_drop_squares_mask(self):
        king_bb = self.kings & self.occupied_co[self.turn]
        if not king_bb:
            return ~self.occupied

        king_square = chess.msb(king_bb)
        king_attackers = self.attackers_mask(not self.turn, king_square)

        if not king_attackers:
            return ~self.occupied
        elif chess.popcount(king_attackers) == 1:
            return chess.BB_BETWEEN[king_square][chess.msb(
                king_attackers)] & ~self.occupied
        else:
            return chess.BB_VOID
예제 #10
0
def evaluate(board):
    """ This is a simple evaluation function that takes only into account the material 
    :param board: a Board object, i.e. the state of the game
    :return score: the score associated to the Board in centipawns
    """
    score = 0
    w = board.occupied_co[ch.WHITE]
    b = board.occupied_co[ch.BLACK]
    score += (ch.popcount(board.pawns & w) - ch.popcount(board.pawns & b)) * PAWN_VALUE + \
             (ch.popcount(board.knights & w) - ch.popcount(board.knights & b)) * KNIGHT_VALUE + \
             (ch.popcount(board.bishops & w) - ch.popcount(board.bishops & b)) * BISHOP_VALUE + \
             (ch.popcount(board.rooks & w) - ch.popcount(board.rooks & b)) * ROOK_VALUE + \
             (ch.popcount(board.queens & w) - ch.popcount(board.queens & b)) * QUEEN_VALUE + \
             (ch.popcount(board.kings & w) - ch.popcount(board.kings & b)) * KING_VALUE
    return score
예제 #11
0
 def piece_count(self, board):
     return chess.popcount(board.occupied)
예제 #12
0
파일: tb.py 프로젝트: petersn/TinyMLChess
def score_position(board):
	if chess.popcount(board.occupied) > 5:
		return
	return tablebase.probe_dtz(board)
예제 #13
0
def material_count(board):
    return chess.popcount(board.occupied)
예제 #14
0
def material_count(board: Board) -> int:
    """ Count the number of pieces on the board
    """
    return popcount(board.occupied)
예제 #15
0
 def material_count(self):
     return chess.popcount(self.position.occupied)
예제 #16
0
if len(sys.argv) < 2:
    print(
        "Usage:", sys.argv[0],
        "<filename_with_games_fen_positions_each_game_starting_with_underscore>"
    )
    exit(1)

filename = sys.argv[1]
fileopenings = open(filename + ".openings.epd", "w")
filemiddlegames = open(filename + ".middlegames.epd", "w")
fileendgames = open(filename + ".endgames.epd", "w")

board = chess.Board()
lines = [line.rstrip('\n') for line in open(sys.argv[1])]
for line in lines:
    if line == "_":
        currentmove = 0
    else:
        currentmove = currentmove + 1
        epdposition = " ".join(line.split()[0:4])
        if currentmove < 10:
            fileopenings.write(epdposition + "\n")
        else:
            board.set_epd(epdposition)
            if chess.popcount(board.occupied) < 10:
                fileendgames.write(epdposition + "\n")
            else:
                filemiddlegames.write(epdposition + "\n")

exit(0)
예제 #17
0
 def get_psqt_index(self, board: chess.Board):
     return (chess.popcount(board.occupied) - 1) // 4
예제 #18
0
 def _material_balance(self):
     return (chess.popcount(self.occupied_co[self.turn]) -
             chess.popcount(self.occupied_co[not self.turn]))
예제 #19
0
    async def index(self, request):
        render = {}

        # Setup a board from the given valid FEN or fall back to the default FEN.
        try:
            board = chess.Board(
                request.query.get("fen", DEFAULT_FEN).replace("_", " "))
        except ValueError:
            try:
                board, _ = chess.Board.from_epd(
                    request.query.get("fen", DEFAULT_FEN).replace("_", " "))
            except ValueError:
                board = chess.Board(DEFAULT_FEN)

        # Get FENs with the current side to move, black and white to move.
        original_turn = board.turn
        board.turn = chess.WHITE
        render["white_fen"] = board.fen()
        board.turn = chess.BLACK
        render["black_fen"] = board.fen()
        board.turn = original_turn
        render["fen"] = fen = board.fen()

        # Mirrored and color swapped FENs for the toolbar.
        render["turn"] = "white" if board.turn == chess.WHITE else "black"
        render["horizontal_fen"] = mirror_horizontal(fen)
        render["vertical_fen"] = mirror_vertical(fen)
        render["swapped_fen"] = swap_colors(fen)
        render["clear_fen"] = clear_fen(fen)
        render["fen_input"] = "" if board.epd(
        ) + " 0 1" == DEFAULT_FEN else board.epd() + " 0 1"

        # Material key for the page title.
        render["material"] = material(board)
        render["piece_count"] = chess.popcount(board.occupied)

        # Moves are going to be grouped by WDL.
        grouped_moves = {-2: [], -1: [], 0: [], 1: [], 2: [], None: []}

        if not board.is_valid():
            render["status"] = "Invalid position"
            render["illegal"] = True
        elif board.is_stalemate():
            render["status"] = "Draw by stalemate"
        elif board.is_checkmate():
            if board.turn == chess.WHITE:
                render["status"] = "Black won by checkmate"
                render["winning_side"] = "black"
            else:
                render["status"] = "White won by checkmate"
                render["winning_side"] = "white"
        else:
            # Probe.
            probe = await self.api.probe_async(board,
                                               load_root=True,
                                               load_dtz=True,
                                               load_wdl=True,
                                               load_dtm=True)
            render["blessed_loss"] = probe["wdl"] == -1
            render["cursed_win"] = probe["wdl"] == 1

            # Set status line.
            if board.is_insufficient_material():
                render["status"] = "Draw by insufficient material"
                render["insufficient_material"] = True
            elif probe["wdl"] is None or probe["dtz"] is None:
                render["status"] = "Position not found in tablebases"
            elif probe["wdl"] == 0:
                render["status"] = "Tablebase draw"
            elif probe["dtz"] > 0 and board.turn == chess.WHITE:
                render["status"] = "White is winning with DTZ %d" % (abs(
                    probe["dtz"]), )
                render["winning_side"] = "white"
            elif probe["dtz"] < 0 and board.turn == chess.WHITE:
                render["status"] = "White is losing with DTZ %d" % (abs(
                    probe["dtz"]), )
                render["winning_side"] = "black"
            elif probe["dtz"] > 0 and board.turn == chess.BLACK:
                render["status"] = "Black is winning with DTZ %d" % (abs(
                    probe["dtz"]), )
                render["winning_side"] = "black"
            elif probe["dtz"] < 0 and board.turn == chess.BLACK:
                render["status"] = "Black is losing with DTZ %d" % (abs(
                    probe["dtz"]), )
                render["winning_side"] = "white"

            render["frustrated"] = probe["wdl"] is not None and abs(
                probe["wdl"]) == 1

            # Label and group all legal moves.
            for move in board.legal_moves:
                san = board.san(move)
                uci = board.uci(move)
                board.push(move)

                move_info = {
                    "uci": uci,
                    "san": san,
                    "fen": board.epd() + " 0 1",
                    "wdl": probe["moves"][uci]["wdl"],
                    "dtz": probe["moves"][uci]["dtz"],
                    "dtm": probe["moves"][uci]["dtm"],
                    "zeroing": board.halfmove_clock == 0,
                    "checkmate": board.is_checkmate(),
                    "stalemate": board.is_stalemate(),
                    "insufficient_material": board.is_insufficient_material(),
                }

                move_info["dtm"] = abs(
                    move_info["dtm"]) if move_info["dtm"] is not None else None

                if move_info["checkmate"]:
                    move_info["wdl"] = -2
                elif move_info["stalemate"] or move_info[
                        "insufficient_material"]:
                    move_info["wdl"] = 0

                if move_info["checkmate"]:
                    move_info["badge"] = "Checkmate"
                elif move_info["stalemate"]:
                    move_info["badge"] = "Stalemate"
                elif move_info["insufficient_material"]:
                    move_info["badge"] = "Insufficient material"
                elif move_info["dtz"] == 0:
                    move_info["badge"] = "Draw"
                elif move_info["dtz"] is None:
                    move_info["badge"] = "Unknown"
                elif move_info["zeroing"]:
                    move_info["badge"] = "Zeroing"
                elif move_info["dtz"] < 0:
                    move_info["badge"] = "Win with DTZ %d" % (abs(
                        move_info["dtz"]), )
                elif move_info["dtz"] > 0:
                    move_info["badge"] = "Loss with DTZ %d" % (abs(
                        move_info["dtz"]), )

                grouped_moves[move_info["wdl"]].append(move_info)

                board.pop()

        # Sort winning moves.
        grouped_moves[-2].sort(key=lambda move: move["uci"])
        grouped_moves[-2].sort(
            key=lambda move: (move["dtm"] is None, move["dtm"]))
        grouped_moves[-2].sort(key=lambda move:
                               (move["dtz"] is None, move["dtz"]),
                               reverse=True)
        grouped_moves[-2].sort(key=lambda move: move["zeroing"], reverse=True)
        grouped_moves[-2].sort(key=lambda move: move["checkmate"],
                               reverse=True)
        render["winning_moves"] = grouped_moves[-2]

        # Sort moves leading to cursed wins.
        grouped_moves[-1].sort(key=lambda move: move["uci"])
        grouped_moves[-1].sort(
            key=lambda move: (move["dtm"] is None, move["dtm"]))
        grouped_moves[-1].sort(key=lambda move:
                               (move["dtz"] is None, move["dtz"]),
                               reverse=True)
        grouped_moves[-1].sort(key=lambda move: move["zeroing"], reverse=True)
        render["cursed_moves"] = grouped_moves[-1]

        # Sort drawing moves.
        grouped_moves[0].sort(key=lambda move: move["uci"])
        grouped_moves[0].sort(key=lambda move: move["insufficient_material"],
                              reverse=True)
        grouped_moves[0].sort(key=lambda move: move["stalemate"], reverse=True)
        render["drawing_moves"] = grouped_moves[0]

        # Sort moves leading to a blessed loss.
        grouped_moves[1].sort(key=lambda move: move["uci"])
        grouped_moves[1].sort(key=lambda move:
                              (move["dtm"] is not None, move["dtm"]),
                              reverse=True)
        grouped_moves[1].sort(key=lambda move:
                              (move["dtz"] is None, move["dtz"]),
                              reverse=True)
        grouped_moves[1].sort(key=lambda move: move["zeroing"])
        render["blessed_moves"] = grouped_moves[1]

        # Sort losing moves.
        grouped_moves[2].sort(key=lambda move: move["uci"])
        grouped_moves[2].sort(key=lambda move:
                              (move["dtm"] is not None, move["dtm"]),
                              reverse=True)
        grouped_moves[2].sort(key=lambda move:
                              (move["dtz"] is None, move["dtz"]),
                              reverse=True)
        grouped_moves[2].sort(key=lambda move: move["zeroing"])
        render["losing_moves"] = grouped_moves[2]

        # Sort unknown moves.
        grouped_moves[None].sort(key=lambda move: move["uci"])
        render["unknown_moves"] = grouped_moves[None]

        if "xhr" in request.query:
            template = self.jinja.get_template("xhr-probe.html")
        else:
            template = self.jinja.get_template("index.html")

        return aiohttp.web.Response(text=html_minify(template.render(render)),
                                    content_type="text/html")
예제 #20
0
 def our_piece_count(self):
     return chess.popcount(self.board.occupied_co(self.board.current_color))
예제 #21
0
 def their_piece_count(self):
     return chess.popcount(self.board.occupied_co(self.board.other_color))
예제 #22
0
    def has_insufficient_material(self, color: chess.Color) -> bool:
        # The side with the king can always win by capturing the horde.
        if color == chess.BLACK:
            return False

        white = self.occupied_co[chess.WHITE]
        queens = chess.popcount( white & self.queens )
        pawns = chess.popcount( white & self.pawns )
        rooks = chess.popcount( white & self.rooks )
        bishops = chess.popcount( white & self.bishops )
        knights = chess.popcount( white & self.knights )
        horde_darkb = chess.popcount(chess.BB_DARK_SQUARES & white & self.bishops)
        horde_lightb = chess.popcount(chess.BB_LIGHT_SQUARES & white & self.bishops)
        horde_bishop_co = lambda : chess.WHITE if horde_lightb >= 1 else chess.BLACK
        horde_num = (
            pawns + knights + rooks + queens +
            (horde_darkb if horde_darkb <= 2 else 2) +
            (horde_lightb if horde_lightb <= 2 else 2)
            )
            # Two same color bishops suffice to cover all the light and dark squares around the enemy king.

        pieces = self.occupied_co[chess.BLACK]
        pieces_pawns = chess.popcount( pieces & self.pawns )
        pieces_bishops = chess.popcount( pieces & self.bishops )
        pieces_knights = chess.popcount( pieces & self.knights )
        pieces_rooks = chess.popcount( pieces & self.rooks )
        pieces_queens = chess.popcount( pieces & self.queens )
        pieces_darkb = lambda :  chess.popcount(chess.BB_DARK_SQUARES & pieces & self.bishops)
        pieces_lightb = lambda : chess.popcount(chess.BB_LIGHT_SQUARES & pieces & self.bishops)
        pieces_num = chess.popcount(pieces)        
        pieces_oppositeb_of = lambda square_color: pieces_darkb() if square_color == chess.WHITE else pieces_lightb()
        pieces_sameb_as = lambda square_color: pieces_lightb() if square_color == chess.WHITE else pieces_darkb()
        pieces_of_type_not = lambda piece: pieces_num - piece
        has_bishop_pair = lambda side: (horde_lightb >= 1 and horde_darkb >= 1) if side == chess.WHITE else (pieces_lightb() >= 1 and pieces_darkb() >= 1)

        if horde_num == 0:
            return True

        if horde_num >= 4: 
            # Four or more white pieces can always deliver mate.
            return False
        
        if (pawns >= 1 or queens >= 1) and horde_num >= 2:
            # Pawns/queens are never insufficient material when paired with any other
            # piece (a pawn promotes to a queen and delivers mate).
            return False
        
        if rooks >= 1  and horde_num >= 2:
            # A rook is insufficient material only when it is paired with a bishop
            # against a lone king. The horde can mate in any other case.
            # A rook on A1 and a bishop on C3 mate a king on B1 when there is a
            # friendly pawn/opposite-color-bishop/rook/queen on C2.
            # A rook on B8 and a bishop C3 mate a king on A1 when there is a friendly
            # knight on A2.
            if not (horde_num == 2 and rooks == 1 and bishops == 1 and pieces_of_type_not(pieces_sameb_as(horde_bishop_co())) == 1):
                return False

        if horde_num == 1:
            if pieces_num == 1:
                # A lone piece cannot mate a lone king.
                return True
            elif queens == 1:
                # The horde has a lone queen.
                # A lone queen mates a king on A1 bounded by:
                #  -- a pawn/rook on A2
                #  -- two same color bishops on A2, B1
                # We ignore every other mating case, since it can be reduced to
                # the two previous cases (e.g. a black pawn on A2 and a black
                # bishop on B1).
                return not (
                        pieces_pawns >= 1 or
                        pieces_rooks >= 1 or
                        pieces_lightb() >= 2 or
                        pieces_darkb() >= 2
                        )
            elif pawns == 1:
                # Promote the pawn to a queen or a knight and check whether white
                # can mate.
                pawn_square = chess.SquareSet(self.pawns & white).pop()
                promote_to_queen = HordeBoard(self.fen())
                promote_to_queen.set_piece_at( pawn_square, chess.Piece(chess.QUEEN,chess.WHITE) )
                promote_to_knight = HordeBoard(self.fen())
                promote_to_knight.set_piece_at( pawn_square, chess.Piece(chess.KNIGHT,chess.WHITE) )
                return promote_to_queen.has_insufficient_material(chess.WHITE) and promote_to_knight.has_insufficient_material(chess.WHITE)
            elif rooks == 1:
                # A lone rook mates a king on A8 bounded by a pawn/rook on A7 and a
                # pawn/knight on B7. We ignore every other case, since it can be
                # reduced to the two previous cases.
                # (e.g. three pawns on A7, B7, C7)
                return not (
                    pieces_pawns >= 2 or
                    (pieces_rooks >= 1 and pieces_pawns >= 1) or
                    (pieces_rooks >= 1 and pieces_knights >= 1) or
                    (pieces_pawns >= 1 and pieces_knights >= 1)
                    )
            elif bishops == 1:
                # The horde has a lone bishop.
                return not (
                        # The king can be mated on A1 if there is a pawn/opposite-color-bishop
                        # on A2 and an opposite-color-bishop on B1.
                        # If black has two or more pawns, white gets the benefit of the doubt;
                        # there is an outside chance that white promotes its pawns to
                        # opposite-color-bishops and selfmates theirself.
                        # Every other case that the king is mated by the bishop requires that
                        # black has two pawns or two opposite-color-bishop or a pawn and an
                        # opposite-color-bishop.
                        # For example a king on A3 can be mated if there is
                        # a pawn/opposite-color-bishop on A4, a pawn/opposite-color-bishop on
                        # B3, a pawn/bishop/rook/queen on A2 and any other piece on B2.
                        pieces_oppositeb_of(horde_bishop_co()) >= 2 or
                        (pieces_oppositeb_of(horde_bishop_co()) >= 1 and pieces_pawns >= 1) or
                        pieces_pawns >= 2
                    )
            elif knights == 1:
                # The horde has a lone knight.
                return not (
                        # The king on A1 can be smother mated by a knight on C2 if there is
                        # a pawn/knight/bishop on B2, a knight/rook on B1 and any other piece
                        # on A2.
                        # Moreover, when black has four or more pieces and two of them are
                        # pawns, black can promote their pawns and selfmate theirself.
                        pieces_num >= 4 and (
                            pieces_knights>=2 or pieces_pawns>=2 or
                            (pieces_rooks>=1 and pieces_knights>=1) or
                            (pieces_rooks>=1 and pieces_bishops>=1) or
                            (pieces_knights>=1 and pieces_bishops>=1) or
                            (pieces_rooks>=1 and pieces_pawns>=1) or
                            (pieces_knights>=1 and pieces_pawns>=1) or
                            (pieces_bishops>=1 and pieces_pawns>=1) or
                            (has_bishop_pair(chess.BLACK) and pieces_pawns>=1)
                        ) and ( pieces_of_type_not(pieces_darkb())>=3 if pieces_darkb()>=2 else True )
                        and ( pieces_of_type_not(pieces_lightb())>=3 if pieces_lightb()>=2 else True )
                    )
            
        # By this point, we only need to deal with white's minor pieces.

        elif horde_num == 2:

            if pieces_num == 1: 
                # Two minor pieces cannot mate a lone king.
                return True
            elif knights == 2:
                # A king on A1 is mated by two knights, if it is obstructed by a
                # pawn/bishop/knight on B2. On the other hand, if black only has
                # major pieces it is a draw.
                return not (pieces_pawns + pieces_bishops + pieces_knights >= 1)
            elif has_bishop_pair(chess.WHITE): 
                return not (
                        # A king on A1 obstructed by a pawn/bishop on A2 is mated
                        # by the bishop pair.
                        pieces_pawns >= 1 or pieces_bishops >= 1 or
                        # A pawn/bishop/knight on B4, a pawn/bishop/rook/queen on
                        # A4 and the king on A3 enable Boden's mate by the bishop
                        # pair. In every other case white cannot win.
                        ( pieces_knights >= 1 and pieces_rooks + pieces_queens >= 1 )
                        )
            elif bishops >= 1 and knights >= 1: 
                # The horde has a bishop and a knight.
                return not (
                        # A king on A1 obstructed by a pawn/opposite-color-bishop on
                        # A2 is mated by a knight on D2 and a bishop on C3.
                        pieces_pawns >= 1 or pieces_oppositeb_of(horde_bishop_co()) >= 1 or
                        # A king on A1 bounded by two friendly pieces on A2 and B1 is
                        # mated when the knight moves from D4 to C2 so that both the
                        # knight and the bishop deliver check.
                        pieces_of_type_not( pieces_sameb_as(horde_bishop_co()) ) >=3
                        )
            else:
                # The horde has two or more bishops on the same color.
                # White can only win if black has enough material to obstruct
                # the squares of the opposite color around the king.
                return not (
                        # A king on A1 obstructed by a pawn/opposite-bishop/knight
                        # on A2 and a opposite-bishop/knight on B1 is mated by two
                        # bishops on B2 and C3. This position is theoretically
                        # achievable even when black has two pawns or when they
                        # have a pawn and an opposite color bishop.
                        ( pieces_pawns >= 1 and pieces_oppositeb_of(horde_bishop_co()) >= 1 ) or
                        ( pieces_pawns >= 1 and pieces_knights >= 1 ) or
                        ( pieces_oppositeb_of(horde_bishop_co()) >= 1 and pieces_knights >= 1 ) or
                        ( pieces_oppositeb_of(horde_bishop_co()) >= 2 ) or
                        pieces_knights >= 2 or
                        pieces_pawns >= 2
                        # In every other case, white can only draw.
                        )

        elif horde_num == 3:
            # A king in the corner is mated by two knights and a bishop or three
            # knights or the bishop pair and a knight/bishop.
            if (knights == 2 and bishops == 1) or knights == 3 or has_bishop_pair(chess.WHITE):
                return False
            else:
                # White has two same color bishops and a knight.
                # A king on A1 is mated by a bishop on B2, a bishop on C1 and a
                # knight on C3, as long as there is another black piece to waste
                # a tempo.
                return pieces_num == 1

        return True
예제 #23
0
파일: train.py 프로젝트: dshawul/nn-train
def fill_planes_nnue_(iplanes, ivalues, cidx, pid, kidx, b, side, flip_rank,
                      flip_file):
    """ Compute piece and attack planes for all pieces"""

    pl = side
    npl = not side

    st = cidx

    #white piece attacks
    bb = b.kings & b.occupied_co[pl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 0, bb, flip_rank, flip_file)
    bb = b.queens & b.occupied_co[pl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 1, bb, flip_rank, flip_file)
    bb = b.rooks & b.occupied_co[pl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 2, bb, flip_rank, flip_file)
    bb = b.bishops & b.occupied_co[pl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 3, bb, flip_rank, flip_file)
    bb = b.knights & b.occupied_co[pl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 4, bb, flip_rank, flip_file)
    bb = b.pawns & b.occupied_co[pl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 5, bb, flip_rank, flip_file)

    #black piece attacks
    bb = b.kings & b.occupied_co[npl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 6, bb, flip_rank, flip_file)
    bb = b.queens & b.occupied_co[npl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 7, bb, flip_rank, flip_file)
    bb = b.rooks & b.occupied_co[npl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 8, bb, flip_rank, flip_file)
    bb = b.bishops & b.occupied_co[npl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 9, bb, flip_rank, flip_file)
    bb = b.knights & b.occupied_co[npl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 10, bb, flip_rank, flip_file)
    bb = b.pawns & b.occupied_co[npl]
    cidx = fill_piece_nnue(iplanes, cidx, kidx, 11, bb, flip_rank, flip_file)

    ivalues[st:cidx] = 1

    #extra factorizers
    if NNUE_FACTORIZER_EXTRA > 0:

        OFF0 = (NNUE_KINDICES + 1) * 12
        OFF1 = OFF0 + 1

        for ix in range(0, 6):
            bb = b.pieces_mask(chess.PIECE_TYPES[5 - ix], pl)
            if flip_file: bb = flip_horizontal(bb)
            if flip_rank: bb = flip_vertical(bb)

            #file
            for f in range(0, 8):
                cnt = chess.popcount(bb & chess.BB_FILES[f])
                if cnt > 0:
                    iplanes[cidx, 1] = (ix * 8 + f) * NNUE_CHANNELS + OFF0
                    ivalues[cidx] = cnt
                    cidx = cidx + 1

            #rank
            for r in range(0, 8):
                cnt = chess.popcount(bb & chess.BB_RANKS[r])
                if cnt > 0:
                    iplanes[cidx, 1] = (ix * 8 + r) * NNUE_CHANNELS + OFF1
                    ivalues[cidx] = cnt
                    cidx = cidx + 1

            #four rings
            BB_RING_0 = 0xffffffffffffffff
            BB_RING_1 = 0x007e7e7e7e7e7e00
            BB_RING_2 = 0x00003c3c3c3c0000
            BB_RING_3 = 0x0000001818000000

            cnt = chess.popcount(bb & BB_RING_0)
            if cnt > 0:
                iplanes[cidx, 1] = (6 * 8 + ix) * NNUE_CHANNELS + OFF0
                ivalues[cidx] = cnt
                cidx = cidx + 1

                cnt = chess.popcount(bb & BB_RING_1)
                if cnt > 0:
                    iplanes[cidx, 1] = (7 * 8 + ix) * NNUE_CHANNELS + OFF0
                    ivalues[cidx] = cnt
                    cidx = cidx + 1

                    cnt = chess.popcount(bb & BB_RING_2)
                    if cnt > 0:
                        iplanes[cidx, 1] = (6 * 8 + ix) * NNUE_CHANNELS + OFF1
                        ivalues[cidx] = cnt
                        cidx = cidx + 1

                        cnt = chess.popcount(bb & BB_RING_3)
                        if cnt > 0:
                            iplanes[cidx,
                                    1] = (7 * 8 + ix) * NNUE_CHANNELS + OFF1
                            ivalues[cidx] = cnt
                            cidx = cidx + 1

            #dark squares
            if ix == 3:
                cnt = chess.popcount(bb & chess.BB_DARK_SQUARES)
                if cnt > 0:
                    iplanes[cidx, 1] = (6 * 8 + 6) * NNUE_CHANNELS + OFF0
                    ivalues[cidx] = cnt
                    cidx = cidx + 1
            if ix == 4:
                cnt = chess.popcount(bb & chess.BB_DARK_SQUARES)
                if cnt > 0:
                    iplanes[cidx, 1] = (6 * 8 + 7) * NNUE_CHANNELS + OFF0
                    ivalues[cidx] = cnt
                    cidx = cidx + 1
            if ix == 5:
                cnt = chess.popcount(bb & chess.BB_DARK_SQUARES)
                if cnt > 0:
                    iplanes[cidx, 1] = (7 * 8 + 6) * NNUE_CHANNELS + OFF0
                    ivalues[cidx] = cnt
                    cidx = cidx + 1

                    cnt = chess.popcount(bb & chess.BB_DARK_SQUARES
                                         & BB_RING_2)
                    if cnt > 0:
                        iplanes[cidx, 1] = (7 * 8 + 7) * NNUE_CHANNELS + OFF0
                        ivalues[cidx] = cnt
                        cidx = cidx + 1

            #diagonals
            BB_DIAG_A1H8 = 0x8040201008040201
            BB_DIAG_A8H1 = 0x0102040810204080
            BB_DIAG_A1H8N = 0x40a05028140a0502
            BB_DIAG_A8H1N = 0x02050a142850a040

            if ix == 3:
                cnt = chess.popcount(bb & BB_DIAG_A1H8)
                if cnt > 0:
                    iplanes[cidx, 1] = (6 * 8 + 6) * NNUE_CHANNELS + OFF1
                    ivalues[cidx] = cnt
                    cidx = cidx + 1
                cnt = chess.popcount(bb & BB_DIAG_A8H1)
                if cnt > 0:
                    iplanes[cidx, 1] = (6 * 8 + 7) * NNUE_CHANNELS + OFF1
                    ivalues[cidx] = cnt
                    cidx = cidx + 1
                cnt = chess.popcount(bb & BB_DIAG_A1H8N)
                if cnt > 0:
                    iplanes[cidx, 1] = (7 * 8 + 6) * NNUE_CHANNELS + OFF1
                    ivalues[cidx] = cnt
                    cidx = cidx + 1
                cnt = chess.popcount(bb & BB_DIAG_A8H1N)
                if cnt > 0:
                    iplanes[cidx, 1] = (7 * 8 + 7) * NNUE_CHANNELS + OFF1
                    ivalues[cidx] = cnt
                    cidx = cidx + 1

    iplanes[st:cidx, 0] = pid

    return cidx
예제 #24
0
def material(board):
    name = ""
    name += "K" * chess.popcount(board.kings & board.occupied_co[chess.WHITE])
    name += "Q" * chess.popcount(board.queens & board.occupied_co[chess.WHITE])
    name += "R" * chess.popcount(board.rooks & board.occupied_co[chess.WHITE])
    name += "B" * chess.popcount(board.bishops
                                 & board.occupied_co[chess.WHITE])
    name += "N" * chess.popcount(board.knights
                                 & board.occupied_co[chess.WHITE])
    name += "P" * chess.popcount(board.pawns & board.occupied_co[chess.WHITE])
    name += "v"
    name += "K" * chess.popcount(board.kings & board.occupied_co[chess.BLACK])
    name += "Q" * chess.popcount(board.queens & board.occupied_co[chess.BLACK])
    name += "R" * chess.popcount(board.rooks & board.occupied_co[chess.BLACK])
    name += "B" * chess.popcount(board.bishops
                                 & board.occupied_co[chess.BLACK])
    name += "N" * chess.popcount(board.knights
                                 & board.occupied_co[chess.BLACK])
    name += "P" * chess.popcount(board.pawns & board.occupied_co[chess.BLACK])
    return name
예제 #25
0
def get_online_egtb_move(li, board, game, online_egtb_cfg):
    wb = 'w' if board.turn == chess.WHITE else 'b'
    pieces = chess.popcount(board.occupied)
    if not online_egtb_cfg.get("enabled", False) or game.state[f"{wb}time"] < online_egtb_cfg.get("min_time", 20) * 1000 or board.uci_variant not in ["chess", "antichess", "atomic"] and online_egtb_cfg.get("source", "lichess") == "lichess" or board.uci_variant != "chess" and online_egtb_cfg.get("source", "lichess") == "chessdb" or pieces > online_egtb_cfg.get("max_pieces", 7) or board.castling_rights:
        return None

    quality = online_egtb_cfg.get("move_quality", "best")
    variant = "standard" if board.uci_variant == "chess" else board.uci_variant

    try:
        if online_egtb_cfg.get("source", "lichess") == "lichess":
            name_to_wld = {"loss": -2, "maybe-loss": -1, "blessed-loss": -1, "draw": 0, "cursed-win": 1, "maybe-win": 1, "win": 2}
            max_pieces = 7 if board.uci_variant == "chess" else 6
            if pieces <= max_pieces:
                data = li.api_get(f"http://tablebase.lichess.ovh/{variant}?fen={board.fen()}")
                if quality == "best":
                    move = data["moves"][0]["uci"]
                    wdl = name_to_wld[data["moves"][0]["category"]] * -1
                    dtz = data["moves"][0]["dtz"] * -1
                    dtm = data["moves"][0]["dtm"]
                    if dtm:
                        dtm *= -1
                else:
                    best_wdl = name_to_wld[data["moves"][0]["category"]]
                    possible_moves = list(filter(lambda possible_move: name_to_wld[possible_move["category"]] == best_wdl, data["moves"]))
                    random_move = random.choice(possible_moves)
                    move = random_move["uci"]
                    wdl = name_to_wld[random_move["category"]] * -1
                    dtz = random_move["dtz"] * -1
                    dtm = random_move["dtm"]
                    if dtm:
                        dtm *= -1
                if wdl is not None:
                    logger.info("Got move {} from tablebase.lichess.ovh (wdl: {}, dtz: {}, dtm: {})".format(move, wdl, dtz, dtm))
                    return move
        elif online_egtb_cfg.get("source", "lichess") == "chessdb":

            def score_to_wdl(score):
                if score < -20000:
                    return -2
                elif score < 0:
                    return -1
                elif score == 0:
                    return 0
                elif score <= 20000:
                    return 1
                else:
                    return 2

            if quality == "best":
                data = li.api_get(f"https://www.chessdb.cn/cdb.php?action=querypv&board={board.fen()}&json=1")
                if data["status"] == "ok":
                    score = data["score"]
                    move = data["pv"][0]
                    logger.info("Got move {} from chessdb.cn (wdl: {})".format(move, score_to_wdl(score)))
                    return move
            else:
                data = li.api_get(f"https://www.chessdb.cn/cdb.php?action=queryall&board={board.fen()}&json=1")
                if data["status"] == "ok":
                    best_wdl = score_to_wdl(data["moves"][0]["score"])
                    possible_moves = list(filter(lambda possible_move: score_to_wdl(possible_move["score"]) == best_wdl, data["moves"]))
                    random_move = random.choice(possible_moves)
                    score = random_move["score"]
                    move = random_move["uci"]
                    logger.info("Got move {} from chessdb.cn (wdl: {})".format(move, score_to_wdl(score)))
                    return move
    except Exception:
        pass

    return None
예제 #26
0
 def piece_count(self):
     return chess.popcount(self.board.occupied)
예제 #27
0
 def our_num_pieces_in_king_ring(self):
     our_pieces_mask = self.board.occupied_co[self.our_color]
     return chess.popcount(self._their_king_ring_mask & our_pieces_mask)
예제 #28
0
 def their_king_ring_size(self):
     return chess.popcount(self._their_king_ring_mask)
예제 #29
0
 def _material_balance(self) -> int:
     return (chess.popcount(self.occupied_co[self.turn]) -
             chess.popcount(self.occupied_co[not self.turn]))
예제 #30
0
async def index(request):
    render = {}

    # Setup a board from the given valid FEN or fall back to the default FEN.
    try:
        board = chess.Board(
            request.query.get("fen", DEFAULT_FEN).replace("_", " "))
    except ValueError:
        try:
            board, _ = chess.Board.from_epd(
                request.query.get("fen", DEFAULT_FEN).replace("_", " "))
        except ValueError:
            board = chess.Board(DEFAULT_FEN)
    board.halfmove_clock = 0
    board.fullmoves = 0

    # Get FENs with the current side to move, black and white to move.
    original_turn = board.turn
    board.turn = chess.WHITE
    render["white_fen"] = board.fen()
    board.turn = chess.BLACK
    render["black_fen"] = board.fen()
    board.turn = original_turn
    render["fen"] = fen = board.fen()
    render["board_fen"] = board.board_fen()
    render["check_square"] = chess.SQUARE_NAMES[board.king(
        board.turn)] if board.is_check() else None

    # Mirrored and color swapped FENs for the toolbar.
    render["turn"] = "white" if board.turn == chess.WHITE else "black"
    render["horizontal_fen"] = mirror_horizontal(fen)
    render["vertical_fen"] = mirror_vertical(fen)
    render["swapped_fen"] = swap_colors(fen)
    render["clear_fen"] = clear_fen(fen)
    render["fen_input"] = "" if board.fen() == DEFAULT_FEN else board.fen()

    # Material key for the page title.
    render["material"] = material = chess.syzygy.calc_key(board)
    render["piece_count"] = chess.popcount(board.occupied)

    # Moves are going to be grouped by WDL.
    grouped_moves = {-2: [], -1: [], 0: [], 1: [], 2: [], None: []}

    dtz = None

    if not board.is_valid():
        render["status"] = "Invalid position"
        render["illegal"] = True
    elif board.is_stalemate():
        render["status"] = "Draw by stalemate"
    elif board.is_checkmate():
        if board.turn == chess.WHITE:
            render["status"] = "Black won by checkmate"
            render["winning_side"] = "black"
        else:
            render["status"] = "White won by checkmate"
            render["winning_side"] = "white"
    else:
        # Query backend.
        async with backend_session(request) as session:
            async with session.get(request.app["config"].get(
                    "server", "backend"),
                                   params={"fen": board.fen()}) as res:
                if res.status != 200:
                    return aiohttp.web.Response(status=res.status,
                                                content_type=res.content_type,
                                                body=await res.read(),
                                                charset=res.charset)

                probe = await res.json()

        dtz = probe["dtz"]

        render["blessed_loss"] = probe["wdl"] == -1
        render["cursed_win"] = probe["wdl"] == 1

        # Set status line.
        if board.is_insufficient_material():
            render["status"] = "Draw by insufficient material"
            render["insufficient_material"] = True
        elif probe["wdl"] is None or probe["dtz"] is None:
            render["status"] = "Position not found in tablebases"
        elif probe["wdl"] == 0:
            render["status"] = "Tablebase draw"
        elif probe["dtz"] > 0 and board.turn == chess.WHITE:
            render["status"] = "White is winning with DTZ %d" % (abs(
                probe["dtz"]), )
            render["winning_side"] = "white"
        elif probe["dtz"] < 0 and board.turn == chess.WHITE:
            render["status"] = "White is losing with DTZ %d" % (abs(
                probe["dtz"]), )
            render["winning_side"] = "black"
        elif probe["dtz"] > 0 and board.turn == chess.BLACK:
            render["status"] = "Black is winning with DTZ %d" % (abs(
                probe["dtz"]), )
            render["winning_side"] = "black"
        elif probe["dtz"] < 0 and board.turn == chess.BLACK:
            render["status"] = "Black is losing with DTZ %d" % (abs(
                probe["dtz"]), )
            render["winning_side"] = "white"

        render["frustrated"] = probe["wdl"] is not None and abs(
            probe["wdl"]) == 1

        # Label and group all legal moves.
        for move_info in probe["moves"]:
            board.push_uci(move_info["uci"])
            move_info["fen"] = board.fen()
            board.pop()

            move_info["dtm"] = abs(
                move_info["dtm"]) if move_info["dtm"] is not None else None

            if move_info["checkmate"]:
                move_info["wdl"] = -2
            elif move_info["stalemate"] or move_info["insufficient_material"]:
                move_info["wdl"] = 0

            if move_info["checkmate"]:
                move_info["badge"] = "Checkmate"
            elif move_info["stalemate"]:
                move_info["badge"] = "Stalemate"
            elif move_info["insufficient_material"]:
                move_info["badge"] = "Insufficient material"
            elif move_info["dtz"] == 0:
                move_info["badge"] = "Draw"
            elif move_info["dtz"] is None:
                move_info["badge"] = "Unknown"
            elif move_info["zeroing"]:
                move_info["badge"] = "Zeroing"
            elif move_info["dtz"] < 0:
                move_info["badge"] = "Win with DTZ %d" % (abs(
                    move_info["dtz"]), )
            elif move_info["dtz"] > 0:
                move_info["badge"] = "Loss with DTZ %d" % (abs(
                    move_info["dtz"]), )

            grouped_moves[move_info["wdl"]].append(move_info)

    # Sort winning moves.
    grouped_moves[-2].sort(key=lambda move: move["uci"])
    grouped_moves[-2].sort(key=lambda move: (move["dtm"] is None, move["dtm"]))
    grouped_moves[-2].sort(key=lambda move: (move["dtz"] is None, move["dtz"]),
                           reverse=True)
    grouped_moves[-2].sort(key=lambda move: move["zeroing"], reverse=True)
    grouped_moves[-2].sort(key=lambda move: move["checkmate"], reverse=True)
    render["winning_moves"] = grouped_moves[-2]

    # Sort moves leading to cursed wins.
    grouped_moves[-1].sort(key=lambda move: move["uci"])
    grouped_moves[-1].sort(key=lambda move: (move["dtm"] is None, move["dtm"]))
    grouped_moves[-1].sort(key=lambda move: (move["dtz"] is None, move["dtz"]),
                           reverse=True)
    grouped_moves[-1].sort(key=lambda move: move["zeroing"], reverse=True)
    render["cursed_moves"] = grouped_moves[-1]

    # Sort drawing moves.
    grouped_moves[0].sort(key=lambda move: move["uci"])
    grouped_moves[0].sort(key=lambda move: move["insufficient_material"],
                          reverse=True)
    grouped_moves[0].sort(key=lambda move: move["stalemate"], reverse=True)
    render["drawing_moves"] = grouped_moves[0]

    # Sort moves leading to a blessed loss.
    grouped_moves[1].sort(key=lambda move: move["uci"])
    grouped_moves[1].sort(key=lambda move:
                          (move["dtm"] is not None, move["dtm"]),
                          reverse=True)
    grouped_moves[1].sort(key=lambda move: (move["dtz"] is None, move["dtz"]),
                          reverse=True)
    grouped_moves[1].sort(key=lambda move: move["zeroing"])
    render["blessed_moves"] = grouped_moves[1]

    # Sort losing moves.
    grouped_moves[2].sort(key=lambda move: move["uci"])
    grouped_moves[2].sort(key=lambda move:
                          (move["dtm"] is not None, move["dtm"]),
                          reverse=True)
    grouped_moves[2].sort(key=lambda move: (move["dtz"] is None, move["dtz"]),
                          reverse=True)
    grouped_moves[2].sort(key=lambda move: move["zeroing"])
    render["losing_moves"] = grouped_moves[2]

    # Sort unknown moves.
    grouped_moves[None].sort(key=lambda move: move["uci"])
    render["unknown_moves"] = grouped_moves[None]

    # Stats.
    render["stats"] = prepare_stats(request, material, render["fen"], dtz)

    if "xhr" in request.query:
        template = request.app["jinja"].get_template("xhr-probe.html")
    else:
        template = request.app["jinja"].get_template("index.html")

    return aiohttp.web.Response(text=html_minify(template.render(render)),
                                content_type="text/html")