DroidFish: Some code re-organization.

This commit is contained in:
Peter Osterlund
2011-12-27 14:57:36 +00:00
parent d44cb003f1
commit 4f2bc00259
9 changed files with 819 additions and 718 deletions

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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++) {

View File

@@ -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>();

View File

@@ -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,28 +60,9 @@ public class DroidComputerPlayer {
book = DroidBook.getInstance(); book = DroidBook.getInstance();
} }
private final synchronized void startEngine() { /** Set engine and engine strength.
boolean useCuckoo = engine.equals("cuckoochess"); * @param engine Name of engine.
if (uciEngine == null) { * @param strength Engine strength, 0 - 1000. */
if (useCuckoo) {
uciEngine = new CuckooChessEngine();
} else {
uciEngine = new NativePipedProcess();
}
uciEngine.initialize();
uciEngine.writeLineToEngine("uci");
readUCIOptions();
int nThreads = getNumCPUs();
if (nThreads > 8) nThreads = 8;
numCPUs = nThreads;
if (!useCuckoo)
uciEngine.setOption("Hash", 16);
uciEngine.setOption("Ponder", false);
uciEngine.writeLineToEngine("ucinewgame");
syncReady();
}
}
public final synchronized void setEngineStrength(String engine, int strength) { public final synchronized void setEngineStrength(String engine, int strength) {
if (!engine.equals(this.engine)) { if (!engine.equals(this.engine)) {
shutdownEngine(); shutdownEngine();
@@ -90,10 +73,17 @@ public class DroidComputerPlayer {
uciEngine.setStrength(strength); 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() { public final synchronized int getMaxPV() {
return maxPV; return maxPV;
} }
/** Set engine multi-PV mode. */
public final synchronized void setNumPV(int numPV) { public final synchronized void setNumPV(int numPV) {
if ((uciEngine != null) && (maxPV > 1)) { if ((uciEngine != null) && (maxPV > 1)) {
int num = Math.min(maxPV, numPV); int num = Math.min(maxPV, numPV);
@@ -101,64 +91,17 @@ public class DroidComputerPlayer {
} }
} }
private static int getNumCPUs() { /** Set opening book options. */
int nCPUsFromProc = 1;
try {
FileReader fr = new FileReader("/proc/stat");
BufferedReader inBuf = new BufferedReader(fr, 8192);
String line;
int nCPUs = 0;
while ((line = inBuf.readLine()) != null) {
if ((line.length() >= 4) && line.startsWith("cpu") && Character.isDigit(line.charAt(3)))
nCPUs++;
}
inBuf.close();
if (nCPUs < 1) nCPUs = 1;
nCPUsFromProc = nCPUs;
} catch (IOException e) {
}
int nCPUsFromOS = NativePipedProcess.getNPhysicalProcessors();
return Math.max(nCPUsFromProc, nCPUsFromOS);
}
public final void setListener(SearchListener listener) {
this.listener = listener;
}
public final void setBookOptions(BookOptions options) { public final void setBookOptions(BookOptions options) {
book.setOptions(options); book.setOptions(options);
} }
private void readUCIOptions() { /** Return all book moves, both as a formatted string and as a list of moves. */
int timeout = 1000; public final Pair<String, ArrayList<Move>> getBookHints(Position pos) {
maxPV = 1; return book.getAllBookMoves(pos);
while (true) {
String s = uciEngine.readLineFromEngine(timeout);
String[] tokens = tokenize(s);
if (tokens[0].equals("uciok"))
break;
else if (tokens[0].equals("id")) {
if (tokens[1].equals("name")) {
engineName = "";
for (int i = 2; i < tokens.length; i++) {
if (engineName.length() > 0)
engineName += " ";
engineName += tokens[i];
}
}
} else if ((tokens.length > 2) && tokens[2].toLowerCase().equals("multipv")) {
try {
for (int i = 3; i < tokens.length; i++) {
if (tokens[i].equals("max") && (i+1 < tokens.length)) {
maxPV = Math.max(maxPV, Integer.parseInt(tokens[i+1]));
break;
}
}
} catch (NumberFormatException nfe) { }
}
}
} }
/** Get engine reported name, including strength setting. */
public synchronized String getEngineName() { public synchronized String getEngineName() {
if (uciEngine != null) if (uciEngine != null)
return engineName + uciEngine.addStrengthToName(); return engineName + uciEngine.addStrengthToName();
@@ -166,26 +109,12 @@ public class DroidComputerPlayer {
return engineName; return engineName;
} }
/** Convert a string to tokens by splitting at whitespace characters. */
private final String[] tokenize(String cmdLine) {
cmdLine = cmdLine.trim();
return cmdLine.split("\\s+");
}
private final void syncReady() {
uciEngine.writeLineToEngine("isready");
while (true) {
String s = uciEngine.readLineFromEngine(1000);
if (s.equals("readyok"))
break;
}
}
/** Clear transposition table. */ /** Clear transposition table. */
public final void clearTT() { public final void clearTT() {
newGame = true; newGame = true;
} }
/** Sends "ucinewgame" to engine if clearTT() has previously been called. */
public final void maybeNewGame() { public final void maybeNewGame() {
if (newGame) { if (newGame) {
newGame = false; newGame = false;
@@ -196,6 +125,14 @@ public class DroidComputerPlayer {
} }
} }
/** 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. */ /** Stop the engine process. */
public final synchronized void shutdownEngine() { public final synchronized void shutdownEngine() {
if (uciEngine != null) { if (uciEngine != null) {
@@ -204,13 +141,6 @@ public class DroidComputerPlayer {
} }
} }
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. * 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 * The command can be a valid move string, in which case the move is played
@@ -316,6 +246,146 @@ public class DroidComputerPlayer {
return new Pair<String,Move>(bestMove, nextPonderMove); 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() {
boolean useCuckoo = engine.equals("cuckoochess");
if (uciEngine == null) {
if (useCuckoo) {
uciEngine = new CuckooChessEngine();
} else {
uciEngine = new NativePipedProcess();
}
uciEngine.initialize();
uciEngine.writeLineToEngine("uci");
readUCIOptions();
int nThreads = getNumCPUs();
if (nThreads > 8) nThreads = 8;
numCPUs = nThreads;
if (!useCuckoo)
uciEngine.setOption("Hash", 16);
uciEngine.setOption("Ponder", false);
uciEngine.writeLineToEngine("ucinewgame");
syncReady();
}
}
private static int getNumCPUs() {
int nCPUsFromProc = 1;
try {
FileReader fr = new FileReader("/proc/stat");
BufferedReader inBuf = new BufferedReader(fr, 8192);
String line;
int nCPUs = 0;
while ((line = inBuf.readLine()) != null) {
if ((line.length() >= 4) && line.startsWith("cpu") && Character.isDigit(line.charAt(3)))
nCPUs++;
}
inBuf.close();
if (nCPUs < 1) nCPUs = 1;
nCPUsFromProc = nCPUs;
} catch (IOException e) {
}
int nCPUsFromOS = NativePipedProcess.getNPhysicalProcessors();
return Math.max(nCPUsFromProc, nCPUsFromOS);
}
private final void readUCIOptions() {
int timeout = 1000;
maxPV = 1;
while (true) {
String s = uciEngine.readLineFromEngine(timeout);
String[] tokens = tokenize(s);
if (tokens[0].equals("uciok"))
break;
else if (tokens[0].equals("id")) {
if (tokens[1].equals("name")) {
engineName = "";
for (int i = 2; i < tokens.length; i++) {
if (engineName.length() > 0)
engineName += " ";
engineName += tokens[i];
}
}
} else if ((tokens.length > 2) && tokens[2].toLowerCase().equals("multipv")) {
try {
for (int i = 3; i < tokens.length; i++) {
if (tokens[i].equals("max") && (i+1 < tokens.length)) {
maxPV = Math.max(maxPV, Integer.parseInt(tokens[i+1]));
break;
}
}
} catch (NumberFormatException nfe) { }
}
}
}
/** Convert a string to tokens by splitting at whitespace characters. */
private final String[] tokenize(String cmdLine) {
cmdLine = cmdLine.trim();
return cmdLine.split("\\s+");
}
private final void syncReady() {
uciEngine.writeLineToEngine("isready");
while (true) {
String s = uciEngine.readLineFromEngine(1000);
if (s.equals("readyok"))
break;
}
}
/** 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;
}
} }

View File

@@ -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() {

View File

@@ -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);

View File

@@ -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));