def make_safe_move(self, move: Union[Move, str]): """ Same as Board.make_move, but validates if the move is legal first (slower). """ if isinstance(move, str): move = Move.from_uci(move) if move in self.legal_moves: self.make_move(move) else: raise IllegalMove(f"Move {move} is illegal.")
def make_move(self, move: Union[Move, str]): """ Moves a piece on the game. Warning: moves are not checked for legality in this function, this is for speed. The consumer of this API should enforce legality by checking Bitboard.legal_moves. """ if isinstance(move, str): move = Move.from_uci(move) self._save() piece = self.piece_at(move.from_square) captured_piece = self.piece_at(move.to_square) if not piece: raise IllegalMove(f"No piece at {move.from_square}") if piece.colour != self.turn: raise IllegalMove(f"Can't move that piece, it's not your turn.") backrank = 7 if self.turn == WHITE else 0 # Castling if a king is moving more than 1 square if piece.type == KING and abs(move.from_square.file - move.to_square.file) > 1: # Move King self.remove_piece(move.from_square) self.place_piece(move.to_square, piece.type, piece.colour) # Move Rook rook_shift = 1 if move.to_square.file < move.from_square.file else -1 # For Queen/Kingside if rook_shift > 0: # Queenside self.remove_piece(Square.from_file_rank( 0, move.to_square.rank)) else: self.remove_piece(Square.from_file_rank( 7, move.to_square.rank)) self.place_piece( Square.from_file_rank(move.to_square.file + rook_shift, move.from_square.rank), ROOK, piece.colour, ) self.repetitions = [] # Reset repetitions when castling elif piece.type == PAWN and move.to_square == self.en_passant_sq: # Take piece by en_passant shift = -8 if piece.colour == WHITE else 8 capture_sq = self.en_passant_sq + shift captured_piece = self.remove_piece(capture_sq) self.remove_piece(move.from_square) self.place_piece(move.to_square, piece.type, piece.colour) elif piece.type == PAWN and move.to_square.rank == backrank: # Promotion self.remove_piece(move.from_square) self.place_piece(move.to_square, move.promotion, piece.colour) # Assume queen for now else: # Regular piece move self.remove_piece(move.from_square) self.place_piece(move.to_square, piece.type, piece.colour) # Set En Passant square if piece.type == PAWN: distance = move.to_square.rank - move.from_square.rank if abs(distance) == 2: if distance > 0: # White pawn goes from low rank to higher self.en_passant_sq = Square( move.from_square + 8) # En Passant square is 1 rank behind else: # Black pawn self.en_passant_sq = Square(move.from_square - 8) else: self.en_passant_sq = None else: self.en_passant_sq = None # Update castling rights if the king or rook move if piece.type in (KING, ROOK): self._update_castling_rights() # Reset halfmove clock if a pawn moved or a piece was captured if piece.type == PAWN or captured_piece: self.halfmove_clock = 0 self.repetitions = [] else: self.halfmove_clock += 1 if self.track_repetitions: self.repetitions.append( self._short_fen) # Imperfect repetition tracking if self.turn == BLACK: # Increment full moves after Black's turn self.fullmoves += 1 self.move_history.append(move) self.turn = not self.turn