def king_safety(board: Board): wpieces = board.occupied_co[WHITE] bpieces = board.occupied_co[BLACK] wkingloc = lsb(board.kings & wpieces) bkingloc = lsb(board.kings & bpieces) safety: float = 0 safety += sum( chess.square_distance(bkingloc, sq) * 2 for sq in scan_forward(board.queens & wpieces)) safety -= sum( chess.square_distance(wkingloc, sq) * 2 for sq in scan_forward(board.queens & bpieces)) safety += sum( chess.square_distance(bkingloc, sq) / 2 for sq in scan_forward(board.bishops & wpieces)) safety -= sum( chess.square_distance(wkingloc, sq) / 2 for sq in scan_forward(board.bishops & bpieces)) safety += sum( chess.square_distance(bkingloc, sq) / 2 for sq in scan_forward(board.rooks & wpieces)) safety -= sum( chess.square_distance(wkingloc, sq) / 2 for sq in scan_forward(board.rooks & bpieces)) safety += sum( chess.square_distance(bkingloc, sq) for sq in scan_forward(board.knights & wpieces)) safety -= sum( chess.square_distance(wkingloc, sq) for sq in scan_forward(board.knights & bpieces)) return safety
def execute(position): board = chess.Board(fen=position) white_running_total = 0 for i in chess.scan_forward(board.occupied_co[True]): white_running_total += sum( list(map(int, bin(board.attackers_mask(True, i))[2:]))) black_running_total = 0 for i in chess.scan_forward(board.occupied_co[False]): black_running_total += sum( list(map(int, bin(board.attackers_mask(False, i))[2:]))) return (black_running_total, white_running_total)
def _locked_pawns(fen): """ A pawn is locked iff. it cannot move forward because opponent's pawn occupy the square in front and cannot move diagonally, i.e. make a capture, because no such capture is available, regardless of whose turn is it. """ board = chess.Board(fen) board_opposite_turn = board.copy() board_opposite_turn.turn = not board.turn locked_pawns = set() for square in chess.scan_forward(board.pawns): color = board.piece_at(square).color square_in_front = square + 8 if color == chess.WHITE else square - 8 if not (0 <= square_in_front < 64): continue piece_in_front = board.piece_at(square_in_front) from_mask = chess.BB_SQUARES[square] if (piece_in_front is not None and piece_in_front.piece_type == chess.PAWN and piece_in_front.color != color and not list(board.generate_legal_captures(from_mask)) and not list( board_opposite_turn.generate_legal_captures(from_mask)) ): locked_pawns.add(square) return locked_pawns
def _attacked_for_king(self, path, occupied): # Can castle onto attacked squares if they are connected to the # enemy king. enemy_kings = self.kings & self.occupied_co[not self.turn] for enemy_king in chess.scan_forward(enemy_kings): path &= ~chess.BB_KING_ATTACKS[enemy_king] return super()._attacked_for_king(path, occupied)
def space(board: Board): w_area = BB_RANK_1 | BB_RANK_2 | BB_RANK_3 | BB_RANK_4 b_area = BB_RANK_8 | BB_RANK_7 | BB_RANK_6 | BB_RANK_5 space = 0 space -= sum( popcount(BB_PAWN_ATTACKS[WHITE][sq] & b_area) for sq in scan_forward(board.pawns & board.occupied_co[WHITE])) space += sum( popcount(BB_PAWN_ATTACKS[BLACK][sq] & w_area) for sq in scan_forward(board.pawns & board.occupied_co[BLACK])) space -= sum( popcount(BB_KNIGHT_ATTACKS[sq] & b_area) for sq in scan_forward(board.knights & board.occupied_co[WHITE])) space += sum( popcount(BB_KNIGHT_ATTACKS[sq] & w_area) for sq in scan_forward(board.knights & board.occupied_co[BLACK])) return space
def _attacked_for_king(self, path, occupied): # Can castle onto attacked squares if they are connected to the # enemy king. enemy_kings = self.kings & self.occupied_co[not self.turn] for enemy_king in chess.scan_forward(enemy_kings): path &= ~chess.BB_KING_ATTACKS[enemy_king] return super(AtomicBoard, self)._attacked_for_king(path, occupied)
def generate_pseudo_legal_drops( self, to_mask: chess.Bitboard = chess.BB_ALL) -> Iterator[chess.Move]: for to_square in chess.scan_forward(to_mask & ~self.occupied): for pt, count in self.pockets[self.turn].pieces.items(): if count and (pt != chess.PAWN or not chess.BB_BACKRANKS & chess.BB_SQUARES[to_square]): yield chess.Move(to_square, to_square, drop=pt)
def piece_attack_counts(board: Board): white_pawn_attacks = sum( popcount(BB_PAWN_ATTACKS[WHITE][sq] & board.occupied_co[BLACK]) for sq in scan_forward(board.pawns & board.occupied_co[WHITE])) black_pawn_attacks = sum( popcount(BB_PAWN_ATTACKS[BLACK][sq] & board.occupied_co[WHITE]) for sq in scan_forward(board.pawns & board.occupied_co[BLACK])) white_knight_attacks = sum( popcount(BB_KNIGHT_ATTACKS[sq] & board.occupied_co[BLACK]) for sq in scan_forward(board.knights & board.occupied_co[WHITE])) black_knight_attacks = sum( popcount(BB_KNIGHT_ATTACKS[sq] & board.occupied_co[WHITE]) for sq in scan_forward(board.knights & board.occupied_co[BLACK])) white_bishop_attacks = sum( popcount( board.attacks_mask(sq) & board.occupied_co[BLACK] & (board.kings | board.queens | board.rooks)) for sq in scan_forward(board.bishops & board.occupied_co[WHITE])) black_bishop_attacks = sum( popcount( board.attacks_mask(sq) & board.occupied_co[WHITE] & (board.kings | board.queens | board.rooks)) for sq in scan_forward(board.bishops & board.occupied_co[BLACK])) white_rook_attacks = sum( popcount( board.attacks_mask(sq) & board.occupied_co[BLACK] & (board.kings | board.queens)) for sq in scan_forward(board.bishops & board.occupied_co[WHITE])) black_rook_attacks = sum( popcount( board.attacks_mask(sq) & board.occupied_co[WHITE] & (board.kings | board.queens)) for sq in scan_forward(board.bishops & board.occupied_co[BLACK])) white_queen_attacks = sum( popcount( board.attacks_mask(sq) & board.occupied_co[BLACK] & board.kings) for sq in scan_forward(board.bishops & board.occupied_co[WHITE])) black_queen_attacks = sum( popcount( board.attacks_mask(sq) & board.occupied_co[WHITE] & board.kings) for sq in scan_forward(board.bishops & board.occupied_co[BLACK])) black = black_pawn_attacks + black_knight_attacks + black_bishop_attacks + black_rook_attacks + black_queen_attacks white = white_pawn_attacks + white_knight_attacks + white_bishop_attacks + white_rook_attacks + white_queen_attacks return black - white
def apply(self, vec, board, move): """ Should be called prior to pushing move to board. Applies the move to the vector. """ # Remove from square. piece_type = board.piece_type_at(move.from_square) color = board.turn vec -= self.piece_to_vec[piece_type, color, move.from_square] # Update castling rights. old_castling_rights = board.clean_castling_rights() new_castling_rights = old_castling_rights & ~chess.BB_SQUARES[ move.to_square] & ~chess.BB_SQUARES[move.from_square] if piece_type == chess.KING: new_castling_rights &= ~chess.BB_RANK_1 if color else ~chess.BB_RANK_8 # Castling rights can only have been removed for sq in chess.scan_forward(old_castling_rights ^ new_castling_rights): vec -= self.castling[sq] # Remove pawns captured en passant. if piece_type == chess.PAWN and move.to_square == board.ep_square: down = -8 if board.turn == chess.WHITE else 8 capture_square = board.ep_square + down vec -= self.piece_to_vec[chess.PAWN, not board.turn, capture_square] # Move rook during castling. if piece_type == chess.KING: if move.from_square == chess.E1: if move.to_square == chess.G1: vec -= self.piece_to_vec[chess.ROOK, color, chess.H1] vec += self.piece_to_vec[chess.ROOK, color, chess.F1] if move.to_square == chess.C1: vec -= self.piece_to_vec[chess.ROOK, color, chess.A1] vec += self.piece_to_vec[chess.ROOK, color, chess.D1] if move.from_square == chess.E8: if move.to_square == chess.G8: vec -= self.piece_to_vec[chess.ROOK, color, chess.H8] vec += self.piece_to_vec[chess.ROOK, color, chess.F8] if move.to_square == chess.C8: vec -= self.piece_to_vec[chess.ROOK, color, chess.A8] vec += self.piece_to_vec[chess.ROOK, color, chess.D8] # Capture captured_piece_type = board.piece_type_at(move.to_square) if captured_piece_type: vec -= self.piece_to_vec[captured_piece_type, not color, move.to_square] # Put the piece on the target square. vec += self.piece_to_vec[move.promotion or piece_type, color, move.to_square] return vec
def getMobilityOfPieces(board, piece_type, color): # PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING 1-6 pcs_mask = board.pieces_mask(piece_type, color) # 返回棋子类型为piece_type的color方棋子的位棋盘 pcs_position = chess.scan_forward(pcs_mask) attack_list = [] for i in pcs_position: atks_mask = board.attacks_mask(i) atks_num = chess.popcount(atks_mask) overlap_mask = atks_mask & board.occupied_co[color] ol_num = chess.popcount(overlap_mask) attack_list.append(atks_num - ol_num) return attack_list
def _push_capture(self, move, capture_square, piece_type, was_promoted): # Explode the capturing piece. self._remove_piece_at(move.to_square) # Explode all non pawns around. explosion_radius = chess.BB_KING_ATTACKS[move.to_square] & ~self.pawns for explosion in chess.scan_forward(explosion_radius): self._remove_piece_at(explosion) # Destroy castling rights. self.castling_rights &= ~explosion_radius
def board_to_words(board, occ=False): for s, p in board.piece_map().items(): yield f'{chess.SQUARE_NAMES[s]}{p.symbol()}' if board.castling_rights & chess.BB_H1: yield 'H1-C' if board.castling_rights & chess.BB_H8: yield 'H8-C' if board.castling_rights & chess.BB_A1: yield 'A1-C' if board.castling_rights & chess.BB_A8: yield 'A8-C' if occ: for square in chess.scan_forward(board.occupied): yield f'{chess.SQUARE_NAMES[square]}-Occ'
def is_variant_end(self) -> bool: if not self.kings & chess.BB_RANK_8: return False black_kings = self.kings & self.occupied_co[chess.BLACK] if self.turn == chess.WHITE or black_kings & chess.BB_RANK_8 or not black_kings: return True # White has reached the backrank. The game is over if black can not # also reach the backrank on the next move. Check if there are any # safe squares for the king. black_king = chess.msb(black_kings) targets = chess.BB_KING_ATTACKS[black_king] & chess.BB_RANK_8 & ~self.occupied_co[chess.BLACK] return all(self.attackers_mask(chess.WHITE, target) for target in chess.scan_forward(targets))
def _generate_pseudo_legal_drops_vp( self, to_mask: chess.Bitboard = chess.BB_ALL, virtual_pocket: Optional[CrazyhousePocket] = None ) -> Iterator[chess.Move]: pocket = self.pockets[ self.turn] if virtual_pocket is None else virtual_pocket for to_square in chess.scan_forward(to_mask & ~self.occupied): for pt, count in pocket.pieces.items(): if count and (pt != chess.PAWN or not chess.BB_BACKRANKS & chess.BB_SQUARES[to_square]): yield chess.Move(to_square, to_square, drop=pt, board_id=self.board_id)
def _push_capture(self, move: chess.Move, capture_square: chess.Square, piece_type: chess.PieceType, was_promoted: bool) -> None: explosion_radius = chess.BB_KING_ATTACKS[move.to_square] & ~self.pawns # Destroy castling rights. self.castling_rights &= ~explosion_radius if explosion_radius & self.kings & self.occupied_co[chess.WHITE] & ~self.promoted: self.castling_rights &= ~chess.BB_RANK_1 if explosion_radius & self.kings & self.occupied_co[chess.BLACK] & ~self.promoted: self.castling_rights &= ~chess.BB_RANK_8 # Explode the capturing piece. self._remove_piece_at(move.to_square) # Explode all non pawns around. for explosion in chess.scan_forward(explosion_radius): self._remove_piece_at(explosion)
def is_variant_end(self): if not self.kings & chess.BB_RANK_8: return False if self.turn == chess.WHITE or self.kings & self.occupied_co[chess.BLACK] & chess.BB_RANK_8: return True black_kings = self.kings & self.occupied_co[chess.BLACK] if not black_kings: return True black_king = chess.msb(black_kings) # White has reached the backrank. The game is over if black can not # also reach the backrank on the next move. Check if there are any # safe squares for the king. targets = chess.BB_KING_ATTACKS[black_king] & chess.BB_RANK_8 return all(self.attackers_mask(chess.WHITE, target) for target in chess.scan_forward(targets))
def get_pieces_positions_by_type(self, piece_type: str, color: str = None) -> list: """Parse the bitboard representation to get pieces positions on the board Args: piece_type (str): the name of the piece in uppercase color (str, optional): WHITE or BLACK. Defaults to None. Raises: Exception: if the name of the piece is not recognized (practical to debug) Returns: list: The position (between 0 and 63) of the piece type selected """ # Prepare binary representation of pieces piece_type = piece_type.upper() if piece_type == "BISHOP": pieces = self.bishops elif piece_type == "PAWN": pieces = self.pawns elif piece_type == "ROOK": pieces = self.rooks elif piece_type == "KNIGHT": pieces = self.knights elif piece_type == "QUEEN": pieces = self.queens elif piece_type == "KING": pieces = self.kings else: raise Exception(f"Piece type {piece_type} is not among {PIECES}") # Prepare binary color mask if color is None: mask = self.occupied else: if isinstance(color, str): color = 0 if color.upper() == "WHITE" else 1 mask = self.occupied_co[1 - color] return list(scan_forward(pieces & mask))
def _kings_connected(self) -> bool: white_kings = self.kings & self.occupied_co[chess.WHITE] black_kings = self.kings & self.occupied_co[chess.BLACK] return any(chess.BB_KING_ATTACKS[sq] & black_kings for sq in chess.scan_forward(white_kings))
def generate_pseudo_legal_drops(self, to_mask=chess.BB_ALL): for to_square in chess.scan_forward(to_mask & ~self.occupied): for pt, count in self.pockets[self.turn].pieces.items(): if count and (pt != chess.PAWN or not chess.BB_BACKRANKS & chess.BB_SQUARES[to_square]): yield chess.Move(to_square, to_square, drop=pt)
def pst_eval(board: Board) -> int: white = board.occupied_co[WHITE] black = board.occupied_co[BLACK] return sum( chain((mg_pst[p][i] for i in scan_forward(board.pawns & black)), (-mg_pst[P][i] for i in scan_forward(board.pawns & white)), (mg_pst[n][i] for i in scan_forward(board.knights & black)), (-mg_pst[N][i] for i in scan_forward(board.knights & white)), (mg_pst[b][i] for i in scan_forward(board.bishops & black)), (-mg_pst[B][i] for i in scan_forward(board.bishops & white)), (mg_pst[r][i] for i in scan_forward(board.rooks & black)), (-mg_pst[R][i] for i in scan_forward(board.rooks & white)), (mg_pst[q][i] for i in scan_forward(board.queens & black)), (-mg_pst[Q][i] for i in scan_forward(board.queens & white)), (mg_pst[k][i] for i in scan_forward(board.kings & black)), (-mg_pst[K][i] for i in scan_forward(board.kings & white))))
def _kings_connected(self): white_kings = self.kings & self.occupied_co[chess.WHITE] black_kings = self.kings & self.occupied_co[chess.BLACK] return any(chess.BB_KING_ATTACKS[sq] & black_kings for sq in chess.scan_forward(white_kings))