mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-15 10:22:40 +01:00
DroidFish: Implemented support for external UCI engines.
This commit is contained in:
@@ -42,6 +42,10 @@
|
|||||||
<item
|
<item
|
||||||
android:id="@+id/select_book"
|
android:id="@+id/select_book"
|
||||||
android:title="@string/option_select_book">
|
android:title="@string/option_select_book">
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:id="@+id/select_engine"
|
||||||
|
android:title="@string/option_select_engine">
|
||||||
</item>
|
</item>
|
||||||
<item
|
<item
|
||||||
android:id="@+id/set_color_theme"
|
android:id="@+id/set_color_theme"
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">DroidFish</string>
|
<string name="app_name">DroidFish</string>
|
||||||
<string-array name="engine_texts">
|
|
||||||
<item>Stockfish</item>
|
|
||||||
<item>CuckooChess</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="engine_values">
|
|
||||||
<item>stockfish</item>
|
|
||||||
<item>cuckoochess</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="engine_threads_texts">
|
<string-array name="engine_threads_texts">
|
||||||
<item>Automatisch</item>
|
<item>Automatisch</item>
|
||||||
<item>1</item>
|
<item>1</item>
|
||||||
@@ -414,8 +407,6 @@ wenn Sie es nicht aktiv nutzen.\
|
|||||||
<string name="prefs_autoSwapSides_title">Automatischer Seitenwechsel</string>
|
<string name="prefs_autoSwapSides_title">Automatischer Seitenwechsel</string>
|
||||||
<string name="prefs_autoSwapSides_summary">Seiten beim Start einer neuen Partie automatisch wechseln (Einstellung <i>Ansicht drehen</i> ignorieren)</string>
|
<string name="prefs_autoSwapSides_summary">Seiten beim Start einer neuen Partie automatisch wechseln (Einstellung <i>Ansicht drehen</i> ignorieren)</string>
|
||||||
<string name="prefs_engine_settings">Engine-Einstellungen</string>
|
<string name="prefs_engine_settings">Engine-Einstellungen</string>
|
||||||
<string name="prefs_engine_title">Schach-Engine</string>
|
|
||||||
<string name="prefs_engine_summary">Auswahl der Schach-Engine</string>
|
|
||||||
<string name="prefs_strength_title">Spielstärke</string>
|
<string name="prefs_strength_title">Spielstärke</string>
|
||||||
<string name="prefs_ponderMode_title">Vorausberechnung</string>
|
<string name="prefs_ponderMode_title">Vorausberechnung</string>
|
||||||
<string name="prefs_ponderMode_summary">Vorausberechnung von Zügen durch die Engine, wenn der Spieler am Zug ist</string>
|
<string name="prefs_ponderMode_summary">Vorausberechnung von Zügen durch die Engine, wenn der Spieler am Zug ist</string>
|
||||||
|
|||||||
@@ -1,15 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">DroidFish</string>
|
<string name="app_name">DroidFish</string>
|
||||||
<string-array name="engine_texts">
|
|
||||||
<item>Stockfish</item>
|
|
||||||
<item>CuckooChess</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="engine_values">
|
|
||||||
<item>stockfish</item>
|
|
||||||
<item>cuckoochess</item>
|
|
||||||
</string-array>
|
|
||||||
<string name="engine_default">stockfish</string>
|
|
||||||
|
|
||||||
<string-array name="engine_threads_texts">
|
<string-array name="engine_threads_texts">
|
||||||
<item>Automatic</item>
|
<item>Automatic</item>
|
||||||
@@ -305,6 +296,7 @@ you are not actively using the program.\
|
|||||||
<string name="edit_move_counters">Edit Move Counters</string>
|
<string name="edit_move_counters">Edit Move Counters</string>
|
||||||
<string name="internal_book"><Internal Book></string>
|
<string name="internal_book"><Internal Book></string>
|
||||||
<string name="select_opening_book_file">Select opening book file</string>
|
<string name="select_opening_book_file">Select opening book file</string>
|
||||||
|
<string name="select_chess_engine">Select Chess Engine</string>
|
||||||
<string name="select_pgn_file">Select PGN file to open</string>
|
<string name="select_pgn_file">Select PGN file to open</string>
|
||||||
<string name="select_pgn_file_save">Save to PGN file</string>
|
<string name="select_pgn_file_save">Save to PGN file</string>
|
||||||
<string name="select_scid_file">Select Scid file to open</string>
|
<string name="select_scid_file">Select Scid file to open</string>
|
||||||
@@ -401,6 +393,9 @@ you are not actively using the program.\
|
|||||||
<string name="after_selected">After Selected</string>
|
<string name="after_selected">After Selected</string>
|
||||||
<string name="replace_selected">Replace Selected</string>
|
<string name="replace_selected">Replace Selected</string>
|
||||||
<string name="engine">Engine</string>
|
<string name="engine">Engine</string>
|
||||||
|
<string name="engine_error">Engine error</string>
|
||||||
|
<string name="stockfish_engine">Stockfish</string>
|
||||||
|
<string name="cuckoochess_engine">CuckooChess</string>
|
||||||
|
|
||||||
<string name="err_too_few_spaces">Too few spaces</string>
|
<string name="err_too_few_spaces">Too few spaces</string>
|
||||||
<string name="err_invalid_piece">Invalid piece</string>
|
<string name="err_invalid_piece">Invalid piece</string>
|
||||||
@@ -425,6 +420,7 @@ you are not actively using the program.\
|
|||||||
<string name="option_force_computer_move">Force Computer Move</string>
|
<string name="option_force_computer_move">Force Computer Move</string>
|
||||||
<string name="option_draw">Claim/Offer/Accept Draw</string>
|
<string name="option_draw">Claim/Offer/Accept Draw</string>
|
||||||
<string name="option_select_book">Select Opening Book</string>
|
<string name="option_select_book">Select Opening Book</string>
|
||||||
|
<string name="option_select_engine">Select Chess Engine</string>
|
||||||
<string name="option_color_theme">Set Color Theme</string>
|
<string name="option_color_theme">Set Color Theme</string>
|
||||||
<string name="option_about">About / Help</string>
|
<string name="option_about">About / Help</string>
|
||||||
|
|
||||||
@@ -434,8 +430,6 @@ you are not actively using the program.\
|
|||||||
<string name="prefs_autoSwapSides_title">Auto Swap Sides</string>
|
<string name="prefs_autoSwapSides_title">Auto Swap Sides</string>
|
||||||
<string name="prefs_autoSwapSides_summary">Automatically swap sides when new game started. Also overrides Flip Board setting</string>
|
<string name="prefs_autoSwapSides_summary">Automatically swap sides when new game started. Also overrides Flip Board setting</string>
|
||||||
<string name="prefs_engine_settings">Engine Settings</string>
|
<string name="prefs_engine_settings">Engine Settings</string>
|
||||||
<string name="prefs_engine_title">Engine</string>
|
|
||||||
<string name="prefs_engine_summary">Chess Engine</string>
|
|
||||||
<string name="prefs_strength_title">Strength</string>
|
<string name="prefs_strength_title">Strength</string>
|
||||||
<string name="prefs_ponderMode_title">Pondering</string>
|
<string name="prefs_ponderMode_title">Pondering</string>
|
||||||
<string name="prefs_ponderMode_summary">Let engine think while waiting for opponent\'s move</string>
|
<string name="prefs_ponderMode_summary">Let engine think while waiting for opponent\'s move</string>
|
||||||
|
|||||||
@@ -18,14 +18,6 @@
|
|||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/prefs_engine_settings">
|
android:title="@string/prefs_engine_settings">
|
||||||
<ListPreference
|
|
||||||
android:key="engine"
|
|
||||||
android:title="@string/prefs_engine_title"
|
|
||||||
android:summary="@string/prefs_engine_summary"
|
|
||||||
android:entryValues="@array/engine_values"
|
|
||||||
android:entries="@array/engine_texts"
|
|
||||||
android:defaultValue="@string/engine_default">
|
|
||||||
</ListPreference>
|
|
||||||
<org.petero.droidfish.SeekBarPreference
|
<org.petero.droidfish.SeekBarPreference
|
||||||
android:key="strength"
|
android:key="strength"
|
||||||
android:defaultValue="1000"
|
android:defaultValue="1000"
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ public class DroidFish extends Activity implements GUIInterface {
|
|||||||
|
|
||||||
private final static String bookDir = "DroidFish";
|
private final static String bookDir = "DroidFish";
|
||||||
private final static String pgnDir = "DroidFish" + File.separator + "pgn";
|
private final static String pgnDir = "DroidFish" + File.separator + "pgn";
|
||||||
|
private final static String engineDir = "DroidFish" + File.separator + "uci";
|
||||||
private BookOptions bookOptions = new BookOptions();
|
private BookOptions bookOptions = new BookOptions();
|
||||||
private PGNOptions pgnOptions = new PGNOptions();
|
private PGNOptions pgnOptions = new PGNOptions();
|
||||||
|
|
||||||
@@ -732,6 +733,10 @@ public class DroidFish extends Activity implements GUIInterface {
|
|||||||
removeDialog(SELECT_BOOK_DIALOG);
|
removeDialog(SELECT_BOOK_DIALOG);
|
||||||
showDialog(SELECT_BOOK_DIALOG);
|
showDialog(SELECT_BOOK_DIALOG);
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.select_engine:
|
||||||
|
removeDialog(SELECT_ENGINE_DIALOG);
|
||||||
|
showDialog(SELECT_ENGINE_DIALOG);
|
||||||
|
return true;
|
||||||
case R.id.set_color_theme:
|
case R.id.set_color_theme:
|
||||||
showDialog(SET_COLOR_THEME_DIALOG);
|
showDialog(SET_COLOR_THEME_DIALOG);
|
||||||
return true;
|
return true;
|
||||||
@@ -1004,16 +1009,17 @@ public class DroidFish extends Activity implements GUIInterface {
|
|||||||
static private final int ABOUT_DIALOG = 2;
|
static private final int ABOUT_DIALOG = 2;
|
||||||
static private final int SELECT_MOVE_DIALOG = 3;
|
static private final int SELECT_MOVE_DIALOG = 3;
|
||||||
static private final int SELECT_BOOK_DIALOG = 4;
|
static private final int SELECT_BOOK_DIALOG = 4;
|
||||||
static private final int SELECT_PGN_FILE_DIALOG = 5;
|
static private final int SELECT_ENGINE_DIALOG = 5;
|
||||||
static private final int SELECT_PGN_FILE_SAVE_DIALOG = 6;
|
static private final int SELECT_PGN_FILE_DIALOG = 6;
|
||||||
static private final int SET_COLOR_THEME_DIALOG = 7;
|
static private final int SELECT_PGN_FILE_SAVE_DIALOG = 7;
|
||||||
static private final int GAME_MODE_DIALOG = 8;
|
static private final int SET_COLOR_THEME_DIALOG = 8;
|
||||||
static private final int SELECT_PGN_SAVE_NEWFILE_DIALOG = 9;
|
static private final int GAME_MODE_DIALOG = 9;
|
||||||
static private final int MOVELIST_MENU_DIALOG = 10;
|
static private final int SELECT_PGN_SAVE_NEWFILE_DIALOG = 10;
|
||||||
static private final int THINKING_MENU_DIALOG = 11;
|
static private final int MOVELIST_MENU_DIALOG = 11;
|
||||||
static private final int GO_BACK_MENU_DIALOG = 12;
|
static private final int THINKING_MENU_DIALOG = 12;
|
||||||
static private final int GO_FORWARD_MENU_DIALOG = 13;
|
static private final int GO_BACK_MENU_DIALOG = 13;
|
||||||
static private final int FILE_MENU_DIALOG = 14;
|
static private final int GO_FORWARD_MENU_DIALOG = 14;
|
||||||
|
static private final int FILE_MENU_DIALOG = 15;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dialog onCreateDialog(int id) {
|
protected Dialog onCreateDialog(int id) {
|
||||||
@@ -1225,6 +1231,45 @@ public class DroidFish extends Activity implements GUIInterface {
|
|||||||
AlertDialog alert = builder.create();
|
AlertDialog alert = builder.create();
|
||||||
return alert;
|
return alert;
|
||||||
}
|
}
|
||||||
|
case SELECT_ENGINE_DIALOG: {
|
||||||
|
String[] fileNames = findFilesInDirectory(engineDir, null);
|
||||||
|
final int numFiles = fileNames.length;
|
||||||
|
final String[] items = new String[numFiles + 2];
|
||||||
|
final String[] ids = new String[numFiles + 2];
|
||||||
|
ids[0] = "stockfish"; items[0] = getString(R.string.stockfish_engine);
|
||||||
|
ids[1] = "cuckoochess"; items[1] = getString(R.string.cuckoochess_engine);
|
||||||
|
String sep = File.separator;
|
||||||
|
String base = Environment.getExternalStorageDirectory() + sep + engineDir + sep;
|
||||||
|
for (int i = 0; i < numFiles; i++) {
|
||||||
|
ids[i+2] = base + fileNames[i];
|
||||||
|
items[i+2] = fileNames[i];
|
||||||
|
}
|
||||||
|
String currEngine = ctrl.getEngine();
|
||||||
|
int defaultItem = 0;
|
||||||
|
for (int i = 0; i < ids.length; i++) {
|
||||||
|
if (ids[i].equals(currEngine)) {
|
||||||
|
defaultItem = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setTitle(R.string.select_chess_engine);
|
||||||
|
builder.setSingleChoiceItems(items, defaultItem, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int item) {
|
||||||
|
if ((item < 0) || (item >= ids.length))
|
||||||
|
return;
|
||||||
|
Editor editor = settings.edit();
|
||||||
|
String engine = ids[item];
|
||||||
|
editor.putString("engine", engine);
|
||||||
|
editor.commit();
|
||||||
|
dialog.dismiss();
|
||||||
|
int strength = settings.getInt("strength", 1000);
|
||||||
|
setEngineStrength(engine, strength);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AlertDialog alert = builder.create();
|
||||||
|
return alert;
|
||||||
|
}
|
||||||
case SELECT_PGN_FILE_DIALOG: {
|
case SELECT_PGN_FILE_DIALOG: {
|
||||||
final String[] fileNames = findFilesInDirectory(pgnDir, null);
|
final String[] fileNames = findFilesInDirectory(pgnDir, null);
|
||||||
final int numFiles = fileNames.length;
|
final int numFiles = fileNames.length;
|
||||||
@@ -1773,6 +1818,13 @@ public class DroidFish extends Activity implements GUIInterface {
|
|||||||
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
|
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reportEngineError(String errMsg) {
|
||||||
|
String msg = String.format("%s: %s",
|
||||||
|
getString(R.string.engine_error), errMsg);
|
||||||
|
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void computerMoveMade() {
|
public void computerMoveMade() {
|
||||||
if (soundEnabled) {
|
if (soundEnabled) {
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ public interface GUIInterface {
|
|||||||
/** Report UCI engine name. */
|
/** Report UCI engine name. */
|
||||||
public void reportEngineName(String engine);
|
public void reportEngineName(String engine);
|
||||||
|
|
||||||
|
/** Report UCI engine error message. */
|
||||||
|
public void reportEngineError(String errMsg);
|
||||||
|
|
||||||
/** Called when computer made a move. GUI can notify user, for example by playing a sound. */
|
/** Called when computer made a move. GUI can notify user, for example by playing a sound. */
|
||||||
public void computerMoveMade();
|
public void computerMoveMade();
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
import org.petero.droidfish.book.BookOptions;
|
import org.petero.droidfish.book.BookOptions;
|
||||||
import org.petero.droidfish.book.DroidBook;
|
import org.petero.droidfish.book.DroidBook;
|
||||||
import org.petero.droidfish.engine.cuckoochess.CuckooChessEngine;
|
|
||||||
import org.petero.droidfish.gamelogic.Move;
|
import org.petero.droidfish.gamelogic.Move;
|
||||||
import org.petero.droidfish.gamelogic.MoveGen;
|
import org.petero.droidfish.gamelogic.MoveGen;
|
||||||
import org.petero.droidfish.gamelogic.Pair;
|
import org.petero.droidfish.gamelogic.Pair;
|
||||||
@@ -292,10 +291,10 @@ public class DroidComputerPlayer {
|
|||||||
/** Stop the engine process. */
|
/** Stop the engine process. */
|
||||||
public final synchronized void shutdownEngine() {
|
public final synchronized void shutdownEngine() {
|
||||||
if (uciEngine != null) {
|
if (uciEngine != null) {
|
||||||
uciEngine.shutDown();
|
|
||||||
uciEngine = null;
|
|
||||||
engineMonitor.interrupt();
|
engineMonitor.interrupt();
|
||||||
engineMonitor = null;
|
engineMonitor = null;
|
||||||
|
uciEngine.shutDown();
|
||||||
|
uciEngine = null;
|
||||||
}
|
}
|
||||||
engineState.state = MainState.DEAD;
|
engineState.state = MainState.DEAD;
|
||||||
}
|
}
|
||||||
@@ -543,15 +542,20 @@ public class DroidComputerPlayer {
|
|||||||
myAssert(searchRequest != null);
|
myAssert(searchRequest != null);
|
||||||
|
|
||||||
engineName = "Computer";
|
engineName = "Computer";
|
||||||
if ("cuckoochess".equals(searchRequest.engine))
|
uciEngine = UCIEngineBase.getEngine(searchRequest.engine, new UCIEngine.Report() {
|
||||||
uciEngine = new CuckooChessEngine();
|
@Override
|
||||||
else
|
public void reportError(String errMsg) {
|
||||||
uciEngine = new StockFishJNI();
|
if (errMsg != null) {
|
||||||
|
listener.reportEngineError(errMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
uciEngine.initialize();
|
uciEngine.initialize();
|
||||||
|
|
||||||
|
final UCIEngine uci = uciEngine;
|
||||||
engineMonitor = new Thread(new Runnable() {
|
engineMonitor = new Thread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
monitorLoop();
|
monitorLoop(uci);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
engineMonitor.start();
|
engineMonitor.start();
|
||||||
@@ -569,16 +573,17 @@ public class DroidComputerPlayer {
|
|||||||
private final static long guiUpdateInterval = 100;
|
private final static long guiUpdateInterval = 100;
|
||||||
private long lastGUIUpdate = 0;
|
private long lastGUIUpdate = 0;
|
||||||
|
|
||||||
private final void monitorLoop() {
|
private final void monitorLoop(UCIEngine uci) {
|
||||||
while (true) {
|
while (true) {
|
||||||
int timeout = getReadTimeout();
|
int timeout = getReadTimeout();
|
||||||
UCIEngine uci = uciEngine;
|
|
||||||
if (uci == null)
|
|
||||||
return;
|
|
||||||
String s = uci.readLineFromEngine(timeout);
|
|
||||||
if (Thread.currentThread().isInterrupted())
|
if (Thread.currentThread().isInterrupted())
|
||||||
return;
|
return;
|
||||||
processEngineOutput(s);
|
String s = uci.readLineFromEngine(timeout);
|
||||||
|
if ((s == null) || Thread.currentThread().isInterrupted())
|
||||||
|
return;
|
||||||
|
processEngineOutput(uci, s);
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return;
|
||||||
notifyGUI();
|
notifyGUI();
|
||||||
if (Thread.currentThread().isInterrupted())
|
if (Thread.currentThread().isInterrupted())
|
||||||
return;
|
return;
|
||||||
@@ -586,7 +591,10 @@ public class DroidComputerPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Process one line of data from the engine. */
|
/** Process one line of data from the engine. */
|
||||||
private final synchronized void processEngineOutput(String s) {
|
private final synchronized void processEngineOutput(UCIEngine uci, String s) {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return;
|
||||||
|
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
shutdownEngine();
|
shutdownEngine();
|
||||||
return;
|
return;
|
||||||
@@ -595,17 +603,13 @@ public class DroidComputerPlayer {
|
|||||||
if (s.length() == 0)
|
if (s.length() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UCIEngine uci = uciEngine;
|
|
||||||
if (uci == null)
|
|
||||||
return;
|
|
||||||
switch (engineState.state) {
|
switch (engineState.state) {
|
||||||
case READ_OPTIONS: {
|
case READ_OPTIONS: {
|
||||||
if (readUCIOption(s)) {
|
if (readUCIOption(s)) {
|
||||||
if (!"cuckoochess".equals(engineState.engine))
|
uci.initOptions();
|
||||||
uci.setOption("Hash", 16);
|
|
||||||
uci.setOption("Ponder", false);
|
uci.setOption("Ponder", false);
|
||||||
uci.writeLineToEngine("ucinewgame");
|
uci.writeLineToEngine("ucinewgame");
|
||||||
uciEngine.writeLineToEngine("isready");
|
uci.writeLineToEngine("isready");
|
||||||
engineState.state = MainState.WAIT_READY;
|
engineState.state = MainState.WAIT_READY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -642,7 +646,7 @@ public class DroidComputerPlayer {
|
|||||||
case STOP_SEARCH: {
|
case STOP_SEARCH: {
|
||||||
String[] tokens = tokenize(s);
|
String[] tokens = tokenize(s);
|
||||||
if (tokens[0].equals("bestmove")) {
|
if (tokens[0].equals("bestmove")) {
|
||||||
uciEngine.writeLineToEngine("isready");
|
uci.writeLineToEngine("isready");
|
||||||
engineState.state = MainState.WAIT_READY;
|
engineState.state = MainState.WAIT_READY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -860,6 +864,9 @@ public class DroidComputerPlayer {
|
|||||||
|
|
||||||
/** Notify GUI about search statistics. */
|
/** Notify GUI about search statistics. */
|
||||||
private final synchronized void notifyGUI() {
|
private final synchronized void notifyGUI() {
|
||||||
|
if (Thread.currentThread().isInterrupted())
|
||||||
|
return;
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if (now < lastGUIUpdate + guiUpdateInterval)
|
if (now < lastGUIUpdate + guiUpdateInterval)
|
||||||
return;
|
return;
|
||||||
|
|||||||
183
DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java
Normal file
183
DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
package org.petero.droidfish.engine;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ExternalEngine extends UCIEngineBase {
|
||||||
|
private File engineFileName;
|
||||||
|
private static final String exePath = "/data/data/org.petero.droidfish/engine.exe";
|
||||||
|
private final Report report;
|
||||||
|
private Process engineProc;
|
||||||
|
private Thread stdInThread;
|
||||||
|
private Thread stdErrThread;
|
||||||
|
private List<String> inLines;
|
||||||
|
|
||||||
|
public ExternalEngine(String engine, Report report) {
|
||||||
|
this.report = report;
|
||||||
|
engineFileName = new File(engine);
|
||||||
|
engineProc = null;
|
||||||
|
stdInThread = null;
|
||||||
|
stdErrThread = null;
|
||||||
|
inLines = new LinkedList<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
|
protected void startProcess() {
|
||||||
|
try {
|
||||||
|
copyFile(engineFileName, new File(exePath));
|
||||||
|
chmod(exePath);
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(exePath);
|
||||||
|
engineProc = pb.start();
|
||||||
|
|
||||||
|
// Start a thread to read stdin
|
||||||
|
stdInThread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Process ep = engineProc;
|
||||||
|
if (ep == null)
|
||||||
|
return;
|
||||||
|
InputStream is = ep.getInputStream();
|
||||||
|
InputStreamReader isr = new InputStreamReader(is);
|
||||||
|
BufferedReader br = new BufferedReader(isr);
|
||||||
|
String line;
|
||||||
|
try {
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
if ((ep == null) || Thread.currentThread().isInterrupted())
|
||||||
|
return;
|
||||||
|
synchronized (inLines) {
|
||||||
|
inLines.add(line);
|
||||||
|
inLines.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stdInThread.start();
|
||||||
|
|
||||||
|
// Start a thread to ignore stderr
|
||||||
|
stdErrThread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
byte[] buffer = new byte[128];
|
||||||
|
while (true) {
|
||||||
|
Process ep = engineProc;
|
||||||
|
if ((ep == null) || Thread.currentThread().isInterrupted())
|
||||||
|
return;
|
||||||
|
try {
|
||||||
|
ep.getErrorStream().read(buffer);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stdErrThread.start();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
report.reportError(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
|
public void initOptions() {
|
||||||
|
setOption("Hash", 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
|
public void setStrength(int strength) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
|
public String readLineFromEngine(int timeoutMillis) {
|
||||||
|
try {
|
||||||
|
synchronized (inLines) {
|
||||||
|
if (inLines.size() == 0) {
|
||||||
|
Thread inThread = stdInThread;
|
||||||
|
if ((inThread == null) || !inThread.isAlive())
|
||||||
|
return null;
|
||||||
|
inLines.wait(timeoutMillis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronized (inLines) {
|
||||||
|
if (inLines.size() > 0) {
|
||||||
|
String ret = inLines.get(0);
|
||||||
|
inLines.remove(0);
|
||||||
|
// System.out.printf("Engine -> GUI: %s\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
|
public void writeLineToEngine(String data) {
|
||||||
|
// System.out.printf("GUI -> Engine: %s\n", data);
|
||||||
|
data += "\n";
|
||||||
|
try {
|
||||||
|
Process ep = engineProc;
|
||||||
|
if (ep != null)
|
||||||
|
ep.getOutputStream().write(data.getBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
|
public void shutDown() {
|
||||||
|
super.shutDown();
|
||||||
|
if (engineProc != null)
|
||||||
|
engineProc.destroy();
|
||||||
|
engineProc = null;
|
||||||
|
if (stdInThread != null)
|
||||||
|
stdInThread.interrupt();
|
||||||
|
if (stdErrThread != null)
|
||||||
|
stdErrThread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static void copyFile(File from, File to) throws IOException {
|
||||||
|
if (to.exists() && (from.length() == to.length()) && (from.lastModified() == to.lastModified()))
|
||||||
|
return;
|
||||||
|
if (to.exists())
|
||||||
|
to.delete();
|
||||||
|
to.createNewFile();
|
||||||
|
FileChannel inFC = null;
|
||||||
|
FileChannel outFC = null;
|
||||||
|
try {
|
||||||
|
inFC = new FileInputStream(from).getChannel();
|
||||||
|
outFC = new FileOutputStream(to).getChannel();
|
||||||
|
long cnt = outFC.transferFrom(inFC, 0, inFC.size());
|
||||||
|
if (cnt < inFC.size())
|
||||||
|
throw new IOException("File copy failed");
|
||||||
|
} finally {
|
||||||
|
if (inFC != null) { try { inFC.close(); } catch (IOException ex) {} }
|
||||||
|
if (outFC != null) { try { outFC.close(); } catch (IOException ex) {} }
|
||||||
|
to.setLastModified(from.lastModified());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final void chmod(String exePath) throws IOException {
|
||||||
|
Process proc = Runtime.getRuntime().exec(new String[]{"chmod", "744", exePath});
|
||||||
|
try {
|
||||||
|
proc.waitFor();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
proc.destroy();
|
||||||
|
throw new IOException("chmod failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,18 +23,19 @@ public class StockFishJNI extends UCIEngineBase {
|
|||||||
System.loadLibrary("stockfishjni");
|
System.loadLibrary("stockfishjni");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
@Override
|
@Override
|
||||||
public void setStrength(int strength) {
|
public final void initOptions() {
|
||||||
|
setOption("Hash", 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
|
public final void setStrength(int strength) {
|
||||||
setOption("Skill Level", strength/50);
|
setOption("Skill Level", strength/50);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* Read a line from the process.
|
|
||||||
* @param timeoutMillis Maximum time to wait for data
|
|
||||||
* @return The line, without terminating newline characters,
|
|
||||||
* or empty string if no data available,
|
|
||||||
* or null if I/O error.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public final String readLineFromEngine(int timeoutMillis) {
|
public final String readLineFromEngine(int timeoutMillis) {
|
||||||
String ret = readFromProcess(timeoutMillis);
|
String ret = readFromProcess(timeoutMillis);
|
||||||
@@ -46,14 +47,15 @@ public class StockFishJNI extends UCIEngineBase {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write a line to the process. \n will be added automatically. */
|
/** @inheritDoc */
|
||||||
@Override
|
@Override
|
||||||
public final synchronized void writeLineToEngine(String data) {
|
public final synchronized void writeLineToEngine(String data) {
|
||||||
// System.out.printf("GUI -> Engine: %s\n", data);
|
// System.out.printf("GUI -> Engine: %s\n", data);
|
||||||
writeToProcess(data + "\n");
|
writeToProcess(data + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start the child process. */
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
protected final native void startProcess();
|
protected final native void startProcess();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,15 +20,24 @@ package org.petero.droidfish.engine;
|
|||||||
|
|
||||||
public interface UCIEngine {
|
public interface UCIEngine {
|
||||||
|
|
||||||
|
/** For reporting engine error messages. */
|
||||||
|
public interface Report {
|
||||||
|
/** Report error message to GUI. */
|
||||||
|
void reportError(String errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
/** Start engine. */
|
/** Start engine. */
|
||||||
public void initialize();
|
public void initialize();
|
||||||
|
|
||||||
|
/** Initialize default options. */
|
||||||
|
public void initOptions();
|
||||||
|
|
||||||
/** Shut down engine. */
|
/** Shut down engine. */
|
||||||
public void shutDown();
|
public void shutDown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a line from the engine.
|
* Read a line from the engine.
|
||||||
* @param timeoutMillis Maximum time to wait for data
|
* @param timeoutMillis Maximum time to wait for data.
|
||||||
* @return The line, without terminating newline characters,
|
* @return The line, without terminating newline characters,
|
||||||
* or empty string if no data available,
|
* or empty string if no data available,
|
||||||
* or null if I/O error.
|
* or null if I/O error.
|
||||||
|
|||||||
@@ -2,10 +2,21 @@ package org.petero.droidfish.engine;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.petero.droidfish.engine.cuckoochess.CuckooChessEngine;
|
||||||
|
|
||||||
public abstract class UCIEngineBase implements UCIEngine {
|
public abstract class UCIEngineBase implements UCIEngine {
|
||||||
|
|
||||||
private boolean processAlive;
|
private boolean processAlive;
|
||||||
|
|
||||||
|
public static UCIEngine getEngine(String engine, Report report) {
|
||||||
|
if ("cuckoochess".equals(engine))
|
||||||
|
return new CuckooChessEngine(report);
|
||||||
|
else if ("stockfish".equals(engine))
|
||||||
|
return new StockFishJNI();
|
||||||
|
else
|
||||||
|
return new ExternalEngine(engine, report);
|
||||||
|
}
|
||||||
|
|
||||||
protected UCIEngineBase() {
|
protected UCIEngineBase() {
|
||||||
processAlive = false;
|
processAlive = false;
|
||||||
}
|
}
|
||||||
@@ -21,7 +32,7 @@ public abstract class UCIEngineBase implements UCIEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void shutDown() {
|
public void shutDown() {
|
||||||
if (processAlive) {
|
if (processAlive) {
|
||||||
writeLineToEngine("quit");
|
writeLineToEngine("quit");
|
||||||
processAlive = false;
|
processAlive = false;
|
||||||
|
|||||||
@@ -51,12 +51,8 @@ public class CuckooChessEngine extends UCIEngineBase {
|
|||||||
private NioInputStream inFromEngine;
|
private NioInputStream inFromEngine;
|
||||||
private Thread engineThread;
|
private Thread engineThread;
|
||||||
|
|
||||||
public CuckooChessEngine() {
|
public CuckooChessEngine(Report report) {
|
||||||
try {
|
pos = null;
|
||||||
pos = TextIO.readFEN(TextIO.startPosFEN);
|
|
||||||
} catch (ChessParseError ex) {
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
moves = new ArrayList<Move>();
|
moves = new ArrayList<Move>();
|
||||||
quit = false;
|
quit = false;
|
||||||
try {
|
try {
|
||||||
@@ -64,14 +60,12 @@ public class CuckooChessEngine extends UCIEngineBase {
|
|||||||
engineToGui = Pipe.open();
|
engineToGui = Pipe.open();
|
||||||
inFromEngine = new NioInputStream(engineToGui);
|
inFromEngine = new NioInputStream(engineToGui);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
report.reportError(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
@Override
|
@Override
|
||||||
public void setStrength(int strength) {
|
|
||||||
setOption("strength", strength);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void startProcess() {
|
protected final void startProcess() {
|
||||||
engineThread = new Thread(new Runnable() {
|
engineThread = new Thread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -87,6 +81,17 @@ public class CuckooChessEngine extends UCIEngineBase {
|
|||||||
engineThread.start();
|
engineThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
|
public final void initOptions() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
@Override
|
||||||
|
public final void setStrength(int strength) {
|
||||||
|
setOption("strength", strength);
|
||||||
|
}
|
||||||
|
|
||||||
private final void mainLoop(NioInputStream is, NioPrintStream os) {
|
private final void mainLoop(NioInputStream is, NioPrintStream os) {
|
||||||
String line;
|
String line;
|
||||||
while ((line = is.readLine()) != null) {
|
while ((line = is.readLine()) != null) {
|
||||||
@@ -97,6 +102,7 @@ public class CuckooChessEngine extends UCIEngineBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
@Override
|
@Override
|
||||||
public final String readLineFromEngine(int timeoutMillis) {
|
public final String readLineFromEngine(int timeoutMillis) {
|
||||||
if ((engineThread != null) && !engineThread.isAlive())
|
if ((engineThread != null) && !engineThread.isAlive())
|
||||||
@@ -110,6 +116,7 @@ public class CuckooChessEngine extends UCIEngineBase {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
@Override
|
@Override
|
||||||
public final synchronized void writeLineToEngine(String data) {
|
public final synchronized void writeLineToEngine(String data) {
|
||||||
// System.out.printf("GUI -> Engine: %s\n", data);
|
// System.out.printf("GUI -> Engine: %s\n", data);
|
||||||
@@ -224,6 +231,13 @@ public class CuckooChessEngine extends UCIEngineBase {
|
|||||||
sPar.infinite = true;
|
sPar.infinite = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (pos == null) {
|
||||||
|
try {
|
||||||
|
pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||||
|
} catch (ChessParseError ex) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ponder) {
|
if (ponder) {
|
||||||
engine.startPonder(pos, moves, sPar);
|
engine.startPonder(pos, moves, sPar);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -157,6 +157,11 @@ public class DroidChessController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return current engine identifier. */
|
||||||
|
public final synchronized String getEngine() {
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
/** Notify controller that preferences has changed. */
|
/** Notify controller that preferences has changed. */
|
||||||
public final synchronized void prefsChanged() {
|
public final synchronized void prefsChanged() {
|
||||||
updateBookHints();
|
updateBookHints();
|
||||||
@@ -649,6 +654,8 @@ public class DroidChessController {
|
|||||||
tmpPos.makeMove(ponderMove, ui);
|
tmpPos.makeMove(ponderMove, ui);
|
||||||
}
|
}
|
||||||
for (Move m : pv.pv) {
|
for (Move m : pv.pv) {
|
||||||
|
if (m == null)
|
||||||
|
break;
|
||||||
String moveStr = TextIO.moveToString(tmpPos, m, false);
|
String moveStr = TextIO.moveToString(tmpPos, m, false);
|
||||||
buf.append(String.format(" %s", moveStr));
|
buf.append(String.format(" %s", moveStr));
|
||||||
tmpPos.makeMove(m, ui);
|
tmpPos.makeMove(m, ui);
|
||||||
@@ -701,6 +708,15 @@ public class DroidChessController {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reportEngineError(final String errMsg) {
|
||||||
|
gui.runOnUIThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
gui.reportEngineError(errMsg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Discard current search. Return true if GUI update needed. */
|
/** Discard current search. Return true if GUI update needed. */
|
||||||
|
|||||||
@@ -90,4 +90,7 @@ public interface SearchListener {
|
|||||||
|
|
||||||
/** Report engine name. */
|
/** Report engine name. */
|
||||||
public void notifyEngineName(String engineName);
|
public void notifyEngineName(String engineName);
|
||||||
|
|
||||||
|
/** Report engine error. */
|
||||||
|
public void reportEngineError(String errMsg);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user