def is_dead(self) -> bool: white_queens = list(split(self.pieces["Q"])) white_rooks = list(split(self.pieces["R"])) white_bishops = list(split(self.pieces["B"])) white_knights = list(split(self.pieces["N"])) white_pawns = list(split(self.pieces["P"])) black_queens = list(split(self.pieces["q"])) black_rooks = list(split(self.pieces["r"])) black_bishops = list(split(self.pieces["b"])) black_knights = list(split(self.pieces["n"])) black_pawns = list(split(self.pieces["p"])) number_of_white_pieces = (len(white_queens) + len(white_rooks) + len(white_bishops) + len(white_knights) + len(white_pawns)) number_of_black_pieces = (len(black_queens) + len(black_rooks) + len(black_bishops) + len(black_knights) + len(black_pawns)) # king against king if number_of_white_pieces + number_of_black_pieces == 0: return True # king against king and bishop if (number_of_white_pieces == 0 and number_of_black_pieces == 1 and len(black_bishops) == 1): return True if (number_of_black_pieces == 0 and number_of_white_pieces == 1 and len(white_bishops) == 1): return True # king against king and knight if (number_of_white_pieces == 0 and number_of_black_pieces == 1 and len(black_knights) == 1): return True if (number_of_black_pieces == 0 and number_of_white_pieces == 1 and len(white_knights) == 1): return True # king and bishop against king and bishop, with both bishops on squares of the same color if (number_of_white_pieces == 1 and number_of_black_pieces == 1 and len(white_bishops) == 1 and len(black_bishops) == 1): is_white_bishop_on_white_square = (white_bishops[0] & 0xAA55_AA55_AA55_AA55) == 0 is_black_bishop_on_white_square = (black_bishops[0] & 0xAA55_AA55_AA55_AA55) == 0 return is_white_bishop_on_white_square == is_black_bishop_on_white_square return False
def checkers(self, player: bool, king: int) -> Iterable[int]: queen = self.pieces["Q" if player else "q"] rook = self.pieces["R" if player else "r"] bishop = self.pieces["B" if player else "b"] knight = self.pieces["N" if player else "n"] pawn = self.pieces["P" if player else "p"] queen_and_rook = queen | rook queen_and_bishop = queen | bishop north_pieces = NORTH_RAY[king] & self.all_pieces south_pieces = SOUTH_RAY[king] & self.all_pieces west_pieces = WEST_RAY[king] & self.all_pieces east_pieces = EAST_RAY[king] & self.all_pieces north_west_pieces = NORTH_WEST_RAY[king] & self.all_pieces south_west_pieces = SOUTH_WEST_RAY[king] & self.all_pieces north_east_pieces = NORTH_EAST_RAY[king] & self.all_pieces south_east_pieces = SOUTH_EAST_RAY[king] & self.all_pieces return split( (NORTH_ATTACKS[king][north_pieces] & queen_and_rook) | (SOUTH_ATTACKS[king][south_pieces] & queen_and_rook) | (WEST_ATTACKS[king][west_pieces] & queen_and_rook) | (EAST_ATTACKS[king][east_pieces] & queen_and_rook) | (NORTH_WEST_ATTACKS[king][north_west_pieces] & queen_and_bishop) | (SOUTH_WEST_ATTACKS[king][south_west_pieces] & queen_and_bishop) | (NORTH_EAST_ATTACKS[king][north_east_pieces] & queen_and_bishop) | (SOUTH_EAST_ATTACKS[king][south_east_pieces] & queen_and_bishop) | (KNIGHT_MOVES[king] & knight) | (PAWN_ATTACKS[player][king] & pawn))
def get_queen_moves(from_square: int) -> Iterable[int]: moveable_squares = ( NORTH_RAY[from_square] | SOUTH_RAY[from_square] | WEST_RAY[from_square] | EAST_RAY[from_square] | NORTH_WEST_RAY[from_square] | NORTH_EAST_RAY[from_square] | SOUTH_WEST_RAY[from_square] | SOUTH_EAST_RAY[from_square] ) return split(moveable_squares)
def legal_moves(self) -> Iterable[Move]: friendly_pieces = (self.position.white_pieces if self.player else self.position.black_pieces) enemy_pieces = (self.position.black_pieces if self.player else self.position.white_pieces) empty_squares = 0xFFFF_FFFF_FFFF_FFFF ^ self.position.all_pieces attacked_squares = self.position.attacked_squares( player=not self.player, exclude_king=True) king = self.position.pieces["K" if self.player else "k"] king_moves = KING_MOVES[king] & (0xFFFF_FFFF_FFFF_FFFF ^ attacked_squares) king_moves ^= king_moves & friendly_pieces for to_square in split(king_moves): yield Move( player=self.player, piece="K", from_square=king, to_square=to_square, ) attackers = list( self.position.checkers(player=not self.player, king=king)) number_of_attackers = len(attackers) if number_of_attackers > 1: # Multiple pieces are giving check, so the king has to move return capture_mask = 0xFFFF_FFFF_FFFF_FFFF push_mask = 0xFFFF_FFFF_FFFF_FFFF if number_of_attackers == 1: attacker = attackers[0] capture_mask = attacker if (attacker & self.position.pieces["n" if self.player else "N"] != 0) or (attacker & self.position.pieces["p" if self.player else "P"] != 0): # checked by knight or pawn, this can't be blocked push_mask = 0 else: # checked by slider, this can be blocked push_mask = (NORTH_MOVES[king].get(attacker, 0) | SOUTH_MOVES[king].get(attacker, 0) | WEST_MOVES[king].get(attacker, 0) | EAST_MOVES[king].get(attacker, 0) | NORTH_WEST_MOVES[king].get(attacker, 0) | NORTH_EAST_MOVES[king].get(attacker, 0) | SOUTH_WEST_MOVES[king].get(attacker, 0) | SOUTH_EAST_MOVES[king].get(attacker, 0)) capture_or_push_mask = capture_mask | push_mask enemy_queens = self.position.pieces["q" if self.player else "Q"] enemy_queens_and_rooks = ( enemy_queens | self.position.pieces["r" if self.player else "R"]) enemy_queens_and_bishops = ( enemy_queens | self.position.pieces["b" if self.player else "B"]) for from_square in split( self.position.pieces["Q" if self.player else "q"]): moveable_squares = ( capture_or_push_mask & (get_rank_and_file_moves(self.position.all_pieces, enemy_pieces, from_square) | get_diagonal_moves(self.position.all_pieces, enemy_pieces, from_square)) & self.position.pinned_movement( square=from_square, king=king, enemy_queens_and_rooks=enemy_queens_and_rooks, enemy_queens_and_bishops=enemy_queens_and_bishops, )) for to_square in split(moveable_squares): yield Move( player=self.player, piece="Q", from_square=from_square, to_square=to_square, ) for from_square in split( self.position.pieces["R" if self.player else "r"]): moveable_squares = ( capture_or_push_mask & get_rank_and_file_moves(self.position.all_pieces, enemy_pieces, from_square) & self.position.pinned_movement( square=from_square, king=king, enemy_queens_and_rooks=enemy_queens_and_rooks, enemy_queens_and_bishops=enemy_queens_and_bishops, )) for to_square in split(moveable_squares): yield Move( player=self.player, piece="R", from_square=from_square, to_square=to_square, ) for from_square in split( self.position.pieces["B" if self.player else "b"]): moveable_squares = ( capture_or_push_mask & get_diagonal_moves(self.position.all_pieces, enemy_pieces, from_square) & self.position.pinned_movement( square=from_square, king=king, enemy_queens_and_rooks=enemy_queens_and_rooks, enemy_queens_and_bishops=enemy_queens_and_bishops, )) for to_square in split(moveable_squares): yield Move( player=self.player, piece="B", from_square=from_square, to_square=to_square, ) for from_square in split( self.position.pieces["N" if self.player else "n"]): moveable_squares = ( capture_or_push_mask & KNIGHT_MOVES[from_square] & (KNIGHT_MOVES[from_square] ^ friendly_pieces) & self.position.pinned_movement( square=from_square, king=king, enemy_queens_and_rooks=enemy_queens_and_rooks, enemy_queens_and_bishops=enemy_queens_and_bishops, )) for to_square in split(moveable_squares): yield Move( player=self.player, piece="N", from_square=from_square, to_square=to_square, ) for from_square in split( self.position.pieces["P" if self.player else "p"]): pinned_movement = self.position.pinned_movement( square=from_square, king=king, enemy_queens_and_rooks=enemy_queens_and_rooks, enemy_queens_and_bishops=enemy_queens_and_bishops, ) to_square = (PAWN_SINGLE_MOVES[self.player][from_square] & empty_squares & pinned_movement & push_mask) if to_square != 0: yield Move( player=self.player, piece="P", from_square=from_square, to_square=to_square, ) attacks = [ p & enemy_pieces & pinned_movement & capture_mask for p in PAWN_ATTACK_MOVES[self.player][from_square] ] for to_square in attacks: if to_square == 0: continue yield Move( player=self.player, piece="P", from_square=from_square, to_square=to_square, ) to_square = (PAWN_DOUBLE_MOVES[self.player][from_square] & empty_squares & (get_top_square(empty_squares) if self.player else get_bottom_square(empty_squares)) & pinned_movement & push_mask) if to_square: yield Move( player=self.player, piece="P", from_square=from_square, to_square=to_square, en_passant_square=get_bottom_square(to_square) if self.player else get_top_square(to_square), ) to_square = (PAWN_EN_PASSANT_CAPTURES[self.player][from_square] & self.en_passant_square & pinned_movement & (get_top_square(capture_mask) if self.player else get_bottom_square(capture_mask))) if to_square: move = Move( player=self.player, piece="P", from_square=from_square, to_square=to_square, is_capturing_en_passant=True, ) position = self.position.move(move)[0] if not position.is_check(self.player): yield move single_move_promotions = [ p & empty_squares & pinned_movement & push_mask for p in PAWN_SINGLE_MOVES_PROMOTION[self.player][from_square] ] attack_promotions = [ p & enemy_pieces & pinned_movement & capture_mask for p in PAWN_ATTACK_MOVES_PROMOTION[self.player][from_square] ] for to_square in single_move_promotions + attack_promotions: if to_square == 0: continue yield Move( player=self.player, piece="P", from_square=from_square, to_square=to_square, is_promoting_to="Q", ) yield Move( player=self.player, piece="P", from_square=from_square, to_square=to_square, is_promoting_to="R", ) yield Move( player=self.player, piece="P", from_square=from_square, to_square=to_square, is_promoting_to="B", ) yield Move( player=self.player, piece="P", from_square=from_square, to_square=to_square, is_promoting_to="N", ) can_castle_kingside = ( self.possible_castles["K" if self.player else "k"] and (self.position.all_pieces & (0x0000_0000_0000_0006 if self.player else 0x0600_0000_0000_0000)) == 0 and (attacked_squares & (0x0000_0000_0000_000E if self.player else 0x0E00_0000_0000_0000) == 0)) if can_castle_kingside: yield Move( player=self.player, piece="K", from_square=0x0000_0000_0000_0008 if self.player else 0x0800_0000_0000_0000, to_square=0x0000_0000_0000_0002 if self.player else 0x0200_0000_0000_0000, is_castling="K" if self.player else "k", ) can_castle_queenside = ( self.possible_castles["Q" if self.player else "q"] and (self.position.all_pieces & (0x0000_0000_0000_0070 if self.player else 0x7000_0000_0000_0000)) == 0 and (attacked_squares & (0x0000_0000_0000_0038 if self.player else 0x3800_0000_0000_0000) == 0)) if can_castle_queenside: yield Move( player=self.player, piece="K", from_square=0x0000_0000_0000_0008 if self.player else 0x0800_0000_0000_0000, to_square=0x0000_0000_0000_0020 if self.player else 0x2000_0000_0000_0000, is_castling="Q" if self.player else "q", )
def attacked_squares(self, player: bool, exclude_king: bool = False) -> int: all_pieces = self.all_pieces if exclude_king: all_pieces ^= self.pieces[ "k" if player else "K"] if exclude_king else 0 attacked = KING_MOVES[self.pieces["K" if player else "k"]] for queen in split(self.pieces["Q" if player else "q"]): north_pieces = NORTH_RAY[queen] & all_pieces attacked |= (NORTH_MOVES[queen][north_pieces] | NORTH_ATTACKS[queen][north_pieces]) south_pieces = SOUTH_RAY[queen] & all_pieces attacked |= (SOUTH_MOVES[queen][south_pieces] | SOUTH_ATTACKS[queen][south_pieces]) west_pieces = WEST_RAY[queen] & all_pieces attacked |= (WEST_MOVES[queen][west_pieces] | WEST_ATTACKS[queen][west_pieces]) east_pieces = EAST_RAY[queen] & all_pieces attacked |= (EAST_MOVES[queen][east_pieces] | EAST_ATTACKS[queen][east_pieces]) north_west_pieces = NORTH_WEST_RAY[queen] & all_pieces attacked |= (NORTH_WEST_MOVES[queen][north_west_pieces] | NORTH_WEST_ATTACKS[queen][north_west_pieces]) north_east_pieces = NORTH_EAST_RAY[queen] & all_pieces attacked |= (NORTH_EAST_MOVES[queen][north_east_pieces] | NORTH_EAST_ATTACKS[queen][north_east_pieces]) south_west_pieces = SOUTH_WEST_RAY[queen] & all_pieces attacked |= (SOUTH_WEST_MOVES[queen][south_west_pieces] | SOUTH_WEST_ATTACKS[queen][south_west_pieces]) south_east_pieces = SOUTH_EAST_RAY[queen] & all_pieces attacked |= (SOUTH_EAST_MOVES[queen][south_east_pieces] | SOUTH_EAST_ATTACKS[queen][south_east_pieces]) for rook in split(self.pieces["R" if player else "r"]): north_pieces = NORTH_RAY[rook] & all_pieces attacked |= (NORTH_MOVES[rook][north_pieces] | NORTH_ATTACKS[rook][north_pieces]) south_pieces = SOUTH_RAY[rook] & all_pieces attacked |= (SOUTH_MOVES[rook][south_pieces] | SOUTH_ATTACKS[rook][south_pieces]) west_pieces = WEST_RAY[rook] & all_pieces attacked |= WEST_MOVES[rook][west_pieces] | WEST_ATTACKS[rook][ west_pieces] east_pieces = EAST_RAY[rook] & all_pieces attacked |= EAST_MOVES[rook][east_pieces] | EAST_ATTACKS[rook][ east_pieces] for bishop in split(self.pieces["B" if player else "b"]): north_west_pieces = NORTH_WEST_RAY[bishop] & all_pieces attacked |= (NORTH_WEST_MOVES[bishop][north_west_pieces] | NORTH_WEST_ATTACKS[bishop][north_west_pieces]) north_east_pieces = NORTH_EAST_RAY[bishop] & all_pieces attacked |= (NORTH_EAST_MOVES[bishop][north_east_pieces] | NORTH_EAST_ATTACKS[bishop][north_east_pieces]) south_west_pieces = SOUTH_WEST_RAY[bishop] & all_pieces attacked |= (SOUTH_WEST_MOVES[bishop][south_west_pieces] | SOUTH_WEST_ATTACKS[bishop][south_west_pieces]) south_east_pieces = SOUTH_EAST_RAY[bishop] & all_pieces attacked |= (SOUTH_EAST_MOVES[bishop][south_east_pieces] | SOUTH_EAST_ATTACKS[bishop][south_east_pieces]) for knight in split(self.pieces["N" if player else "n"]): attacked |= KNIGHT_MOVES[knight] for pawn in split(self.pieces["P" if player else "p"]): for s in PAWN_ATTACK_MOVES[player][pawn]: attacked |= s for s in PAWN_ATTACK_MOVES_PROMOTION[player][pawn]: attacked |= s return attacked
(0x0000_0000_0000_8000, 0x0000_0000_0000_0040), (0x0000_0000_0000_8000, 0x0000_0000_0000_0080), ] move_for_output_index = [] for from_square in split(0xFFFF_FFFF_FFFF_FFFF): for to_square in get_queen_moves(from_square): move_for_output_index += [ Move( player=True, piece="Q", from_square=from_square, to_square=to_square ).id() ] for from_square in split(0xFFFF_FFFF_FFFF_FFFF): for to_square in split(KNIGHT_MOVES[from_square]): move_for_output_index += [ Move( player=True, piece="N", from_square=from_square, to_square=to_square ).id() ] for (from_square, to_square) in white_promotion_moves: move_for_output_index += [ Move( player=True, piece="P", from_square=from_square, to_square=to_square, is_promoting_to="Q", ).id(),