chessengineimpl.java

来自「chess 一个beguanyu国际象棋的一个Java源码」· Java 代码 · 共 1,221 行 · 第 1/3 页

JAVA
1,221
字号
    /**     * Get the current opening book.     *     * @return The current opening book.     */    private final OpeningBook getOpeningBook() {	return _openingBook;    }    /**     * Set a new opening book.     *     * @param book The new opening book.     */    private final void setOpeningBook( OpeningBook book) {	_openingBook = book;    }    /**     * Get the flag to indicate, if we are still in the opening book.     *     * @return true, if we are still in the opening book. False otherwise.     */    private final boolean inOpeningBook() {	return _inOpeningBook;    }    /**     * Set the flag to indicate, if we are still in the opening book.     *     * @param inBook true, if we are still in the opening book. False otherwise.     */    private final void setInOpeningBook( boolean inBook) {	_inOpeningBook = inBook;    }    /**     * Start a new thread to search for a ply.     */    public void start() {	if( _searchThread == null) {	    setSearchStop( false);	    _searchThread = new Thread( this);	    _searchThread.start();	}    }    /**     * Compute the best ply for the current position.     *     * @return The best known ply for the current position.     */    public Ply computeBestPly() {	_bestPly = null;  // Remove ply from last computation.	long startTime = System.currentTimeMillis();	if( inOpeningBook()) {	    _bestPly = getOpeningBook().getOpeningBookPly();	    if( _bestPly == null) {  // If there's no ply in the opening book.		setInOpeningBook( false);	    }	}	if( _bestPly == null) {  // If we don't have a ply yet	    start();	    try {		Thread.sleep( getMaximumSearchTime());		setSearchStop( true);		// if( this.bFixedTime == false) {		    _searchThread.join();      // Wait for the search thread to end the search at this search depth.		// }		_searchThread = null;      // Remove the thread, so it can be recreated for the next move.	    } catch( InterruptedException ignored) {	    }	}	long usedTime = System.currentTimeMillis() - startTime;	if( _bestPly != null) {	    if ( this._enginePanel != null ) {		StringBuffer sOut = new StringBuffer();		sOut.append( "Best ply: ");		sOut.append( _bestPly.getPly().toString());		sOut.append( " with score ");		sOut.append( _bestPly.getScore());		sOut.append( " and search depth ");		sOut.append( getSearchDepth());		this._enginePanel.modifyText( sOut.toString());		sOut = new StringBuffer();		sOut.append( "Analyzed boards: ");		sOut.append( getAnalyzedBoards());		sOut.append( " in ");		sOut.append( usedTime);		sOut.append( " ms");		this._enginePanel.modifyText( sOut.toString());	    }	    if ( this._statusPanel != null )		this._statusPanel.setStatusText( "Your turn..." );	    return _bestPly.getPly();	}	return null;    }    /**     * The main method of the search thread.     */    public void run() {	// setAnalyzedBoards( 0L);  // Is done in the permanent brain now.	setSearchDepth( 0);	// Try to get a move from the permanent brain.	PreComputedPly permanentBrainPly = usePermanentBrain() ? getPermanentBrain().getPlyForUserPly( _lastUserPly) : null;	// If we actually have a precomputed ply, adjust the search parameters.	if( permanentBrainPly != null) {	    _bestPly = permanentBrainPly.getPly();	    setSearchDepth( permanentBrainPly.getSearchDepth());  // depth is increased before next search!	    if ( this._enginePanel != null ) {		StringBuffer sOutput = new StringBuffer();		sOutput.append( "Best ply for search depth ");		sOutput.append( getSearchDepth());		sOutput.append( " is ");		sOutput.append( _bestPly.getPly().toString());		sOutput.append( " with score ");		sOutput.append( _bestPly.getScore());		this._enginePanel.modifyText( sOutput.toString() );	    }	}	// The following search is rather inefficent at the moment, since we should try to get a principal variant	// from a search, so we can presort the plies for the next search.	do {	    increaseSearchDepth();	    AnalyzedPly searchDepthResult = null;	    try {		searchDepthResult = startMinimaxAlphaBeta( isWhite());	    } catch( InterruptedException ignored) {  // The search was just interrupted here, so we don't have to throw this exception...		decreaseSearchDepth();  // But the search depth is 1 too high.	    }	    if( searchDepthResult != null) {  // The exception might not be the only case, where a null is returned, so keep		                              // this additional test.		if ( this._statusPanel != null )		    this._statusPanel.setStatusText( "Thinking..." );		_bestPly = searchDepthResult;		if ( this._enginePanel != null ) {		    StringBuffer sOutput = new StringBuffer();		    sOutput.append( "Best ply for search depth ");		    sOutput.append( getSearchDepth());		    sOutput.append( " is ");		    sOutput.append( _bestPly.getPly().toString());		    sOutput.append( " with score ");		    sOutput.append( _bestPly.getScore());		    this._enginePanel.modifyText( sOutput.toString());		}	    }	    // If search depth 1 was completed and no valid ply was found,	    // it seems that the computer is checkmate and the search can be aborted.	} while( ! isSearchStop() && ( _bestPly != null));    }    /**     * Start a complete Minimax-Alpha-Beta search. This is the search level 1, where we have to store the     * analyzed ply, so it gets a special method.     *     * @param white Flag to indicate, if white is about to move.     *     * @throws InterruptedException if the search was interrupted because of a timeout.     */    public final AnalyzedPly startMinimaxAlphaBeta( boolean white) throws InterruptedException {	short curAlpha = AnalyzedPly.MIN_SCORE;	short curBeta = AnalyzedPly.MAX_SCORE;	int bestPlyIndex = -1;	Ply [] plies = _plyGenerator.getPliesForColor( (BitBoard)getBoard(), white);	if( white) {	    for( int i = 0; i < plies.length; i++) {		if( isSearchStop() && ( getSearchDepth() > 1)) {  // If the search time is over and at least depth 1 was completed		    throw new InterruptedException( "Search interrupted at depth " + getSearchDepth());  // abort the search.		}		getGame().doPly( plies[i]);		short val;		try {		    val = minimaxAlphaBeta( plies[i], getBoard().getBoardAfterPly( plies[i]), false, 1, curAlpha, curBeta);		} catch( InterruptedException ie) {		    getGame().undoLastPly();  // Undo the last move		    throw ie;                 // and pass the exception.		}		if( val > curAlpha) {		    curAlpha = val;		    bestPlyIndex = i;		}		if( curAlpha >= curBeta) {		    getGame().undoLastPly();  // Take the last ply back, before the loop is aborted.		    break;		}		getGame().undoLastPly();	    }	    if( bestPlyIndex != -1) {		// Since this is the best ply so far, we store it in the hashtable. This makes sense,		// since the minimax algorithm is started several times, before a move is selected.		// So this move is not necessarily applied immediately!		getHashtable().pushEntry( new PlyHashtableEntryImpl( getBoard(), plies[bestPlyIndex], getSearchDepth()));		return new AnalyzedPlyImpl( plies[bestPlyIndex], curAlpha);	    } else {		return null;	    }	} else {	    for( int i = 0; i < plies.length; i++) {		if( isSearchStop() && ( getSearchDepth() > 1)) {  // If the search time is over and at least depth 1 was completed		    throw new InterruptedException( "Search interrupted at depth " + getSearchDepth());  // abort the search.		}		getGame().doPly( plies[i]);		short val;		try {		    val = minimaxAlphaBeta( plies[i], getBoard().getBoardAfterPly( plies[i]), true, 1, curAlpha, curBeta);		} catch( InterruptedException ie) {		    getGame().undoLastPly();  // Undo the last move		    throw ie;                 // and pass the exception.		}		if( val < curBeta) {		    curBeta = val;		    bestPlyIndex = i;		}		if( curBeta <= curAlpha) {		    getGame().undoLastPly();  // Take the last ply back, before the loop is aborted.		    break;		}		getGame().undoLastPly();	    }	    if( bestPlyIndex != -1) {		// Since this is the best ply so far, we store it in the hashtable. This makes sense,		// since the minimax algorithm is started several times, before a move is selected.		// So this move is not necessarily applied immediately!		getHashtable().pushEntry( new PlyHashtableEntryImpl( getBoard(), plies[bestPlyIndex], getSearchDepth()));		return new AnalyzedPlyImpl( plies[bestPlyIndex], curBeta);	    } else {		return null;	    }	}    }    /**     * Perform a alpha-beta minimax search on the board.     *     * @param lastPly The ply, that created this board.     * @param board The board to analyze.     * @param white true, if white has the next move.     * @param byte searchLevel The level to search for.     * @param alpha The current maximum.     * @param beta The current minimum.     *     * @throws InterruptedException if the search was interrupted because of a timeout.     */    private final short minimaxAlphaBeta( Ply lastPly, Board board, boolean white, int searchLevel, short alpha, short beta) throws InterruptedException {	if( ( searchLevel >= getSearchDepth()) 	    && ( ! lastPly.isCapture() 		 || ! ( lastPly instanceof TransformationPly) 		 || ! _analyzer.isInCheck( (BitBoard)board, ! white))) {	    increaseAnalyzedBoards();	    return analyzeBoard( board);	} else {	    short curAlpha = alpha;	    short curBeta = beta;	    int bestPlyIndex = -1;	    Ply [] plies = _plyGenerator.getPliesForColor( (BitBoard)board, white);	    if( white) {		for( int i = 0; i < plies.length; i++) {		    if( isSearchStop() && ( getSearchDepth() > 1)) {  // If the search time is over and at least depth 1 was completed			throw new InterruptedException( "Search interrupted at depth " + getSearchDepth());  // abort the search.		    }		    getGame().doPly( plies[i]);		    short val;		    try {			val = minimaxAlphaBeta( plies[i], board.getBoardAfterPly( plies[i]), false, searchLevel + 1, curAlpha, curBeta);		    } catch( InterruptedException ie) {			getGame().undoLastPly();  // Undo the last move			throw ie;                 // and pass the exception.		    }		    if( val > curAlpha) {			curAlpha = val;			bestPlyIndex = i;  // Store the index of this ply, so we can access it later.		    }		    if( curAlpha >= curBeta) {			getGame().undoLastPly();  // Take the last ply back, before the loop is aborted.			break;		    }		    getGame().undoLastPly();		}		if( bestPlyIndex != -1) {		    // Since this is the best ply for this search level, we store it in the hashtable		    getHashtable().pushEntry( new PlyHashtableEntryImpl( board, plies[ bestPlyIndex], getSearchDepth() - searchLevel));		} else {                    if( plies.length == 0) {  // There are no legal moves available?		        if( _analyzer.isInCheck( (BitBoard)board, white)) {  // Is this a checkmate?			    return BitBoardAnalyzer.BLACK_HAS_WON;		        } else {  // Looks like a draw?			    return BitBoardAnalyzer.DRAW;                        }                    }		}		return curAlpha;	    } else {		for( int i = 0; i < plies.length; i++) {		    if( isSearchStop() && ( getSearchDepth() > 1)) {  // If the search time is over and at least depth 1 was completed			throw new InterruptedException( "Search interrupted at depth " + getSearchDepth());  // abort the search.		    }		    getGame().doPly( plies[i]);		    short val;		    try {			val = minimaxAlphaBeta( plies[i], board.getBoardAfterPly( plies[i]), true, searchLevel + 1, curAlpha, curBeta);		    } catch( InterruptedException ie) {			getGame().undoLastPly();  // Undo the last move			throw ie;                 // and pass the exception.		    }		    if( val < curBeta) {			curBeta = val;			bestPlyIndex = i;  // Store the index of this ply, so we can access it later.		    }		    if( curBeta <= curAlpha) {			getGame().undoLastPly();  // Take the last ply back, before the loop is aborted.			break;		    }		    getGame().undoLastPly();		}		if( bestPlyIndex != -1) {		    // Since this is the best ply for this search level, we store it in the hashtable		    getHashtable().pushEntry( new PlyHashtableEntryImpl( board, plies[ bestPlyIndex], getSearchDepth() - searchLevel));		} else {                    if( plies.length == 0) {  // There are no legal moves available?		        if( _analyzer.isInCheck( (BitBoard)board, white)) {  // Is this a checkmate?			    return BitBoardAnalyzer.WHITE_HAS_WON;		        } else {  // Looks like a draw?			    return BitBoardAnalyzer.DRAW;                        }                    }		}		return curBeta;	    }	}    }    /**     * Compute a score for a game position.     *     * @return A score for the current game position.     */    public final short analyzeBoard( Board board) {	return _analyzer.analyze( (BitBoard)board, isWhite());    }    /**     * Get all the potential plies for the human player.     *     * @return All the potential plies for the human player.     */    public final Ply [] getUserPlies() {	return _plyGenerator.getPliesForColor( (BitBoard)getBoard(), ! isWhite());    }    /**     * Check if a ply made by the user is valid.     *     * @param ply The user ply.     *     * @return true, if the ply is valid. false otherwise.     */    public final boolean validateUserPly( Ply ply) {	// Get the user plies from the permanent brain, where they	// were hopefully already computed (if the PB is actually active).	Ply [] plies = getPermanentBrain().getUserPlies();	// If the permanent brain is not activated at the moment, remove the	// computed plies immediately, so they are recomputed for the next move!	if( ! usePermanentBrain()) {	    getPermanentBrain().resetUserPlies();	}	for( int p = 0; p < plies.length; p++) {  // For each ply	    if( plies[p].equals( ply)) {          // if the user ply equals this computed		// Perform this ply in the opening book		getOpeningBook().doUserPly( ply);		// Store the last user ply in a instance variable.		_lastUserPly = ply;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?