Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/** Copyright by Barry G. Becker, 2000-2011. Licensed under MIT License: http://www.opensource.org/licenses/MIT */
package com.barrybecker4.game.twoplayer.common.search.strategy;
import com.barrybecker4.game.common.MoveList;
import com.barrybecker4.game.twoplayer.common.TwoPlayerMove;
import com.barrybecker4.game.twoplayer.common.TwoPlayerBoard;
import com.barrybecker4.game.twoplayer.common.search.SearchWindow;
import com.barrybecker4.game.twoplayer.common.search.Searchable;
import com.barrybecker4.game.twoplayer.common.search.transposition.Entry;
import com.barrybecker4.game.twoplayer.common.search.transposition.HashKey;
import com.barrybecker4.game.twoplayer.common.search.transposition.TranspositionTable;
import com.barrybecker4.game.twoplayer.common.search.tree.SearchTreeNode;
import com.barrybecker4.optimization.parameter.ParameterArray;
/**
* This strategy class defines the NegaScout with memory search algorithm.
* This version stores the values of moves that have already been searched.
* See http://people.csail.mit.edu/plaat/mtdf.html
* and http://en.wikipedia.org/wiki/Negascout
*
* Transposition table (TT) enhanced Alpha-Beta
* (from http://www.top-5000.nl/ps/An%20Algorithm%20faster%20than%20negascout%20and%20SSS%20in%20pratice.pdf)
*
* function AlphaBetaWithMemory(n, a, b) {
* // Check if position is in table and has been searched to sufficient depth.
* if (retrieve(n)) {
* if (n.max <= a or n.max == n.min) return n.max;
* if (n.min >= b) return n.min ;
* }
* // Reached the maximum search depth
* if (n = leaf) {
* n.min = n.max = g = eval(n);
* }
* else {
* g = -inf;
* c = firstchild(n);
* // Search until a cutoff occurs or all children have been considered
* while g < b and c != null {
* g = max(g, -AlphaBetaWithMemory(c, -b, -a));
* a = max(a, g);
* c = nextbrother(c);
* }
* // Save in transposition table
* if g <= a then n.max = g;
* if a < g < b then n.max = n.min = g;
* if g >= b then n.min = g;
* }
* store(n);
* return g;
* }
*
* @author Barry Becker
*/
public final class NegaScoutMemoryStrategy>
extends NegaScoutStrategy
implements MemorySearchStrategy {
/** Stores positions that have already been evaluated, so we do not need to repeat work. */
private TranspositionTable lookupTable;
/**
* Constructor.
*/
public NegaScoutMemoryStrategy(Searchable controller, ParameterArray weights) {
super( controller, weights );
lookupTable = new TranspositionTable<>();
}
@Override
public TranspositionTable getTranspositionTable() {
return lookupTable;
}
@Override
protected M searchInternal( M lastMove, int depth,
SearchWindow window, SearchTreeNode parent ) {
HashKey key = searchable.getHashKey();
Entry entry = lookupTable.get(key);
if (lookupTable.entryExists(entry, lastMove, depth, window))
return entry.bestMove;
boolean done = searchable.done( lastMove, false);
if ( depth <= 0 || done ) {
if (doQuiescentSearch(depth, done, lastMove)) {
M qMove = quiescentSearch(lastMove, depth, window, parent);
if (qMove != null) {
entry = new Entry<>(qMove, qMove.getInheritedValue());
lookupTable.put(key, entry);
return qMove;
}
}
int sign = fromPlayer1sPerspective(lastMove) ? 1 : -1;
lastMove.setInheritedValue(sign * lastMove.getValue());
entry = new Entry<>(lastMove, -lastMove.getInheritedValue());
lookupTable.put(key, entry);
return lastMove;
}
MoveList list = searchable.generateMoves(lastMove, weights_);
if (depth == lookAhead_)
numTopLevelMoves_ = list.size();
if (emptyMoveList(list, lastMove)) return null;
return findBestMove(lastMove, depth, list, window, parent);
}
@Override
protected M findBestMove(M lastMove,int depth, MoveList list,
SearchWindow window, SearchTreeNode parent) {
int newBeta = window.beta;
TwoPlayerMove selectedMove;
M bestMove = list.get(0);
Entry entry = new Entry<>(bestMove, depth, window);
while ( !list.isEmpty() ) {
M theMove = getNextMove(list);
if (pauseInterrupted())
return lastMove;
updatePercentDone(depth, list);
searchable.makeInternalMove( theMove );
SearchTreeNode child = addNodeToTree(parent, theMove, window );
// search with minimal search window
selectedMove = searchInternal( theMove, depth-1, new SearchWindow(-newBeta, -window.alpha), child );
searchable.undoInternalMove( theMove );
if (selectedMove != null) {
int selectedValue = -selectedMove.getInheritedValue();
theMove.setInheritedValue( selectedValue );
if (selectedValue > window.alpha) {
window.alpha = selectedValue;
}
if (window.alpha >= window.beta) {
theMove.setInheritedValue(window.alpha);
bestMove = theMove;
break;
}
if (window.alpha >= newBeta) {
// re-search with narrower window (typical alpha beta search).
searchable.makeInternalMove( theMove );
selectedMove = searchInternal( theMove, depth-1, window.negateAndSwap(), child );
searchable.undoInternalMove( theMove );
selectedValue = -selectedMove.getInheritedValue();
theMove.setInheritedValue(selectedValue);
bestMove = theMove;
if (window.alpha >= window.beta) {
break;
}
}
newBeta = window.alpha + 1;
}
}
storeBestMove(window, entry, bestMove.getInheritedValue());
bestMove.setSelected(true);
lastMove.setInheritedValue(-bestMove.getInheritedValue());
return bestMove;
}
/**
* Store off the best move so we do not need to analyze it again.
*/
private void storeBestMove(SearchWindow window, Entry entry, int bestValue) {
if (bestValue <= window.alpha) {
entry.upperValue = bestValue;
}
else if (window.alpha < bestValue && bestValue < window.beta) {
entry.lowerValue = bestValue;
entry.upperValue = bestValue;
}
else if (bestValue >= window.beta) {
entry.lowerValue = bestValue;
}
lookupTable.put(searchable.getHashKey(), entry);
}
}