def is_legal_king_move(self, move): """ Pseudo-Legal King Moves: one step in any direction :param move: the proposed Move instance :return: True iff Move is legal """ bitboard = make_uint64() moving_to_square = set_bit(bitboard, move.to_sq) for i in [8, -8]: # North-South bitboard |= set_bit(bitboard, np.uint64(move.from_sq + i)) for i in [1, 9, -7]: # East (mask the A file) bitboard |= set_bit(bitboard, np.uint64(move.from_sq + i) & ~np.uint64(File.hexA)) for i in [-1, -9, 7]: # West (mask the H file) bitboard |= set_bit(bitboard, np.uint64(move.from_sq + i) & ~np.uint64(File.hexH)) occupied_squares = { Color.BLACK: self.board.black_pieces_bb, Color.WHITE: self.board.white_pieces_bb, } # remove own piece targets own_piece_targets = occupied_squares[self.color_to_move] & moving_to_square if own_piece_targets: bitboard &= ~own_piece_targets return bitboard
def is_legal_rook_move(self, move: Move) -> bool: """ Pseudo-Legal Rook Moves Implements the classical approach for determining legal sliding-piece moves for rank and file directions. Gets first blocker with forward or reverse bitscan based on the ray direction and XORs the open board ray with the ray continuation from the blocked square. :param move: the proposed Move instance :return: True iff Move is legal """ bitboard = make_uint64() moving_to_square = set_bit(bitboard, move.to_sq) occupied = self.board.occupied_squares_bb # north route north_ray = get_north_ray(bitboard, move.from_sq) intersection = occupied & north_ray if intersection: first_blocker = bitscan_forward(intersection) block_ray = get_northwest_ray(bitboard, first_blocker) north_ray ^= block_ray # east route east_ray = get_east_ray(bitboard, move.from_sq) intersection = occupied & east_ray if intersection: first_blocker = bitscan_forward(intersection) block_ray = get_northeast_ray(bitboard, first_blocker) east_ray ^= block_ray # south route south_ray = get_south_ray(bitboard, move.from_sq) intersection = occupied & south_ray if intersection: first_blocker = bitscan_reverse(intersection) block_ray = get_southwest_ray(bitboard, first_blocker) south_ray ^= block_ray # west route west_ray = get_west_ray(bitboard, move.from_sq) intersection = occupied & west_ray if intersection: first_blocker = bitscan_reverse(intersection) block_ray = get_southeast_ray(bitboard, first_blocker) west_ray ^= block_ray legal_moves = moving_to_square & (north_ray | east_ray | south_ray | west_ray) occupied_squares = { Color.BLACK: self.board.black_pieces_bb, Color.WHITE: self.board.white_pieces_bb, } # remove own piece targets own_piece_targets = occupied_squares[self.color_to_move] & moving_to_square if own_piece_targets: legal_moves &= ~own_piece_targets return legal_moves
def intersects_with_opp_pieces(self, square): bitboard = make_uint64() move_square_bb = set_bit(bitboard, square) occupy_lookup = { Color.WHITE: self.board.black_pieces_bb, Color.BLACK: self.board.white_pieces_bb, } return occupy_lookup[self.color_to_move] & move_square_bb
def is_legal_knight_move(self, move: Move) -> np.uint64: bitboard = make_uint64() legal_knight_moves = self.board.get_knight_attack_from(move.from_sq) if not legal_knight_moves & set_bit(bitboard, move.to_sq): return False # if intersects_with_own_pieces return False if self.intersects_with_own_pieces(move.to_sq): return False # if intersects_with_opp_pieces return True, is_capture => True if self.intersects_with_opp_pieces(move.to_sq): move.is_capture = True return True return True
def is_legal_pawn_move(self, move: Move) -> bool: """ Pseudo-Legal Pawn Moves: - Pawn non-attacks that don't intersect with occupied squares - Pawn attacks that intersect with opponent pieces :param move: the proposed Move instance :return: True iff Move is legal """ bitboard = make_uint64() moving_to_square = set_bit(bitboard, move.to_sq) legal_non_attack_moves = { Color.WHITE: self.board.white_pawn_motion_bbs[move.from_sq], Color.BLACK: self.board.black_pawn_motion_bbs[move.from_sq] } legal_non_attack_moves[self.color_to_move] &= self.board.empty_squares_bb legal_attack_moves = { Color.WHITE: self.board.white_pawn_attack_bbs[move.from_sq], Color.BLACK: self.board.black_pawn_attack_bbs[move.from_sq] } opp_occupied = { Color.WHITE: self.board.black_pieces_bb, Color.BLACK: self.board.white_pieces_bb } legal_attack_moves[self.color_to_move] &= opp_occupied[self.color_to_move] legal_moves = legal_non_attack_moves[self.color_to_move] | legal_attack_moves[self.color_to_move] # Handle en-passant targets if self.en_passant_target: en_passant_bb = set_bit(bitboard, self.en_passant_target) en_passant_move = legal_attack_moves[self.color_to_move] & en_passant_bb legal_moves |= en_passant_move # Handle Captures if moving_to_square & legal_attack_moves[self.color_to_move]: move.is_capture = True # Handle removing own piece targets occupied_squares = { Color.BLACK: self.board.black_pieces_bb, Color.WHITE: self.board.white_pieces_bb, } own_piece_targets = occupied_squares[self.color_to_move] & moving_to_square if own_piece_targets: legal_moves &= ~own_piece_targets # Handle promotion promotion_rank = { Color.WHITE: Rank.hex8, Color.BLACK: Rank.hex1 } if moving_to_square & promotion_rank[self.color_to_move]: move.is_promotion = True return legal_moves & moving_to_square
def __init__(self): # white piece groups self.white_R_bb = make_uint64() self.white_K_bb = make_uint64() self.white_B_bb = make_uint64() self.white_P_bb = make_uint64() self.white_N_bb = make_uint64() self.white_Q_bb = make_uint64() # black piece groups self.black_R_bb = make_uint64() self.black_K_bb = make_uint64() self.black_B_bb = make_uint64() self.black_P_bb = make_uint64() self.black_N_bb = make_uint64() self.black_Q_bb = make_uint64() self.init_pieces() # static bitboards self.knight_attack_bbs = make_knight_attack_bbs() self.bishop_attack_bbs = make_diag_attack_bbs() self.king_attack_bbs = make_king_attack_bbs() self.rook_attack_bbs = make_rook_attack_bbs() self.queen_attack_bbs = make_queen_attack_bbs() self.white_pawn_attack_bbs = make_white_pawn_attack_bbs() self.black_pawn_attack_bbs = make_black_pawn_attack_bbs() self.white_pawn_motion_bbs = make_white_pawn_motion_bbs() self.black_pawn_motion_bbs = make_black_pawn_motion_bbs()