?? chessengineimpl.java
字號:
/** * 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;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -