DroidFish: Rewrote the engine communication so that the GUI never waits for the engine.

This commit is contained in:
Peter Osterlund 2011-12-31 02:30:18 +00:00
parent 9d7d30b293
commit c1ab57b4f9
10 changed files with 959 additions and 715 deletions

@ -73,20 +73,26 @@ static std::deque<char> inBuf;
static bool getNextChar(int& c, int timeoutMillis) { static bool getNextChar(int& c, int timeoutMillis) {
if (inBuf.empty()) { if (inBuf.empty()) {
fd_set readfds, writefds; fd_set readfds, exceptfds;
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(fdFromChild, &readfds); FD_SET(fdFromChild, &readfds);
FD_ZERO(&exceptfds);
FD_SET(fdFromChild, &exceptfds);
struct timeval tv; struct timeval tv;
tv.tv_sec = timeoutMillis / 1000; tv.tv_sec = timeoutMillis / 1000;
tv.tv_usec = (timeoutMillis % 1000) * 1000; tv.tv_usec = (timeoutMillis % 1000) * 1000;
int ret = select(fdFromChild + 1, &readfds, NULL, NULL, &tv); int ret = select(fdFromChild + 1, &readfds, NULL, &exceptfds, &tv);
if (ret < 0) if ((ret < 0) || FD_ISSET(fdFromChild, &exceptfds))
return false; return false;
static char buf[4096]; if (FD_ISSET(fdFromChild, &readfds)) {
int len = read(fdFromChild, &buf[0], sizeof(buf)); static char buf[4096];
for (int i = 0; i < len; i++) int len = read(fdFromChild, &buf[0], sizeof(buf));
inBuf.push_back(buf[i]); if (len == 0)
return false; // EOF
for (int i = 0; i < len; i++)
inBuf.push_back(buf[i]);
}
} }
if (inBuf.empty()) { if (inBuf.empty()) {
c = -1; c = -1;

@ -108,6 +108,7 @@ public class DroidFish extends Activity implements GUIInterface {
// 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!!! Display chess notation in local language
// FIXME!!! Add support for "Chess Leipzig" font
// 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
@ -118,8 +119,6 @@ public class DroidFish extends Activity implements GUIInterface {
// FIXME!!! Add chess960 support // FIXME!!! Add chess960 support
// FIXME!!! Implement "hint" feature // FIXME!!! Implement "hint" feature
// FIXME!!! Don't send "stop" command when engine is already stopped
// FIXME!!! Show extended book info. (Win percent, number of games, performance rating, etc.) // FIXME!!! Show extended book info. (Win percent, number of games, performance rating, etc.)
// FIXME!!! Green color for "main move". Red color for "don't play in tournaments" moves. // FIXME!!! Green color for "main move". Red color for "don't play in tournaments" moves.
@ -542,7 +541,7 @@ public class DroidFish extends Activity implements GUIInterface {
mEngineThreads = getIntSetting("threads", 0); mEngineThreads = getIntSetting("threads", 0);
String engine = settings.getString("engine", ""); String engine = settings.getString("engine", "stockfish");
int strength = settings.getInt("strength", 1000); int strength = settings.getInt("strength", 1000);
setEngineStrength(engine, strength); setEngineStrength(engine, strength);
@ -1142,7 +1141,7 @@ public class DroidFish extends Activity implements GUIInterface {
return alert; return alert;
} }
case SELECT_MOVE_DIALOG: { case SELECT_MOVE_DIALOG: {
View content = View.inflate(this, R.layout.select_move_number, null); View content = View.inflate(this, R.layout.select_move_number, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(content); builder.setView(content);
builder.setTitle(R.string.goto_move); builder.setTitle(R.string.goto_move);
@ -1159,14 +1158,14 @@ public class DroidFish extends Activity implements GUIInterface {
} }
}; };
builder.setPositiveButton(R.string.ok, new Dialog.OnClickListener() { builder.setPositiveButton(R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
gotoMove.run(); gotoMove.run();
} }
}); });
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
final AlertDialog dialog = builder.create(); final AlertDialog dialog = builder.create();
moveNrView.setOnKeyListener(new OnKeyListener() { moveNrView.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
@ -1297,7 +1296,7 @@ public class DroidFish extends Activity implements GUIInterface {
return alert; return alert;
} }
case SELECT_PGN_SAVE_NEWFILE_DIALOG: { case SELECT_PGN_SAVE_NEWFILE_DIALOG: {
View content = View.inflate(this, R.layout.create_pgn_file, null); View content = View.inflate(this, R.layout.create_pgn_file, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(content); builder.setView(content);
builder.setTitle(R.string.select_pgn_file_save); builder.setTitle(R.string.select_pgn_file_save);
@ -1312,12 +1311,12 @@ public class DroidFish extends Activity implements GUIInterface {
} }
}; };
builder.setPositiveButton(R.string.ok, new Dialog.OnClickListener() { builder.setPositiveButton(R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
savePGN.run(); savePGN.run();
} }
}); });
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
final Dialog dialog = builder.create(); final Dialog dialog = builder.create();
fileNameView.setOnKeyListener(new OnKeyListener() { fileNameView.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event) {
@ -1419,57 +1418,57 @@ public class DroidFish extends Activity implements GUIInterface {
final TreeMap<String,String> headers = new TreeMap<String,String>(); final TreeMap<String,String> headers = new TreeMap<String,String>();
ctrl.getHeaders(headers); ctrl.getHeaders(headers);
AlertDialog.Builder builder = new AlertDialog.Builder(DroidFish.this); AlertDialog.Builder builder = new AlertDialog.Builder(DroidFish.this);
builder.setTitle(R.string.edit_headers); builder.setTitle(R.string.edit_headers);
View content = View.inflate(DroidFish.this, R.layout.edit_headers, null); View content = View.inflate(DroidFish.this, R.layout.edit_headers, null);
builder.setView(content); builder.setView(content);
final TextView event, site, date, round, white, black; final TextView event, site, date, round, white, black;
event = (TextView)content.findViewById(R.id.ed_header_event); event = (TextView)content.findViewById(R.id.ed_header_event);
site = (TextView)content.findViewById(R.id.ed_header_site); site = (TextView)content.findViewById(R.id.ed_header_site);
date = (TextView)content.findViewById(R.id.ed_header_date); date = (TextView)content.findViewById(R.id.ed_header_date);
round = (TextView)content.findViewById(R.id.ed_header_round); round = (TextView)content.findViewById(R.id.ed_header_round);
white = (TextView)content.findViewById(R.id.ed_header_white); white = (TextView)content.findViewById(R.id.ed_header_white);
black = (TextView)content.findViewById(R.id.ed_header_black); black = (TextView)content.findViewById(R.id.ed_header_black);
event.setText(headers.get("Event")); event.setText(headers.get("Event"));
site .setText(headers.get("Site")); site .setText(headers.get("Site"));
date .setText(headers.get("Date")); date .setText(headers.get("Date"));
round.setText(headers.get("Round")); round.setText(headers.get("Round"));
white.setText(headers.get("White")); white.setText(headers.get("White"));
black.setText(headers.get("Black")); black.setText(headers.get("Black"));
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
headers.put("Event", event.getText().toString().trim());
headers.put("Site", site .getText().toString().trim());
headers.put("Date", date .getText().toString().trim());
headers.put("Round", round.getText().toString().trim());
headers.put("White", white.getText().toString().trim());
headers.put("Black", black.getText().toString().trim());
ctrl.setHeaders(headers);
}
});
builder.show(); builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
headers.put("Event", event.getText().toString().trim());
headers.put("Site", site .getText().toString().trim());
headers.put("Date", date .getText().toString().trim());
headers.put("Round", round.getText().toString().trim());
headers.put("White", white.getText().toString().trim());
headers.put("Black", black.getText().toString().trim());
ctrl.setHeaders(headers);
}
});
builder.show();
break; break;
} }
case EDIT_COMMENTS: { case EDIT_COMMENTS: {
AlertDialog.Builder builder = new AlertDialog.Builder(DroidFish.this); AlertDialog.Builder builder = new AlertDialog.Builder(DroidFish.this);
builder.setTitle(R.string.edit_comments); builder.setTitle(R.string.edit_comments);
View content = View.inflate(DroidFish.this, R.layout.edit_comments, null); View content = View.inflate(DroidFish.this, R.layout.edit_comments, null);
builder.setView(content); builder.setView(content);
DroidChessController.CommentInfo commInfo = ctrl.getComments(); DroidChessController.CommentInfo commInfo = ctrl.getComments();
final TextView preComment, moveView, nag, postComment; final TextView preComment, moveView, nag, postComment;
preComment = (TextView)content.findViewById(R.id.ed_comments_pre); preComment = (TextView)content.findViewById(R.id.ed_comments_pre);
moveView = (TextView)content.findViewById(R.id.ed_comments_move); moveView = (TextView)content.findViewById(R.id.ed_comments_move);
nag = (TextView)content.findViewById(R.id.ed_comments_nag); nag = (TextView)content.findViewById(R.id.ed_comments_nag);
postComment = (TextView)content.findViewById(R.id.ed_comments_post); postComment = (TextView)content.findViewById(R.id.ed_comments_post);
preComment.setText(commInfo.preComment); preComment.setText(commInfo.preComment);
postComment.setText(commInfo.postComment); postComment.setText(commInfo.postComment);
moveView.setText(commInfo.move); moveView.setText(commInfo.move);
@ -1477,22 +1476,22 @@ public class DroidFish extends Activity implements GUIInterface {
if ((nagStr.length() == 0) && (commInfo.nag > 0)) if ((nagStr.length() == 0) && (commInfo.nag > 0))
nagStr = String.format("%d", commInfo.nag); nagStr = String.format("%d", commInfo.nag);
nag.setText(nagStr); nag.setText(nagStr);
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.ok, new Dialog.OnClickListener() { builder.setPositiveButton(R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String pre = preComment.getText().toString().trim(); String pre = preComment.getText().toString().trim();
String post = postComment.getText().toString().trim(); String post = postComment.getText().toString().trim();
int nagVal = Node.strToNag(nag.getText().toString()); int nagVal = Node.strToNag(nag.getText().toString());
DroidChessController.CommentInfo commInfo = new DroidChessController.CommentInfo(); DroidChessController.CommentInfo commInfo = new DroidChessController.CommentInfo();
commInfo.preComment = pre; commInfo.preComment = pre;
commInfo.postComment = post; commInfo.postComment = post;
commInfo.nag = nagVal; commInfo.nag = nagVal;
ctrl.setComments(commInfo); ctrl.setComments(commInfo);
} }
}); });
builder.show(); builder.show();
break; break;
} }

