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
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
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
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))
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
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
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
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
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
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
def piece_count(self, board): return chess.popcount(board.occupied)
def score_position(board): if chess.popcount(board.occupied) > 5: return return tablebase.probe_dtz(board)
def material_count(board): return chess.popcount(board.occupied)
def material_count(board: Board) -> int: """ Count the number of pieces on the board """ return popcount(board.occupied)
def material_count(self): return chess.popcount(self.position.occupied)
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)
def get_psqt_index(self, board: chess.Board): return (chess.popcount(board.occupied) - 1) // 4
def _material_balance(self): return (chess.popcount(self.occupied_co[self.turn]) - chess.popcount(self.occupied_co[not self.turn]))
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")
def our_piece_count(self): return chess.popcount(self.board.occupied_co(self.board.current_color))
def their_piece_count(self): return chess.popcount(self.board.occupied_co(self.board.other_color))
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
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
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
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
def piece_count(self): return chess.popcount(self.board.occupied)
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)
def their_king_ring_size(self): return chess.popcount(self._their_king_ring_mask)
def _material_balance(self) -> int: return (chess.popcount(self.occupied_co[self.turn]) - chess.popcount(self.occupied_co[not self.turn]))
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")