mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-11 16:42:41 +01:00
DroidFish: Some code re-organization.
This commit is contained in:
@@ -107,6 +107,7 @@ public class DroidFish extends Activity implements GUIInterface {
|
|||||||
// FIXME!!! PGN view option: game continuation (for training)
|
// FIXME!!! PGN view option: game continuation (for training)
|
||||||
// FIXME!!! Remove invalid playerActions in PGN import (should be done in verifyChildren)
|
// FIXME!!! Remove invalid playerActions in PGN import (should be done in verifyChildren)
|
||||||
// FIXME!!! Implement bookmark mechanism for positions in pgn files
|
// FIXME!!! Implement bookmark mechanism for positions in pgn files
|
||||||
|
// FIXME!!! Display chess notation in local language
|
||||||
|
|
||||||
// FIXME!!! Computer clock should stop if phone turned off (computer stops thinking if unplugged)
|
// FIXME!!! Computer clock should stop if phone turned off (computer stops thinking if unplugged)
|
||||||
// FIXME!!! Add support for all time controls defined by the PGN standard
|
// FIXME!!! Add support for all time controls defined by the PGN standard
|
||||||
@@ -511,9 +512,8 @@ public class DroidFish extends Activity implements GUIInterface {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
if (ctrl != null) {
|
if (ctrl != null)
|
||||||
ctrl.shutdownEngine();
|
ctrl.shutdownEngine();
|
||||||
}
|
|
||||||
setNotification(false);
|
setNotification(false);
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
@@ -1552,7 +1552,7 @@ public class DroidFish extends Activity implements GUIInterface {
|
|||||||
for (int i = 0; i < pvMovesTmp.size(); i++) {
|
for (int i = 0; i < pvMovesTmp.size(); i++) {
|
||||||
ArrayList<Move> pv = pvMovesTmp.get(i);
|
ArrayList<Move> pv = pvMovesTmp.get(i);
|
||||||
StringBuilder preComment = new StringBuilder();
|
StringBuilder preComment = new StringBuilder();
|
||||||
if (pvStrs.length > i) {
|
if (i < pvStrs.length) {
|
||||||
String[] tmp = pvStrs[i].split(" ");
|
String[] tmp = pvStrs[i].split(" ");
|
||||||
for (int j = 0; j < 2; j++) {
|
for (int j = 0; j < 2; j++) {
|
||||||
if (j < tmp.length) {
|
if (j < tmp.length) {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public interface GUIInterface {
|
|||||||
/** Update the displayed board position. */
|
/** Update the displayed board position. */
|
||||||
public void setPosition(Position pos, String variantInfo, List<Move> variantMoves);
|
public void setPosition(Position pos, String variantInfo, List<Move> variantMoves);
|
||||||
|
|
||||||
/** Mark square i as selected. Set to -1 to clear selection. */
|
/** Mark square sq as selected. Set to -1 to clear selection. */
|
||||||
public void setSelection(int sq);
|
public void setSelection(int sq);
|
||||||
|
|
||||||
final static class GameStatus {
|
final static class GameStatus {
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ public class CtgBook implements IOpeningBook {
|
|||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private static int tbl[] = {
|
private final static int tbl[] = {
|
||||||
0x3100d2bf, 0x3118e3de, 0x34ab1372, 0x2807a847,
|
0x3100d2bf, 0x3118e3de, 0x34ab1372, 0x2807a847,
|
||||||
0x1633f566, 0x2143b359, 0x26d56488, 0x3b9e6f59,
|
0x1633f566, 0x2143b359, 0x26d56488, 0x3b9e6f59,
|
||||||
0x37755656, 0x3089ca7b, 0x18e92d85, 0x0cd0e9d8,
|
0x37755656, 0x3089ca7b, 0x18e92d85, 0x0cd0e9d8,
|
||||||
@@ -290,7 +290,7 @@ public class CtgBook implements IOpeningBook {
|
|||||||
0x274c7e7c, 0x1e8be65c, 0x2fa0b0bb, 0x1eb6c371
|
0x274c7e7c, 0x1e8be65c, 0x2fa0b0bb, 0x1eb6c371
|
||||||
};
|
};
|
||||||
|
|
||||||
final private static int getHashValue(byte[] encodedPos) {
|
private final static int getHashValue(byte[] encodedPos) {
|
||||||
int hash = 0;
|
int hash = 0;
|
||||||
int tmp = 0;
|
int tmp = 0;
|
||||||
for (int i = 0; i < encodedPos.length; i++) {
|
for (int i = 0; i < encodedPos.length; i++) {
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ public final class DroidBook {
|
|||||||
rndGen.setSeed(System.currentTimeMillis());
|
rndGen.setSeed(System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set opening book options. */
|
||||||
public final void setOptions(BookOptions options) {
|
public final void setOptions(BookOptions options) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
if (CtgBook.canHandle(options))
|
if (CtgBook.canHandle(options))
|
||||||
@@ -136,7 +137,7 @@ public final class DroidBook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a string describing all book moves. */
|
/** Return all book moves, both as a formatted string and as a list of moves. */
|
||||||
public final Pair<String,ArrayList<Move>> getAllBookMoves(Position pos) {
|
public final Pair<String,ArrayList<Move>> getAllBookMoves(Position pos) {
|
||||||
StringBuilder ret = new StringBuilder();
|
StringBuilder ret = new StringBuilder();
|
||||||
ArrayList<Move> bookMoveList = new ArrayList<Move>();
|
ArrayList<Move> bookMoveList = new ArrayList<Move>();
|
||||||
|
|||||||
@@ -46,11 +46,13 @@ public class DroidComputerPlayer {
|
|||||||
private DroidBook book;
|
private DroidBook book;
|
||||||
private boolean newGame = false;
|
private boolean newGame = false;
|
||||||
private String engine = "";
|
private String engine = "";
|
||||||
private int maxPV = 1; // >1 if multiPV mode is supported
|
/** >1 if multiPV mode is supported. */
|
||||||
|
private int maxPV = 1;
|
||||||
private int numCPUs = 1;
|
private int numCPUs = 1;
|
||||||
|
|
||||||
private boolean havePonderHit = false;
|
private boolean havePonderHit = false;
|
||||||
|
|
||||||
|
/** Constructor. Starts engine process if not already started. */
|
||||||
public DroidComputerPlayer(String engine) {
|
public DroidComputerPlayer(String engine) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
startEngine();
|
startEngine();
|
||||||
@@ -58,6 +60,245 @@ public class DroidComputerPlayer {
|
|||||||
book = DroidBook.getInstance();
|
book = DroidBook.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set engine and engine strength.
|
||||||
|
* @param engine Name of engine.
|
||||||
|
* @param strength Engine strength, 0 - 1000. */
|
||||||
|
public final synchronized void setEngineStrength(String engine, int strength) {
|
||||||
|
if (!engine.equals(this.engine)) {
|
||||||
|
shutdownEngine();
|
||||||
|
this.engine = engine;
|
||||||
|
startEngine();
|
||||||
|
}
|
||||||
|
if (uciEngine != null)
|
||||||
|
uciEngine.setStrength(strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set search listener. */
|
||||||
|
public final void setListener(SearchListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return maximum number of PVs supported by engine. */
|
||||||
|
public final synchronized int getMaxPV() {
|
||||||
|
return maxPV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set engine multi-PV mode. */
|
||||||
|
public final synchronized void setNumPV(int numPV) {
|
||||||
|
if ((uciEngine != null) && (maxPV > 1)) {
|
||||||
|
int num = Math.min(maxPV, numPV);
|
||||||
|
uciEngine.setOption("MultiPV", num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set opening book options. */
|
||||||
|
public final void setBookOptions(BookOptions options) {
|
||||||
|
book.setOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return all book moves, both as a formatted string and as a list of moves. */
|
||||||
|
public final Pair<String, ArrayList<Move>> getBookHints(Position pos) {
|
||||||
|
return book.getAllBookMoves(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get engine reported name, including strength setting. */
|
||||||
|
public synchronized String getEngineName() {
|
||||||
|
if (uciEngine != null)
|
||||||
|
return engineName + uciEngine.addStrengthToName();
|
||||||
|
else
|
||||||
|
return engineName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clear transposition table. */
|
||||||
|
public final void clearTT() {
|
||||||
|
newGame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sends "ucinewgame" to engine if clearTT() has previously been called. */
|
||||||
|
public final void maybeNewGame() {
|
||||||
|
if (newGame) {
|
||||||
|
newGame = false;
|
||||||
|
if (uciEngine != null) {
|
||||||
|
uciEngine.writeLineToEngine("ucinewgame");
|
||||||
|
syncReady();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sends "ponderhit" command to engine. */
|
||||||
|
public final synchronized void ponderHit(Position pos, Move ponderMove) {
|
||||||
|
havePonderHit = true;
|
||||||
|
uciEngine.writeLineToEngine("ponderhit");
|
||||||
|
pvModified = true;
|
||||||
|
notifyGUI(pos, ponderMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stop the engine process. */
|
||||||
|
public final synchronized void shutdownEngine() {
|
||||||
|
if (uciEngine != null) {
|
||||||
|
uciEngine.shutDown();
|
||||||
|
uciEngine = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a search and return a command from the computer player.
|
||||||
|
* The command can be a valid move string, in which case the move is played
|
||||||
|
* and the turn goes over to the other player. The command can also be a special
|
||||||
|
* command, such as "draw" and "resign".
|
||||||
|
* @param pos An earlier position from the game
|
||||||
|
* @param mList List of moves to go from the earlier position to the current position.
|
||||||
|
* This list makes it possible for the computer to correctly handle draw
|
||||||
|
* by repetition/50 moves.
|
||||||
|
* @param ponderEnabled True if pondering is enabled in the GUI. Can affect time management.
|
||||||
|
* @param ponderMove Move to ponder, or null for non-ponder search.
|
||||||
|
* @param engineThreads Number of engine threads to use, if supported by engine.
|
||||||
|
* @return The computer player command, and the next ponder move.
|
||||||
|
*/
|
||||||
|
public final Pair<String,Move> doSearch(Position prevPos, ArrayList<Move> mList,
|
||||||
|
Position currPos, boolean drawOffer,
|
||||||
|
int wTime, int bTime, int inc, int movesToGo,
|
||||||
|
boolean ponderEnabled, Move ponderMove,
|
||||||
|
int engineThreads) {
|
||||||
|
if (listener != null)
|
||||||
|
listener.notifyBookInfo("", null);
|
||||||
|
|
||||||
|
if (ponderMove != null)
|
||||||
|
mList.add(ponderMove);
|
||||||
|
|
||||||
|
havePonderHit = false;
|
||||||
|
|
||||||
|
// Set up for draw detection
|
||||||
|
long[] posHashList = new long[mList.size()+1];
|
||||||
|
int posHashListSize = 0;
|
||||||
|
Position p = new Position(prevPos);
|
||||||
|
UndoInfo ui = new UndoInfo();
|
||||||
|
for (int i = 0; i < mList.size(); i++) {
|
||||||
|
posHashList[posHashListSize++] = p.zobristHash();
|
||||||
|
p.makeMove(mList.get(i), ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ponderMove == null) {
|
||||||
|
// If we have a book move, play it.
|
||||||
|
Move bookMove = book.getBookMove(currPos);
|
||||||
|
if (bookMove != null) {
|
||||||
|
if (canClaimDraw(currPos, posHashList, posHashListSize, bookMove) == "") {
|
||||||
|
return new Pair<String,Move>(TextIO.moveToString(currPos, bookMove, false), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If only one legal move, play it without searching
|
||||||
|
ArrayList<Move> moves = new MoveGen().pseudoLegalMoves(currPos);
|
||||||
|
moves = MoveGen.removeIllegal(currPos, moves);
|
||||||
|
if (moves.size() == 0) {
|
||||||
|
return new Pair<String,Move>("", null); // User set up a position where computer has no valid moves.
|
||||||
|
}
|
||||||
|
if (moves.size() == 1) {
|
||||||
|
Move bestMove = moves.get(0);
|
||||||
|
if (canClaimDraw(currPos, posHashList, posHashListSize, bestMove) == "") {
|
||||||
|
return new Pair<String,Move>(TextIO.moveToUCIString(bestMove), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder posStr = new StringBuilder();
|
||||||
|
posStr.append("position fen ");
|
||||||
|
posStr.append(TextIO.toFEN(prevPos));
|
||||||
|
int nMoves = mList.size();
|
||||||
|
if (nMoves > 0) {
|
||||||
|
posStr.append(" moves");
|
||||||
|
for (int i = 0; i < nMoves; i++) {
|
||||||
|
posStr.append(" ");
|
||||||
|
posStr.append(TextIO.moveToUCIString(mList.get(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maybeNewGame();
|
||||||
|
uciEngine.setOption("Ponder", ponderEnabled);
|
||||||
|
uciEngine.setOption("UCI_AnalyseMode", false);
|
||||||
|
uciEngine.setOption("Threads", engineThreads > 0 ? engineThreads : numCPUs);
|
||||||
|
uciEngine.writeLineToEngine(posStr.toString());
|
||||||
|
if (wTime < 1) wTime = 1;
|
||||||
|
if (bTime < 1) bTime = 1;
|
||||||
|
StringBuilder goStr = new StringBuilder(96);
|
||||||
|
goStr.append(String.format("go wtime %d btime %d", wTime, bTime));
|
||||||
|
if (inc > 0)
|
||||||
|
goStr.append(String.format(" winc %d binc %d", inc, inc));
|
||||||
|
if (movesToGo > 0)
|
||||||
|
goStr.append(String.format(" movestogo %d", movesToGo));
|
||||||
|
if (ponderMove != null)
|
||||||
|
goStr.append(" ponder");
|
||||||
|
uciEngine.writeLineToEngine(goStr.toString());
|
||||||
|
|
||||||
|
Pair<String,String> pair = monitorEngine(currPos, ponderMove);
|
||||||
|
String bestMove = pair.first;
|
||||||
|
Move nextPonderMove = TextIO.UCIstringToMove(pair.second);
|
||||||
|
|
||||||
|
// Claim draw if appropriate
|
||||||
|
if (statScore <= 0) {
|
||||||
|
String drawClaim = canClaimDraw(currPos, posHashList, posHashListSize, TextIO.UCIstringToMove(bestMove));
|
||||||
|
if (drawClaim != "")
|
||||||
|
bestMove = drawClaim;
|
||||||
|
}
|
||||||
|
// Accept draw offer if engine is losing
|
||||||
|
if (drawOffer && !statIsMate && (statScore <= -300)) {
|
||||||
|
bestMove = "draw accept";
|
||||||
|
}
|
||||||
|
return new Pair<String,Move>(bestMove, nextPonderMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldStop = false;
|
||||||
|
|
||||||
|
/** Tell engine to stop searching. */
|
||||||
|
public final synchronized void stopSearch() {
|
||||||
|
shouldStop = true;
|
||||||
|
if (uciEngine != null)
|
||||||
|
uciEngine.writeLineToEngine("stop");
|
||||||
|
havePonderHit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Start analyzing a position.
|
||||||
|
* @param prevPos Position corresponding to last irreversible move.
|
||||||
|
* @param mList List of moves from prevPos to currPos.
|
||||||
|
* @param currPos Position to analyze.
|
||||||
|
* @param drawOffer True if other side have offered draw.
|
||||||
|
* @param engineThreads Number of threads to use, or 0 for default value.
|
||||||
|
*/
|
||||||
|
public final void analyze(Position prevPos, ArrayList<Move> mList, Position currPos,
|
||||||
|
boolean drawOffer, int engineThreads) {
|
||||||
|
if (shouldStop)
|
||||||
|
return;
|
||||||
|
if (listener != null) {
|
||||||
|
Pair<String, ArrayList<Move>> bi = getBookHints(currPos);
|
||||||
|
listener.notifyBookInfo(bi.first, bi.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no legal moves, there is nothing to analyze
|
||||||
|
ArrayList<Move> moves = new MoveGen().pseudoLegalMoves(currPos);
|
||||||
|
moves = MoveGen.removeIllegal(currPos, moves);
|
||||||
|
if (moves.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
StringBuilder posStr = new StringBuilder();
|
||||||
|
posStr.append("position fen ");
|
||||||
|
posStr.append(TextIO.toFEN(prevPos));
|
||||||
|
int nMoves = mList.size();
|
||||||
|
if (nMoves > 0) {
|
||||||
|
posStr.append(" moves");
|
||||||
|
for (int i = 0; i < nMoves; i++) {
|
||||||
|
posStr.append(" ");
|
||||||
|
posStr.append(TextIO.moveToUCIString(mList.get(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maybeNewGame();
|
||||||
|
uciEngine.writeLineToEngine(posStr.toString());
|
||||||
|
uciEngine.setOption("UCI_AnalyseMode", true);
|
||||||
|
uciEngine.setOption("Threads", engineThreads > 0 ? engineThreads : numCPUs);
|
||||||
|
String goStr = String.format("go infinite");
|
||||||
|
uciEngine.writeLineToEngine(goStr);
|
||||||
|
|
||||||
|
monitorEngine(currPos, null);
|
||||||
|
}
|
||||||
|
|
||||||
private final synchronized void startEngine() {
|
private final synchronized void startEngine() {
|
||||||
boolean useCuckoo = engine.equals("cuckoochess");
|
boolean useCuckoo = engine.equals("cuckoochess");
|
||||||
if (uciEngine == null) {
|
if (uciEngine == null) {
|
||||||
@@ -80,27 +321,6 @@ public class DroidComputerPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final synchronized void setEngineStrength(String engine, int strength) {
|
|
||||||
if (!engine.equals(this.engine)) {
|
|
||||||
shutdownEngine();
|
|
||||||
this.engine = engine;
|
|
||||||
startEngine();
|
|
||||||
}
|
|
||||||
if (uciEngine != null)
|
|
||||||
uciEngine.setStrength(strength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final synchronized int getMaxPV() {
|
|
||||||
return maxPV;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final synchronized void setNumPV(int numPV) {
|
|
||||||
if ((uciEngine != null) && (maxPV > 1)) {
|
|
||||||
int num = Math.min(maxPV, numPV);
|
|
||||||
uciEngine.setOption("MultiPV", num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getNumCPUs() {
|
private static int getNumCPUs() {
|
||||||
int nCPUsFromProc = 1;
|
int nCPUsFromProc = 1;
|
||||||
try {
|
try {
|
||||||
@@ -121,15 +341,7 @@ public class DroidComputerPlayer {
|
|||||||
return Math.max(nCPUsFromProc, nCPUsFromOS);
|
return Math.max(nCPUsFromProc, nCPUsFromOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setListener(SearchListener listener) {
|
private final void readUCIOptions() {
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setBookOptions(BookOptions options) {
|
|
||||||
book.setOptions(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readUCIOptions() {
|
|
||||||
int timeout = 1000;
|
int timeout = 1000;
|
||||||
maxPV = 1;
|
maxPV = 1;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -159,13 +371,6 @@ public class DroidComputerPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized String getEngineName() {
|
|
||||||
if (uciEngine != null)
|
|
||||||
return engineName + uciEngine.addStrengthToName();
|
|
||||||
else
|
|
||||||
return engineName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convert a string to tokens by splitting at whitespace characters. */
|
/** Convert a string to tokens by splitting at whitespace characters. */
|
||||||
private final String[] tokenize(String cmdLine) {
|
private final String[] tokenize(String cmdLine) {
|
||||||
cmdLine = cmdLine.trim();
|
cmdLine = cmdLine.trim();
|
||||||
@@ -181,141 +386,6 @@ public class DroidComputerPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clear transposition table. */
|
|
||||||
public final void clearTT() {
|
|
||||||
newGame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void maybeNewGame() {
|
|
||||||
if (newGame) {
|
|
||||||
newGame = false;
|
|
||||||
if (uciEngine != null) {
|
|
||||||
uciEngine.writeLineToEngine("ucinewgame");
|
|
||||||
syncReady();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Stop the engine process. */
|
|
||||||
public final synchronized void shutdownEngine() {
|
|
||||||
if (uciEngine != null) {
|
|
||||||
uciEngine.shutDown();
|
|
||||||
uciEngine = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final synchronized void ponderHit(Position pos, Move ponderMove) {
|
|
||||||
havePonderHit = true;
|
|
||||||
uciEngine.writeLineToEngine("ponderhit");
|
|
||||||
pvModified = true;
|
|
||||||
notifyGUI(pos, ponderMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do a search and return a command from the computer player.
|
|
||||||
* The command can be a valid move string, in which case the move is played
|
|
||||||
* and the turn goes over to the other player. The command can also be a special
|
|
||||||
* command, such as "draw" and "resign".
|
|
||||||
* @param pos An earlier position from the game
|
|
||||||
* @param mList List of moves to go from the earlier position to the current position.
|
|
||||||
* This list makes it possible for the computer to correctly handle draw
|
|
||||||
* by repetition/50 moves.
|
|
||||||
* @param ponderEnabled True if pondering is enabled in the GUI. Can affect time management.
|
|
||||||
* @param ponderMove Move to ponder, or null for non-ponder search.
|
|
||||||
* @param engineThreads Number of engine threads to use, if supported by engine.
|
|
||||||
* @return The computer player command, and the next ponder move.
|
|
||||||
*/
|
|
||||||
public final Pair<String,Move> doSearch(Position prevPos, ArrayList<Move> mList,
|
|
||||||
Position currPos, boolean drawOffer,
|
|
||||||
int wTime, int bTime, int inc, int movesToGo,
|
|
||||||
boolean ponderEnabled, Move ponderMove,
|
|
||||||
int engineThreads) {
|
|
||||||
if (listener != null)
|
|
||||||
listener.notifyBookInfo("", null);
|
|
||||||
|
|
||||||
if (ponderMove != null)
|
|
||||||
mList.add(ponderMove);
|
|
||||||
|
|
||||||
havePonderHit = false;
|
|
||||||
|
|
||||||
// Set up for draw detection
|
|
||||||
long[] posHashList = new long[mList.size()+1];
|
|
||||||
int posHashListSize = 0;
|
|
||||||
Position p = new Position(prevPos);
|
|
||||||
UndoInfo ui = new UndoInfo();
|
|
||||||
for (int i = 0; i < mList.size(); i++) {
|
|
||||||
posHashList[posHashListSize++] = p.zobristHash();
|
|
||||||
p.makeMove(mList.get(i), ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ponderMove == null) {
|
|
||||||
// If we have a book move, play it.
|
|
||||||
Move bookMove = book.getBookMove(currPos);
|
|
||||||
if (bookMove != null) {
|
|
||||||
if (canClaimDraw(currPos, posHashList, posHashListSize, bookMove) == "") {
|
|
||||||
return new Pair<String,Move>(TextIO.moveToString(currPos, bookMove, false), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If only one legal move, play it without searching
|
|
||||||
ArrayList<Move> moves = new MoveGen().pseudoLegalMoves(currPos);
|
|
||||||
moves = MoveGen.removeIllegal(currPos, moves);
|
|
||||||
if (moves.size() == 0) {
|
|
||||||
return new Pair<String,Move>("", null); // User set up a position where computer has no valid moves.
|
|
||||||
}
|
|
||||||
if (moves.size() == 1) {
|
|
||||||
Move bestMove = moves.get(0);
|
|
||||||
if (canClaimDraw(currPos, posHashList, posHashListSize, bestMove) == "") {
|
|
||||||
return new Pair<String,Move>(TextIO.moveToUCIString(bestMove), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder posStr = new StringBuilder();
|
|
||||||
posStr.append("position fen ");
|
|
||||||
posStr.append(TextIO.toFEN(prevPos));
|
|
||||||
int nMoves = mList.size();
|
|
||||||
if (nMoves > 0) {
|
|
||||||
posStr.append(" moves");
|
|
||||||
for (int i = 0; i < nMoves; i++) {
|
|
||||||
posStr.append(" ");
|
|
||||||
posStr.append(TextIO.moveToUCIString(mList.get(i)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maybeNewGame();
|
|
||||||
uciEngine.setOption("Ponder", ponderEnabled);
|
|
||||||
uciEngine.setOption("UCI_AnalyseMode", false);
|
|
||||||
uciEngine.setOption("Threads", engineThreads > 0 ? engineThreads : numCPUs);
|
|
||||||
uciEngine.writeLineToEngine(posStr.toString());
|
|
||||||
if (wTime < 1) wTime = 1;
|
|
||||||
if (bTime < 1) bTime = 1;
|
|
||||||
StringBuilder goStr = new StringBuilder(96);
|
|
||||||
goStr.append(String.format("go wtime %d btime %d", wTime, bTime));
|
|
||||||
if (inc > 0)
|
|
||||||
goStr.append(String.format(" winc %d binc %d", inc, inc));
|
|
||||||
if (movesToGo > 0)
|
|
||||||
goStr.append(String.format(" movestogo %d", movesToGo));
|
|
||||||
if (ponderMove != null)
|
|
||||||
goStr.append(" ponder");
|
|
||||||
uciEngine.writeLineToEngine(goStr.toString());
|
|
||||||
|
|
||||||
Pair<String,String> pair = monitorEngine(currPos, ponderMove);
|
|
||||||
String bestMove = pair.first;
|
|
||||||
Move nextPonderMove = TextIO.UCIstringToMove(pair.second);
|
|
||||||
|
|
||||||
// Claim draw if appropriate
|
|
||||||
if (statScore <= 0) {
|
|
||||||
String drawClaim = canClaimDraw(currPos, posHashList, posHashListSize, TextIO.UCIstringToMove(bestMove));
|
|
||||||
if (drawClaim != "")
|
|
||||||
bestMove = drawClaim;
|
|
||||||
}
|
|
||||||
// Accept draw offer if engine is losing
|
|
||||||
if (drawOffer && !statIsMate && (statScore <= -300)) {
|
|
||||||
bestMove = "draw accept";
|
|
||||||
}
|
|
||||||
return new Pair<String,Move>(bestMove, nextPonderMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Wait for engine to respond with bestMove and ponderMove.
|
/** Wait for engine to respond with bestMove and ponderMove.
|
||||||
* While waiting, monitor and report search info. */
|
* While waiting, monitor and report search info. */
|
||||||
private final Pair<String,String> monitorEngine(Position pos, Move ponderMove) {
|
private final Pair<String,String> monitorEngine(Position pos, Move ponderMove) {
|
||||||
@@ -355,49 +425,6 @@ public class DroidComputerPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Pair<String, ArrayList<Move>> getBookHints(Position pos) {
|
|
||||||
Pair<String, ArrayList<Move>> bi = book.getAllBookMoves(pos);
|
|
||||||
return new Pair<String, ArrayList<Move>>(bi.first, bi.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean shouldStop = false;
|
|
||||||
|
|
||||||
public final void analyze(Position prevPos, ArrayList<Move> mList, Position currPos,
|
|
||||||
boolean drawOffer, int engineThreads) {
|
|
||||||
if (shouldStop)
|
|
||||||
return;
|
|
||||||
if (listener != null) {
|
|
||||||
Pair<String, ArrayList<Move>> bi = getBookHints(currPos);
|
|
||||||
listener.notifyBookInfo(bi.first, bi.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no legal moves, there is nothing to analyze
|
|
||||||
ArrayList<Move> moves = new MoveGen().pseudoLegalMoves(currPos);
|
|
||||||
moves = MoveGen.removeIllegal(currPos, moves);
|
|
||||||
if (moves.size() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
StringBuilder posStr = new StringBuilder();
|
|
||||||
posStr.append("position fen ");
|
|
||||||
posStr.append(TextIO.toFEN(prevPos));
|
|
||||||
int nMoves = mList.size();
|
|
||||||
if (nMoves > 0) {
|
|
||||||
posStr.append(" moves");
|
|
||||||
for (int i = 0; i < nMoves; i++) {
|
|
||||||
posStr.append(" ");
|
|
||||||
posStr.append(TextIO.moveToUCIString(mList.get(i)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maybeNewGame();
|
|
||||||
uciEngine.writeLineToEngine(posStr.toString());
|
|
||||||
uciEngine.setOption("UCI_AnalyseMode", true);
|
|
||||||
uciEngine.setOption("Threads", engineThreads > 0 ? engineThreads : numCPUs);
|
|
||||||
String goStr = String.format("go infinite");
|
|
||||||
uciEngine.writeLineToEngine(goStr);
|
|
||||||
|
|
||||||
monitorEngine(currPos, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Check if a draw claim is allowed, possibly after playing "move".
|
/** Check if a draw claim is allowed, possibly after playing "move".
|
||||||
* @param move The move that may have to be made before claiming draw.
|
* @param move The move that may have to be made before claiming draw.
|
||||||
* @return The draw string that claims the draw, or empty string if draw claim not valid.
|
* @return The draw string that claims the draw, or empty string if draw claim not valid.
|
||||||
@@ -583,11 +610,4 @@ public class DroidComputerPlayer {
|
|||||||
statsModified = false;
|
statsModified = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final synchronized void stopSearch() {
|
|
||||||
shouldStop = true;
|
|
||||||
if (uciEngine != null)
|
|
||||||
uciEngine.writeLineToEngine("stop");
|
|
||||||
havePonderHit = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@ public class Game {
|
|||||||
private DroidComputerPlayer computerPlayer;
|
private DroidComputerPlayer computerPlayer;
|
||||||
TimeControl timeController;
|
TimeControl timeController;
|
||||||
private boolean gamePaused;
|
private boolean gamePaused;
|
||||||
|
/** If true, add new moves as mainline moves. */
|
||||||
private boolean addFirst;
|
private boolean addFirst;
|
||||||
|
|
||||||
PgnToken.PgnTokenReceiver gameTextListener;
|
PgnToken.PgnTokenReceiver gameTextListener;
|
||||||
@@ -50,6 +51,7 @@ public class Game {
|
|||||||
newGame();
|
newGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** De-serialize from byte array. */
|
||||||
final void fromByteArray(byte[] data) {
|
final void fromByteArray(byte[] data) {
|
||||||
tree.fromByteArray(data);
|
tree.fromByteArray(data);
|
||||||
updateTimeControl(true);
|
updateTimeControl(true);
|
||||||
@@ -66,10 +68,12 @@ public class Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set whether new moves are entered as mainline moves or variations. */
|
||||||
public final void setAddFirst(boolean addFirst) {
|
public final void setAddFirst(boolean addFirst) {
|
||||||
this.addFirst = addFirst;
|
this.addFirst = addFirst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sets start position and discards the whole game tree. */
|
||||||
final void setPos(Position pos) {
|
final void setPos(Position pos) {
|
||||||
tree.setStartPos(new Position(pos));
|
tree.setStartPos(new Position(pos));
|
||||||
updateTimeControl(false);
|
updateTimeControl(false);
|
||||||
@@ -217,6 +221,7 @@ public class Game {
|
|||||||
return nVar > 0;
|
return nVar > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get number of variations in current game position. */
|
||||||
public final int numVariations() {
|
public final int numVariations() {
|
||||||
if (tree.currentNode == tree.rootNode)
|
if (tree.currentNode == tree.rootNode)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -226,6 +231,7 @@ public class Game {
|
|||||||
return nChildren;
|
return nChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get current variation in current position. */
|
||||||
public final int currVariation() {
|
public final int currVariation() {
|
||||||
if (tree.currentNode == tree.rootNode)
|
if (tree.currentNode == tree.rootNode)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -235,6 +241,7 @@ public class Game {
|
|||||||
return defChild;
|
return defChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Go to a new variation in the game tree. */
|
||||||
public final void changeVariation(int delta) {
|
public final void changeVariation(int delta) {
|
||||||
if (tree.currentNode == tree.rootNode)
|
if (tree.currentNode == tree.rootNode)
|
||||||
return;
|
return;
|
||||||
@@ -249,6 +256,7 @@ public class Game {
|
|||||||
updateTimeControl(true);
|
updateTimeControl(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Move current variation up/down in the game tree. */
|
||||||
public final void moveVariation(int delta) {
|
public final void moveVariation(int delta) {
|
||||||
if (tree.currentNode == tree.rootNode)
|
if (tree.currentNode == tree.rootNode)
|
||||||
return;
|
return;
|
||||||
@@ -264,6 +272,7 @@ public class Game {
|
|||||||
updateTimeControl(true);
|
updateTimeControl(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Delete whole game sub-tree rooted at current position. */
|
||||||
public final void removeSubTree() {
|
public final void removeSubTree() {
|
||||||
if (getLastMove() != null) {
|
if (getLastMove() != null) {
|
||||||
tree.goBack();
|
tree.goBack();
|
||||||
@@ -323,10 +332,14 @@ public class Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void goNode(Node node) {
|
/** Go to given node in game tree.
|
||||||
tree.goNode(node);
|
* @return True if current node changed, false otherwise. */
|
||||||
|
public final boolean goNode(Node node) {
|
||||||
|
if (!tree.goNode(node))
|
||||||
|
return false;
|
||||||
pendingDrawOffer = false;
|
pendingDrawOffer = false;
|
||||||
updateTimeControl(true);
|
updateTimeControl(true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void newGame() {
|
public final void newGame() {
|
||||||
|
|||||||
@@ -657,13 +657,17 @@ public class GameTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Go to given node in game tree. */
|
/** Go to given node in game tree.
|
||||||
public final void goNode(Node node) {
|
* @return True if current node changed, false otherwise. */
|
||||||
|
public final boolean goNode(Node node) {
|
||||||
|
if (node == currentNode)
|
||||||
|
return false;
|
||||||
ArrayList<Integer> path = node.getPathFromRoot();
|
ArrayList<Integer> path = node.getPathFromRoot();
|
||||||
while (currentNode != rootNode)
|
while (currentNode != rootNode)
|
||||||
goBack();
|
goBack();
|
||||||
for (Integer c : path)
|
for (Integer c : path)
|
||||||
goForward(c);
|
goForward(c);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** List of possible continuation moves. */
|
/** List of possible continuation moves. */
|
||||||
@@ -1388,6 +1392,7 @@ public class GameTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set PGN header tags and values. */
|
||||||
void setHeaders(Map<String,String> headers) {
|
void setHeaders(Map<String,String> headers) {
|
||||||
for (Entry<String, String> entry : headers.entrySet()) {
|
for (Entry<String, String> entry : headers.entrySet()) {
|
||||||
String tag = entry.getKey();
|
String tag = entry.getKey();
|
||||||
@@ -1417,6 +1422,7 @@ public class GameTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get PGN header tags and values. */
|
||||||
void getHeaders(Map<String,String> headers) {
|
void getHeaders(Map<String,String> headers) {
|
||||||
headers.put("Event", event);
|
headers.put("Event", event);
|
||||||
headers.put("Site", site);
|
headers.put("Site", site);
|
||||||
|
|||||||
@@ -667,7 +667,8 @@ public class GameTreeTest extends TestCase {
|
|||||||
for (int i = 0; i < 5; i++) gt.goForward(-1);
|
for (int i = 0; i < 5; i++) gt.goForward(-1);
|
||||||
assertEquals("e4 e5 Nf3 Nc6 Bb5 a6*", getMoveListAsString(gt));
|
assertEquals("e4 e5 Nf3 Nc6 Bb5 a6*", getMoveListAsString(gt));
|
||||||
Node na6 = gt.currentNode;
|
Node na6 = gt.currentNode;
|
||||||
gt.goNode(gt.rootNode);
|
assertTrue(gt.goNode(gt.rootNode));
|
||||||
|
assertFalse(gt.goNode(gt.rootNode));
|
||||||
assertEquals("*e4 e5 Nf3 Nc6 Bb5 a6", getMoveListAsString(gt));
|
assertEquals("*e4 e5 Nf3 Nc6 Bb5 a6", getMoveListAsString(gt));
|
||||||
gt.goNode(na6);
|
gt.goNode(na6);
|
||||||
assertEquals("e4 e5 Nf3 Nc6 Bb5 a6*", getMoveListAsString(gt));
|
assertEquals("e4 e5 Nf3 Nc6 Bb5 a6*", getMoveListAsString(gt));
|
||||||
|
|||||||
Reference in New Issue
Block a user