@ -86,7 +86,7 @@ public class SeekBarPreference extends Preference
currValBox.setOnClickListener(new OnClickListener() { currValBox.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
View content = View.inflate(SeekBarPreference.this.getContext(), R.layout.select_percentage, null); View content = View.inflate(SeekBarPreference.this.getContext(), R.layout.select_percentage, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(SeekBarPreference.this.getContext()); final AlertDialog.Builder builder = new AlertDialog.Builder(SeekBarPreference.this.getContext());
builder.setView(content); builder.setView(content);
String title = ""; String title = "";
@ -121,9 +121,9 @@ public class SeekBarPreference extends Preference
} }
}); });
builder.setPositiveButton("Ok", new Dialog.OnClickListener() { builder.setPositiveButton("Ok", new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
selectValue.run(); selectValue.run();
} }
}); });
builder.setNegativeButton("Cancel", null); builder.setNegativeButton("Cancel", null);

@ -347,7 +347,7 @@ public class EditBoard extends Activity {
checkValid(); checkValid();
dialog.cancel(); dialog.cancel();
} }
} }
}); });
AlertDialog alert = builder.create(); AlertDialog alert = builder.create();
return alert; return alert;
@ -406,9 +406,9 @@ public class EditBoard extends Activity {
return alert; return alert;
} }
case MOVCNT_DIALOG: { case MOVCNT_DIALOG: {
View content = View.inflate(this, R.layout.edit_move_counters, null); View content = View.inflate(this, R.layout.edit_move_counters, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(content); builder.setView(content);
builder.setTitle(R.string.edit_move_counters); builder.setTitle(R.string.edit_move_counters);
final EditText halfMoveClock = (EditText)content.findViewById(R.id.ed_cnt_halfmove); final EditText halfMoveClock = (EditText)content.findViewById(R.id.ed_cnt_halfmove);
@ -428,14 +428,14 @@ public class EditBoard extends Activity {
} }
}; };
builder.setPositiveButton("Ok", new Dialog.OnClickListener() { builder.setPositiveButton("Ok", new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
setCounters.run(); setCounters.run();
} }
}); });
builder.setNegativeButton("Cancel", null); builder.setNegativeButton("Cancel", null);
final Dialog dialog = builder.create(); final Dialog dialog = builder.create();
fullMoveCounter.setOnKeyListener(new OnKeyListener() { fullMoveCounter.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {

@ -118,7 +118,7 @@ public final class DroidBook {
StringBuilder ret = new StringBuilder(); StringBuilder ret = new StringBuilder();
ArrayList<Move> bookMoveList = new ArrayList<Move>(); ArrayList<Move> bookMoveList = new ArrayList<Move>();
List<BookEntry> bookMoves = getBook().getBookEntries(pos); List<BookEntry> bookMoves = getBook().getBookEntries(pos);
// Check legality // Check legality
if (bookMoves != null) { if (bookMoves != null) {
ArrayList<Move> legalMoves = new MoveGen().pseudoLegalMoves(pos); ArrayList<Move> legalMoves = new MoveGen().pseudoLegalMoves(pos);
@ -131,7 +131,7 @@ public final class DroidBook {
} }
} }
} }
if (bookMoves != null) { if (bookMoves != null) {
Collections.sort(bookMoves, new Comparator<BookEntry>() { Collections.sort(bookMoves, new Comparator<BookEntry>() {
public int compare(BookEntry arg0, BookEntry arg1) { public int compare(BookEntry arg0, BookEntry arg1) {

File diff suppressed because it is too large Load Diff

@ -35,6 +35,7 @@ public interface UCIEngine {
*/ */
public String readLineFromEngine(int timeoutMillis); public String readLineFromEngine(int timeoutMillis);
// FIXME!! Writes should be handled by separate thread.
/** Write a line to the engine. \n will be added automatically. */ /** Write a line to the engine. \n will be added automatically. */
public void writeLineToEngine(String data); public void writeLineToEngine(String data);

@ -49,6 +49,7 @@ public class CuckooChessEngine extends UCIEngineBase {
private Pipe guiToEngine; private Pipe guiToEngine;
private Pipe engineToGui; private Pipe engineToGui;
private NioInputStream inFromEngine; private NioInputStream inFromEngine;
private Thread engineThread;
public CuckooChessEngine() { public CuckooChessEngine() {
try { try {
@ -72,13 +73,14 @@ public class CuckooChessEngine extends UCIEngineBase {
} }
protected final void startProcess() { protected final void startProcess() {
new Thread(new Runnable() { engineThread = new Thread(new Runnable() {
public void run() { public void run() {
NioInputStream in = new NioInputStream(guiToEngine); NioInputStream in = new NioInputStream(guiToEngine);
NioPrintStream out = new NioPrintStream(engineToGui); NioPrintStream out = new NioPrintStream(engineToGui);
mainLoop(in, out); mainLoop(in, out);
} }
}).start(); });
engineThread.start();
} }
private final void mainLoop(NioInputStream is, NioPrintStream os) { private final void mainLoop(NioInputStream is, NioPrintStream os) {
@ -93,6 +95,8 @@ public class CuckooChessEngine extends UCIEngineBase {
@Override @Override
public final String readLineFromEngine(int timeoutMillis) { public final String readLineFromEngine(int timeoutMillis) {
if ((engineThread != null) && !engineThread.isAlive())
return null;
String ret = inFromEngine.readLine(timeoutMillis); String ret = inFromEngine.readLine(timeoutMillis);
if (ret == null) if (ret == null)
return null; return null;

@ -27,6 +27,8 @@ import org.petero.droidfish.GUIInterface;
import org.petero.droidfish.GameMode; import org.petero.droidfish.GameMode;
import org.petero.droidfish.PGNOptions; import org.petero.droidfish.PGNOptions;
import org.petero.droidfish.engine.DroidComputerPlayer; import org.petero.droidfish.engine.DroidComputerPlayer;
import org.petero.droidfish.engine.DroidComputerPlayer.SearchRequest;
import org.petero.droidfish.engine.DroidComputerPlayer.SearchType;
import org.petero.droidfish.gamelogic.Game.GameState; import org.petero.droidfish.gamelogic.Game.GameState;
import org.petero.droidfish.gamelogic.GameTree.Node; import org.petero.droidfish.gamelogic.GameTree.Node;
@ -43,8 +45,6 @@ public class DroidChessController {
private GUIInterface gui; private GUIInterface gui;
private GameMode gameMode; private GameMode gameMode;
private PGNOptions pgnOptions; private PGNOptions pgnOptions;
private Thread computerThread;
private Thread analysisThread;
private String engine = ""; private String engine = "";
private int strength = 1000; private int strength = 1000;
@ -60,30 +60,30 @@ public class DroidChessController {
/** Partial move that needs promotion choice to be completed. */ /** Partial move that needs promotion choice to be completed. */
private Move promoteMove; private Move promoteMove;
private Object shutdownEngineLock = new Object(); private int searchId;
/** Constructor. */ /** Constructor. */
public DroidChessController(GUIInterface gui, PgnToken.PgnTokenReceiver gameTextListener, PGNOptions options) { public DroidChessController(GUIInterface gui, PgnToken.PgnTokenReceiver gameTextListener, PGNOptions options) {
this.gui = gui; this.gui = gui;
this.gameTextListener = gameTextListener; this.gameTextListener = gameTextListener;
gameMode = new GameMode(GameMode.TWO_PLAYERS);
pgnOptions = options; pgnOptions = options;
listener = new SearchListener(); listener = new SearchListener();
searchId = 0;
} }
/** Start a new game. */ /** Start a new game. */
public final synchronized void newGame(GameMode gameMode) { public final synchronized void newGame(GameMode gameMode) {
ss.searchResultWanted = false; boolean updateGui = abortSearch();
boolean updateGui = stopComputerThinking();
updateGui |= stopAnalysis();
if (updateGui) if (updateGui)
updateGUI(); updateGUI();
this.gameMode = gameMode; this.gameMode = gameMode;
ponderMove = null;
if (computerPlayer == null) { if (computerPlayer == null) {
computerPlayer = new DroidComputerPlayer(engine, listener); computerPlayer = new DroidComputerPlayer(listener);
computerPlayer.setBookOptions(bookOptions); computerPlayer.setBookOptions(bookOptions);
} }
computerPlayer.queueStartEngine(searchId, engine);
searchId++;
game = new Game(gameTextListener, timeControl, movesPerSession, timeIncrement); game = new Game(gameTextListener, timeControl, movesPerSession, timeIncrement);
computerPlayer.clearTT(); computerPlayer.clearTT();
setPlayerNames(game); setPlayerNames(game);
@ -92,7 +92,7 @@ public class DroidChessController {
/** Start playing a new game. Should be called after newGame(). */ /** Start playing a new game. Should be called after newGame(). */
public final synchronized void startGame() { public final synchronized void startGame() {
updateComputeThreads(true); updateComputeThreads();
setSelection(); setSelection();
updateGUI(); updateGUI();
updateGameMode(); updateGameMode();
@ -117,15 +117,13 @@ public class DroidChessController {
public final synchronized void setGameMode(GameMode newMode) { public final synchronized void setGameMode(GameMode newMode) {
if (!gameMode.equals(newMode)) { if (!gameMode.equals(newMode)) {
if (newMode.humansTurn(game.currPos().whiteMove)) if (newMode.humansTurn(game.currPos().whiteMove))
ss.searchResultWanted = false; searchId++;
gameMode = newMode; gameMode = newMode;
if (!gameMode.playerWhite() || !gameMode.playerBlack()) if (!gameMode.playerWhite() || !gameMode.playerBlack())
setPlayerNames(game); // If computer player involved, set player names setPlayerNames(game); // If computer player involved, set player names
updateGameMode(); updateGameMode();
ponderMove = null; abortSearch();
ss.searchResultWanted = false; updateComputeThreads();
stopComputerThinking();
updateComputeThreads(true);
updateGUI(); updateGUI();
} }
} }
@ -136,12 +134,6 @@ public class DroidChessController {
bookOptions = options; bookOptions = options;
if (computerPlayer != null) { if (computerPlayer != null) {
computerPlayer.setBookOptions(bookOptions); computerPlayer.setBookOptions(bookOptions);
if (analysisThread != null) {
boolean updateGui = stopAnalysis();
updateGui |= startAnalysis();
if (updateGui)
updateGUI();
}
updateBookHints(); updateBookHints();
} }
} }
@ -157,9 +149,9 @@ public class DroidChessController {
if (newEngine || (strength != this.strength)) { if (newEngine || (strength != this.strength)) {
this.engine = engine; this.engine = engine;
this.strength = strength; this.strength = strength;
if (newEngine && ((analysisThread != null) || (computerThread != null))) { if (game != null) {
stopAnalysis(); abortSearch();
updateComputeThreads(true); updateComputeThreads();
updateGUI(); updateGUI();
} }
} }
@ -169,7 +161,7 @@ public class DroidChessController {
public final synchronized void prefsChanged() { public final synchronized void prefsChanged() {
updateBookHints(); updateBookHints();
updateMoveList(); updateMoveList();
listener.prefsChanged(); listener.prefsChanged(searchId);
} }
/** De-serialize from byte array. */ /** De-serialize from byte array. */
@ -204,29 +196,27 @@ public class DroidChessController {
if (!newGame.readPGN(fenPgn, pgnOptions)) if (!newGame.readPGN(fenPgn, pgnOptions))
throw e; throw e;
} }
ss.searchResultWanted = false; searchId++;
game = newGame; game = newGame;
if (computerPlayer != null)
computerPlayer.clearTT();
gameTextListener.clear(); gameTextListener.clear();
updateGameMode(); updateGameMode();
stopAnalysis(); abortSearch();
stopComputerThinking();
computerPlayer.clearTT(); computerPlayer.clearTT();
ponderMove = null; updateComputeThreads();
updateComputeThreads(true);
gui.setSelection(-1); gui.setSelection(-1);
updateGUI(); updateGUI();
} }
/** True if human's turn to make a move. (True in analysis mode.) */ /** True if human's turn to make a move. (True in analysis mode.) */
public final synchronized boolean humansTurn() { public final synchronized boolean humansTurn() {
if (game == null)
return false;
return gameMode.humansTurn(game.currPos().whiteMove); return gameMode.humansTurn(game.currPos().whiteMove);
} }
/** Return true if computer player is using CPU power. */ /** Return true if computer player is using CPU power. */
public final synchronized boolean computerBusy() { public final synchronized boolean computerBusy() {
return (computerThread != null) || (analysisThread != null); return (computerPlayer != null) && computerPlayer.computerBusy();
} }
/** Make a move for a human player. */ /** Make a move for a human player. */
@ -235,13 +225,12 @@ public class DroidChessController {
Position oldPos = new Position(game.currPos()); Position oldPos = new Position(game.currPos());
if (doMove(m)) { if (doMove(m)) {
if (m.equals(ponderMove) && !gameMode.analysisMode() && if (m.equals(ponderMove) && !gameMode.analysisMode() &&
(analysisThread == null) && (computerThread != null)) { (computerPlayer.getSearchType() == SearchType.PONDER)) {
computerPlayer.ponderHit(oldPos, ponderMove); computerPlayer.ponderHit(searchId);
ponderMove = null;
} else { } else {
ss.searchResultWanted = false; abortSearch();
stopAnalysis(); updateComputeThreads();
stopComputerThinking();
updateComputeThreads(true);
} }
setAnimMove(oldPos, m, true); setAnimMove(oldPos, m, true);
updateGUI(); updateGUI();
@ -251,7 +240,7 @@ public class DroidChessController {
} }
} }
/** Report promotion choice for incomplete move. /** Report promotion choice for incomplete move.
* @param choice 0=queen, 1=rook, 2=bishop, 3=knight. */ * @param choice 0=queen, 1=rook, 2=bishop, 3=knight. */
public final synchronized void reportPromotePiece(int choice) { public final synchronized void reportPromotePiece(int choice) {
if (promoteMove == null) if (promoteMove == null)
@ -283,11 +272,8 @@ public class DroidChessController {
if (humansTurn()) { if (humansTurn()) {
int varNo = game.tree.addMove("--", "", 0, "", ""); int varNo = game.tree.addMove("--", "", 0, "", "");
game.tree.goForward(varNo); game.tree.goForward(varNo);
ss.searchResultWanted = false; abortSearch();
stopAnalysis(); updateComputeThreads();
stopComputerThinking();
ponderMove = null;
updateComputeThreads(true);
updateGUI(); updateGUI();
gui.setSelection(-1); gui.setSelection(-1);
} }
@ -312,11 +298,9 @@ public class DroidChessController {
/** Undo last move. Does not truncate game tree. */ /** Undo last move. Does not truncate game tree. */
public final synchronized void undoMove() { public final synchronized void undoMove() {
if (game.getLastMove() != null) { if (game.getLastMove() != null) {
ss.searchResultWanted = false; abortSearch();
stopAnalysis();
stopComputerThinking();
boolean didUndo = undoMoveNoUpdate(); boolean didUndo = undoMoveNoUpdate();
updateComputeThreads(true); updateComputeThreads();
setSelection(); setSelection();
if (didUndo) if (didUndo)
setAnimMove(game.currPos(), game.getNextMove(), false); setAnimMove(game.currPos(), game.getNextMove(), false);
@ -327,11 +311,9 @@ public class DroidChessController {
/** Redo last move. Follows default variation. */ /** Redo last move. Follows default variation. */
public final synchronized void redoMove() { public final synchronized void redoMove() {
if (game.canRedoMove()) { if (game.canRedoMove()) {
ss.searchResultWanted = false; abortSearch();
stopAnalysis();
stopComputerThinking();
redoMoveNoUpdate(); redoMoveNoUpdate();
updateComputeThreads(true); updateComputeThreads();
setSelection(); setSelection();
setAnimMove(game.prevPos(), game.getLastMove(), true); setAnimMove(game.prevPos(), game.getLastMove(), true);
updateGUI(); updateGUI();
@ -359,11 +341,8 @@ public class DroidChessController {
needUpdate = true; needUpdate = true;
} }
if (needUpdate) { if (needUpdate) {
ss.searchResultWanted = false; abortSearch();
stopAnalysis(); updateComputeThreads();
stopComputerThinking();
ponderMove = null;
updateComputeThreads(true);
setSelection(); setSelection();
updateGUI(); updateGUI();
} }
@ -380,11 +359,8 @@ public class DroidChessController {
break; break;
} }
if (needUpdate) { if (needUpdate) {
ss.searchResultWanted = false; abortSearch();
stopAnalysis(); updateComputeThreads();
stopComputerThinking();
ponderMove = null;
updateComputeThreads(true);
setSelection(); setSelection();
updateGUI(); updateGUI();
} }
@ -396,7 +372,6 @@ public class DroidChessController {
return; return;
if (!game.goNode(node)) if (!game.goNode(node))
return; return;
ponderMove = null;
if (!humansTurn()) { if (!humansTurn()) {
if (game.getLastMove() != null) { if (game.getLastMove() != null) {
game.undoMove(); game.undoMove();
@ -404,11 +379,8 @@ public class DroidChessController {
game.redoMove(); game.redoMove();
} }
} }
ss.searchResultWanted = false; abortSearch();
stopAnalysis(); updateComputeThreads();
stopComputerThinking();
ponderMove = null;
updateComputeThreads(true);
setSelection(); setSelection();
updateGUI(); updateGUI();
} }
@ -426,12 +398,9 @@ public class DroidChessController {
/** Go to a new variation in the game tree. */ /** Go to a new variation in the game tree. */
public final synchronized void changeVariation(int delta) { public final synchronized void changeVariation(int delta) {
if (game.numVariations() > 1) { if (game.numVariations() > 1) {
ss.searchResultWanted = false; abortSearch();
stopAnalysis();
stopComputerThinking();
game.changeVariation(delta); game.changeVariation(delta);
ponderMove = null; updateComputeThreads();
updateComputeThreads(true);
setSelection(); setSelection();
updateGUI(); updateGUI();
} }
@ -439,12 +408,9 @@ public class DroidChessController {
/** Delete whole game sub-tree rooted at current position. */ /** Delete whole game sub-tree rooted at current position. */
public final synchronized void removeSubTree() { public final synchronized void removeSubTree() {
ss.searchResultWanted = false; abortSearch();
stopAnalysis();
stopComputerThinking();
game.removeSubTree(); game.removeSubTree();
ponderMove = null; updateComputeThreads();
updateComputeThreads(true);
setSelection(); setSelection();
updateGUI(); updateGUI();
} }
@ -459,7 +425,7 @@ public class DroidChessController {
/** Add a variation to the game tree. /** Add a variation to the game tree.
* @param preComment Comment to add before first move. * @param preComment Comment to add before first move.
* @param pvMoves List of moves in variation. * @param pvMoves List of moves in variation.
* @param updateDefault If true, make this the default variation. */ * @param updateDefault If true, make this the default variation. */
public final synchronized void addVariation(String preComment, List<Move> pvMoves, boolean updateDefault) { public final synchronized void addVariation(String preComment, List<Move> pvMoves, boolean updateDefault) {
for (int i = 0; i < pvMoves.size(); i++) { for (int i = 0; i < pvMoves.size(); i++) {
@ -508,43 +474,34 @@ public class DroidChessController {
if (numPV > maxPV()) numPV = maxPV(); if (numPV > maxPV()) numPV = maxPV();
if (numPV != this.numPV) { if (numPV != this.numPV) {
this.numPV = numPV; this.numPV = numPV;
if (analysisThread != null) { abortSearch();
stopAnalysis(); updateComputeThreads();
ponderMove = null; updateGUI();
updateComputeThreads(true);
updateGUI();
}
} }
} }
/** Request computer player to make a move immediately. */ /** Request computer player to make a move immediately. */
public final synchronized void stopSearch() { public final synchronized void stopSearch() {
if (computerThread != null) { if (!humansTurn() && (computerPlayer != null))
computerPlayer.stopSearch(); computerPlayer.stopSearch();
}
} }
/** Stop ponder search. */ /** Stop ponder search. */
public final synchronized void stopPonder() { public final synchronized void stopPonder() {
if ((computerThread != null) && humansTurn()) { if (humansTurn() && (computerPlayer != null)) {
boolean updateGui = stopComputerThinking(); if (computerPlayer.getSearchType() == SearchType.PONDER) {
if (updateGui) boolean updateGui = abortSearch();
updateGUI(); if (updateGui)
updateGUI();
}
} }
} }
/** Shut down chess engine process. */ /** Shut down chess engine process. */
public final synchronized void shutdownEngine() { public final synchronized void shutdownEngine() {
synchronized (shutdownEngineLock) { gameMode = new GameMode(GameMode.TWO_PLAYERS);
gameMode = new GameMode(GameMode.TWO_PLAYERS); abortSearch();
ss.searchResultWanted = false; computerPlayer.shutdownEngine();
boolean updateGui = stopComputerThinking();
updateGui |= stopAnalysis();
if (updateGui)
updateGUI();
ponderMove = null;
computerPlayer.shutdownEngine();
}
} }
/** Get PGN header tags and values. */ /** Get PGN header tags and values. */
@ -600,17 +557,19 @@ public class DroidChessController {
private String bookInfo = ""; private String bookInfo = "";
private List<Move> bookMoves = null; private List<Move> bookMoves = null;
private Move ponderMove = null;
private ArrayList<PvInfo> pvInfoV = new ArrayList<PvInfo>(); private ArrayList<PvInfo> pvInfoV = new ArrayList<PvInfo>();
public final void clearSearchInfo() { public final void clearSearchInfo(int id) {
ponderMove = null;
pvInfoV.clear(); pvInfoV.clear();
currDepth = 0; currDepth = 0;
bookInfo = ""; bookInfo = "";
bookMoves = null; bookMoves = null;
setSearchInfo(); setSearchInfo(id);
} }
private final void setSearchInfo() { private final void setSearchInfo(final int id) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
for (int i = 0; i < pvInfoV.size(); i++) { for (int i = 0; i < pvInfoV.size(); i++) {
PvInfo pvi = pvInfoV.get(i); PvInfo pvi = pvInfoV.get(i);
@ -639,35 +598,42 @@ public class DroidChessController {
: ""; : "";
final String newPV = buf.toString(); final String newPV = buf.toString();
final String newBookInfo = bookInfo; final String newBookInfo = bookInfo;
final SearchStatus localSS = ss; final ArrayList<ArrayList<Move>> pvMoves = new ArrayList<ArrayList<Move>>();
for (int i = 0; i < pvInfoV.size(); i++) {
if (ponderMove != null) {
ArrayList<Move> tmp = new ArrayList<Move>();
tmp.add(ponderMove);
for (Move m : pvInfoV.get(i).pv)
tmp.add(m);
pvMoves.add(tmp);
} else {
pvMoves.add(pvInfoV.get(i).pv);
}
}
gui.runOnUIThread(new Runnable() { gui.runOnUIThread(new Runnable() {
public void run() { public void run() {
if (!localSS.searchResultWanted && (bookMoves != null)) setThinkingInfo(id, pvMoves, newPV, statStr, newBookInfo, bookMoves);
return;
ArrayList<ArrayList<Move>> pvMoves = new ArrayList<ArrayList<Move>>();
for (int i = 0; i < pvInfoV.size(); i++)
pvMoves.add(pvInfoV.get(i).pv);
gui.setThinkingInfo(newPV, statStr, newBookInfo, pvMoves, bookMoves);
} }
}); });
} }
@Override @Override
public void notifyDepth(int depth) { public void notifyDepth(int id, int depth) {
currDepth = depth; currDepth = depth;
setSearchInfo(); setSearchInfo(id);
} }
@Override @Override
public void notifyCurrMove(Position pos, Move m, int moveNr) { public void notifyCurrMove(int id, Position pos, Move m, int moveNr) {
currMove = TextIO.moveToString(pos, m, false); currMove = TextIO.moveToString(pos, m, false);
currMoveNr = moveNr; currMoveNr = moveNr;
setSearchInfo(); setSearchInfo(id);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public void notifyPV(Position pos, ArrayList<PvInfo> pvInfo, boolean isPonder) { public void notifyPV(int id, Position pos, ArrayList<PvInfo> pvInfo, Move ponderMove) {
this.ponderMove = ponderMove;
pvInfoV = (ArrayList<PvInfo>) pvInfo.clone(); pvInfoV = (ArrayList<PvInfo>) pvInfo.clone();
for (PvInfo pv : pvInfo) { for (PvInfo pv : pvInfo) {
currTime = pv.time; currTime = pv.time;
@ -677,65 +643,85 @@ public class DroidChessController {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
Position tmpPos = new Position(pos); Position tmpPos = new Position(pos);
UndoInfo ui = new UndoInfo(); UndoInfo ui = new UndoInfo();
boolean first = true; if (ponderMove != null) {
String moveStr = TextIO.moveToString(tmpPos, ponderMove, false);
buf.append(String.format(" [%s]", moveStr));
tmpPos.makeMove(ponderMove, ui);
}
for (Move m : pv.pv) { for (Move m : pv.pv) {
String moveStr = TextIO.moveToString(tmpPos, m, false); String moveStr = TextIO.moveToString(tmpPos, m, false);
if (first && isPonder) { buf.append(String.format(" %s", moveStr));
buf.append(String.format(" [%s]", moveStr));
first = false;
} else {
buf.append(String.format(" %s", moveStr));
}
tmpPos.makeMove(m, ui); tmpPos.makeMove(m, ui);
} }
pv.pvStr = buf.toString(); pv.pvStr = buf.toString();
} }
whiteMove = pos.whiteMove ^ isPonder; whiteMove = pos.whiteMove ^ (ponderMove != null);
setSearchInfo(); setSearchInfo(id);
} }
@Override @Override
public void notifyStats(int nodes, int nps, int time) { public void notifyStats(int id, int nodes, int nps, int time) {
currNodes = nodes; currNodes = nodes;
currNps = nps; currNps = nps;
currTime = time; currTime = time;
setSearchInfo(); setSearchInfo(id);
} }
@Override @Override
public void notifyBookInfo(String bookInfo, List<Move> moveList) { public void notifyBookInfo(int id, String bookInfo, List<Move> moveList) {
this.bookInfo = bookInfo; this.bookInfo = bookInfo;
bookMoves = moveList; bookMoves = moveList;
setSearchInfo(); setSearchInfo(id);
} }
public void prefsChanged() { public void prefsChanged(int id) {
setSearchInfo(); setSearchInfo(id);
} }
@Override @Override
public void notifySearchResult(Game g, String cmd, Move ponder) { public void notifySearchResult(final int id, final String cmd, final Move ponder) {
makeComputerMove(g, cmd, ponder); new Thread(new Runnable() {
public void run() {
gui.runOnUIThread(new Runnable() {
public void run() {
makeComputerMove(id, cmd, ponder);
}
});
}
}).start();
} }
@Override
public void notifyEngineName(final String engineName) {
gui.runOnUIThread(new Runnable() {
public void run() {
updatePlayerNames(engineName);
}
});
}
}
/** Discard current search. Return true if GUI update needed. */
private final boolean abortSearch() {
ponderMove = null;
searchId++;
if (computerPlayer == null)
return false;
if (computerPlayer.stopSearch()) {
listener.clearSearchInfo(searchId);
return true;
}
return false;
} }
private final void updateBookHints() { private final void updateBookHints() {
if (gameMode != null) { if (humansTurn()) {
boolean analysis = gameMode.analysisMode(); Pair<String, ArrayList<Move>> bi = computerPlayer.getBookHints(game.currPos());
if (!analysis && humansTurn()) { listener.notifyBookInfo(searchId, bi.first, bi.second);
ss = new SearchStatus();
Pair<String, ArrayList<Move>> bi = computerPlayer.getBookHints(game.currPos());
listener.notifyBookInfo(bi.first, bi.second);
}
} }
} }
private final static class SearchStatus {
boolean searchResultWanted = true;
}
private SearchStatus ss = new SearchStatus();
private final void updateGameMode() { private final void updateGameMode() {
if (game != null) { if (game != null) {
boolean gamePaused = !gameMode.clocksActive() || (humansTurn() && guiPaused); boolean gamePaused = !gameMode.clocksActive() || (humansTurn() && guiPaused);
@ -747,40 +733,98 @@ public class DroidChessController {
} }
/** Start/stop computer thinking/analysis as appropriate. */ /** Start/stop computer thinking/analysis as appropriate. */
private final void updateComputeThreads(boolean clearPV) { private final void updateComputeThreads() {
boolean analysis = gameMode.analysisMode(); boolean alive = game.tree.getGameState() == GameState.ALIVE;
boolean computersTurn = !humansTurn(); boolean analysis = gameMode.analysisMode() && alive;
boolean ponder = gui.ponderMode() && !analysis && !computersTurn && (ponderMove != null); boolean computersTurn = !humansTurn() && alive;
if (!analysis) boolean ponder = gui.ponderMode() && !analysis && !computersTurn && (ponderMove != null) && alive;
stopAnalysis(); if (!analysis && !(computersTurn || ponder))
if (!(computersTurn || ponder)) computerPlayer.stopSearch();
stopComputerThinking(); listener.clearSearchInfo(searchId);
if (clearPV) { updateBookHints();
listener.clearSearchInfo(); if (!computerPlayer.sameSearchId(searchId)) {
updateBookHints(); if (analysis) {
Pair<Position, ArrayList<Move>> ph = game.getUCIHistory();
SearchRequest sr = DroidComputerPlayer.SearchRequest.analyzeRequest(
searchId, ph.first, ph.second,
new Position(game.currPos()),
game.haveDrawOffer(), engine,
gui.engineThreads(), numPV);
computerPlayer.queueAnalyzeRequest(sr);
} else if (computersTurn || ponder) {
listener.clearSearchInfo(searchId);
listener.notifyBookInfo(searchId, "", null);
final Pair<Position, ArrayList<Move>> ph = game.getUCIHistory();
Position currPos = new Position(game.currPos());
long now = System.currentTimeMillis();
int wTime = game.timeController.getRemainingTime(true, now);
int bTime = game.timeController.getRemainingTime(false, now);
int inc = game.timeController.getIncrement();
int movesToGo = game.timeController.getMovesToTC();
if (ponder && !currPos.whiteMove && (movesToGo > 0)) {
movesToGo--;
if (movesToGo <= 0)
movesToGo += game.timeController.getMovesPerSession();
}
final Move fPonderMove = ponder ? ponderMove : null;
SearchRequest sr = DroidComputerPlayer.SearchRequest.searchRequest(
searchId, now, ph.first, ph.second, currPos,
game.haveDrawOffer(),
wTime, bTime, inc, movesToGo,
gui.ponderMode(), fPonderMove,
engine, gui.engineThreads(),
strength);
computerPlayer.queueSearchRequest(sr);
}
} }
if (analysis) }
startAnalysis();
if (computersTurn || ponder) private final synchronized void makeComputerMove(int id, final String cmd, final Move ponder) {
startComputerThinking(ponder); if (searchId != id)
return;
searchId++;
Position oldPos = new Position(game.currPos());
game.processString(cmd);
ponderMove = ponder;
updateGameMode();
gui.computerMoveMade();
listener.clearSearchInfo(searchId);
updateComputeThreads();
setSelection();
setAnimMove(oldPos, game.getLastMove(), true);
updateGUI();
} }
private final void setPlayerNames(Game game) { private final void setPlayerNames(Game game) {
if (game != null) { if (game != null) {
String engine = (computerPlayer != null) ? computerPlayer.getEngineName() : String engine = "Computer";
"Computer"; if (computerPlayer != null) {
engine = computerPlayer.getEngineName();
if (strength < 1000)
engine += String.format(" (%.1f%%)", strength * 0.1);
}
String white = gameMode.playerWhite() ? "Player" : engine; String white = gameMode.playerWhite() ? "Player" : engine;
String black = gameMode.playerBlack() ? "Player" : engine; String black = gameMode.playerBlack() ? "Player" : engine;
game.tree.setPlayerNames(white, black); game.tree.setPlayerNames(white, black);
} }
} }
private final synchronized void updatePlayerNames(String engineName) {
if (game != null) {
if (strength < 1000)
engineName += String.format(" (%.1f%%)", strength * 0.1);
String white = gameMode.playerWhite() ? game.tree.white : engineName;
String black = gameMode.playerBlack() ? game.tree.black : engineName;
game.tree.setPlayerNames(white, black);
updateMoveList();
}
}
private final boolean undoMoveNoUpdate() { private final boolean undoMoveNoUpdate() {
if (game.getLastMove() == null) if (game.getLastMove() == null)
return false; return false;
ss.searchResultWanted = false; searchId++;
game.undoMove(); game.undoMove();
ponderMove = null;
if (!humansTurn()) { if (!humansTurn()) {
if (game.getLastMove() != null) { if (game.getLastMove() != null) {
game.undoMove(); game.undoMove();
@ -802,9 +846,8 @@ public class DroidChessController {
private final void redoMoveNoUpdate() { private final void redoMoveNoUpdate() {
if (game.canRedoMove()) { if (game.canRedoMove()) {
ss.searchResultWanted = false; searchId++;
game.redoMove(); game.redoMove();
ponderMove = null;
if (!humansTurn() && game.canRedoMove()) { if (!humansTurn() && game.canRedoMove()) {
game.redoMove(); game.redoMove();
if (!humansTurn()) if (!humansTurn())
@ -846,13 +889,14 @@ public class DroidChessController {
if (s.state == Game.GameState.ALIVE) { if (s.state == Game.GameState.ALIVE) {
s.moveNr = game.currPos().fullMoveCounter; s.moveNr = game.currPos().fullMoveCounter;
s.white = game.currPos().whiteMove; s.white = game.currPos().whiteMove;
if (computerThread != null) DroidComputerPlayer.SearchType st = SearchType.NONE;
if (humansTurn()) if (computerPlayer != null)
s.ponder = true; st = computerPlayer.getSearchType();
else switch (st) {
s.thinking = true; case SEARCH: s.thinking = true; break;
if (analysisThread != null) case PONDER: s.ponder = true; break;
s.analyzing = true; case ANALYZE: s.analyzing = true; break;
}
} else { } else {
if ((s.state == GameState.DRAW_REP) || (s.state == GameState.DRAW_50)) if ((s.state == GameState.DRAW_REP) || (s.state == GameState.DRAW_50))
s.drawInfo = game.getDrawInfo(); s.drawInfo = game.getDrawInfo();
@ -880,6 +924,12 @@ public class DroidChessController {
updateRemainingTime(); updateRemainingTime();
} }
private final synchronized void setThinkingInfo(int id, ArrayList<ArrayList<Move>> pvMoves, String pvStr,
String statStr, String bookInfo, List<Move> bookMoves) {
if (id == searchId)
gui.setThinkingInfo(pvStr, statStr, bookInfo, pvMoves, bookMoves);
}
private final void updateMoveList() { private final void updateMoveList() {
if (game == null) if (game == null)
return; return;
@ -909,120 +959,6 @@ public class DroidChessController {
gui.setAnimMove(sourcePos, move, forward); gui.setAnimMove(sourcePos, move, forward);
} }
private final synchronized void startComputerThinking(boolean ponder) {
if (analysisThread != null) return;
if (game.getGameState() != GameState.ALIVE) return;
if (computerThread == null) {
ss = new SearchStatus();
final Pair<Position, ArrayList<Move>> ph = game.getUCIHistory();
final Game g = game;
final boolean haveDrawOffer = g.haveDrawOffer();
final Position currPos = new Position(g.currPos());
long now = System.currentTimeMillis();
final int wTime = game.timeController.getRemainingTime(true, now);
final int bTime = game.timeController.getRemainingTime(false, now);
final int inc = game.timeController.getIncrement();
int movesToGo = game.timeController.getMovesToTC();
if (ponder && !currPos.whiteMove && (movesToGo > 0)) {
movesToGo--;
if (movesToGo <= 0)
movesToGo += game.timeController.getMovesPerSession();
}
final int fMovesToGo = movesToGo;
final Move fPonderMove = ponder ? ponderMove : null;
computerThread = new Thread(new Runnable() {
public void run() {
computerPlayer.doSearch(ph.first, ph.second, currPos, haveDrawOffer,
wTime, bTime, inc, fMovesToGo,
gui.ponderMode(), fPonderMove,
gui.engineThreads(),
engine, strength, g);
}
});
listener.clearSearchInfo();
computerPlayer.shouldStop = false;
computerThread.start();
}
}
private final void makeComputerMove(final Game g, final String cmd, final Move ponder) {
final SearchStatus localSS = ss;
gui.runOnUIThread(new Runnable() {
public void run() {
synchronized (shutdownEngineLock) {
if (!localSS.searchResultWanted)
return;
Position oldPos = new Position(g.currPos());
g.processString(cmd);
ponderMove = ponder;
updateGameMode();
gui.computerMoveMade();
listener.clearSearchInfo();
stopComputerThinking();
stopAnalysis(); // To force analysis to restart for new position
updateComputeThreads(true);
setSelection();
setAnimMove(oldPos, g.getLastMove(), true);
updateGUI();
}
}
});
}
private final synchronized boolean stopComputerThinking() {
if (computerThread != null) {
computerPlayer.stopSearch();
try {
computerThread.join();
} catch (InterruptedException ex) {
System.out.printf("Could not stop computer thread%n");
}
computerThread = null;
return true;
}
return false;
}
private final synchronized boolean startAnalysis() {
if (gameMode.analysisMode()) {
if (computerThread != null) return false;
if (analysisThread == null) {
ss = new SearchStatus();
final Pair<Position, ArrayList<Move>> ph = game.getUCIHistory();
final boolean haveDrawOffer = game.haveDrawOffer();
final Position currPos = new Position(game.currPos());
final boolean alive = game.tree.getGameState() == GameState.ALIVE;
analysisThread = new Thread(new Runnable() {
public void run() {
if (alive)
computerPlayer.analyze(ph.first, ph.second, currPos, haveDrawOffer,
gui.engineThreads(), engine, numPV);
}
});
listener.clearSearchInfo();
computerPlayer.shouldStop = false;
analysisThread.start();
return true;
}
}
return false;
}
private final synchronized boolean stopAnalysis() {
if (analysisThread != null) {
computerPlayer.stopSearch();
try {
analysisThread.join();
} catch (InterruptedException ex) {
System.out.printf("Could not stop analysis thread%n");
}
analysisThread = null;
listener.clearSearchInfo();
return true;
}
return false;
}
private final boolean findValidDrawClaim() { private final boolean findValidDrawClaim() {
if (game.getGameState() != GameState.ALIVE) return true; if (game.getGameState() != GameState.ALIVE) return true;
game.processString("draw accept"); game.processString("draw accept");

@ -65,18 +65,29 @@ public interface SearchListener {
this.lowerBound = lowerBound; this.lowerBound = lowerBound;
this.pv = pv; this.pv = pv;
} }
public final void removeFirstMove() {
if (!pv.isEmpty())
pv.remove(0);
}
} }
public void notifyDepth(int depth); /** Report current engine search depth. */
public void notifyCurrMove(Position pos, Move m, int moveNr); public void notifyDepth(int id, int depth);
public void notifyPV(Position pos, ArrayList<PvInfo> pvInfo, boolean isPonder);
public void notifyStats(int nodes, int nps, int time);
public void notifyBookInfo(String bookInfo, List<Move> moveList);
public void notifySearchResult(Game g, String cmd, Move ponder); /** Report the move, valid in position pos, that the engine is currently searching. */
public void notifyCurrMove(int id, Position pos, Move m, int moveNr);
/**
* Report PV information. If ponderMove is non-null, ponderMove is the first move
* to play from position pos.
*/
public void notifyPV(int id, Position pos, ArrayList<PvInfo> pvInfo, Move ponderMove);
/** Report search statistics. */
public void notifyStats(int id, int nodes, int nps, int time);
/** Report opening book information. */
public void notifyBookInfo(int id, String bookInfo, List<Move> moveList);
/** Report move (or command, such as "resign") played by the engine. */
public void notifySearchResult(int id, String cmd, Move ponder);
/** Report engine name. */
public void notifyEngineName(String engineName);
} }