DroidFish: Made it possible to change the engine hash table size.

This commit is contained in:
Peter Osterlund
2012-07-24 01:04:43 +00:00
parent bb3cff5298
commit 04f1225d3e
10 changed files with 131 additions and 50 deletions

View File

@@ -2,6 +2,7 @@
<resources> <resources>
<string name="app_name">DroidFish</string> <string name="app_name">DroidFish</string>
<string name="engine_threads_default">1</string> <string name="engine_threads_default">1</string>
<string name="engine_hash_default">16</string>
<string name="moves_per_session_default">60</string> <string name="moves_per_session_default">60</string>
<string name="time_control_default">120000</string> <string name="time_control_default">120000</string>
<string name="time_increment_default">0</string> <string name="time_increment_default">0</string>
@@ -220,6 +221,8 @@ you are not actively using the program.\
<string name="prefs_ponderMode_summary">Let engine think while waiting for opponent\'s move. Supported by most engines.</string> <string name="prefs_ponderMode_summary">Let engine think while waiting for opponent\'s move. Supported by most engines.</string>
<string name="prefs_threads_title">Threads</string> <string name="prefs_threads_title">Threads</string>
<string name="prefs_threads_summary">Number of engine threads (CPU cores) to use. Not supported by all engines.</string> <string name="prefs_threads_summary">Number of engine threads (CPU cores) to use. Not supported by all engines.</string>
<string name="prefs_hash_title">Hash Table</string>
<string name="prefs_hash_summary">Hash table size in megabytes</string>
<string name="prefs_time_control">Time Control</string> <string name="prefs_time_control">Time Control</string>
<string name="prefs_movesPerSession_title">Moves</string> <string name="prefs_movesPerSession_title">Moves</string>
<string name="prefs_movesPerSession_summary">Number of moves between time controls</string> <string name="prefs_movesPerSession_summary">Number of moves between time controls</string>
@@ -364,6 +367,18 @@ you are not actively using the program.\
<item>3</item> <item>3</item>
<item>4</item> <item>4</item>
</string-array> </string-array>
<string-array name="engine_hash_texts">
<item>16 MB</item>
<item>32 MB</item>
<item>64 MB</item>
<item>128 MB</item>
</string-array>
<string-array name="engine_hash_values">
<item>16</item>
<item>32</item>
<item>64</item>
<item>128</item>
</string-array>
<string-array name="moves_per_session_texts"> <string-array name="moves_per_session_texts">
<item>Whole Game</item> <item>Whole Game</item>
<item>1 move</item> <item>1 move</item>

View File

@@ -44,7 +44,15 @@
android:entries="@array/engine_threads_texts" android:entries="@array/engine_threads_texts"
android:defaultValue="@string/engine_threads_default"> android:defaultValue="@string/engine_threads_default">
</ListPreference> </ListPreference>
</PreferenceCategory> <ListPreference
android:key="hashMB"
android:title="@string/prefs_hash_title"
android:summary="@string/prefs_hash_summary"
android:entryValues="@array/engine_hash_values"
android:entries="@array/engine_hash_texts"
android:defaultValue="@string/engine_hash_default">
</ListPreference>
</PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/prefs_time_control"> android:title="@string/prefs_time_control">
<ListPreference <ListPreference

View File

@@ -149,7 +149,6 @@ public class DroidFish extends Activity implements GUIInterface {
// FIXME!!! Better behavior if engine is terminated. How exactly? // FIXME!!! Better behavior if engine is terminated. How exactly?
// FIXME!!! Handle PGN non-file intents with more than one game. // FIXME!!! Handle PGN non-file intents with more than one game.
// FIXME!!! File load/save of FEN data // FIXME!!! File load/save of FEN data
// FIXME!!! Make engine hash size configurable.
private ChessBoard cb; private ChessBoard cb;
private static DroidChessController ctrl = null; private static DroidChessController ctrl = null;
@@ -191,7 +190,7 @@ public class DroidFish extends Activity implements GUIInterface {
private final static String gtbDefaultDir = "DroidFish" + File.separator + "gtb"; private final static String gtbDefaultDir = "DroidFish" + File.separator + "gtb";
private BookOptions bookOptions = new BookOptions(); private BookOptions bookOptions = new BookOptions();
private PGNOptions pgnOptions = new PGNOptions(); private PGNOptions pgnOptions = new PGNOptions();
private EGTBOptions egtbOptions = new EGTBOptions(); private EngineOptions engineOptions = new EngineOptions();
private long lastVisibleMillis; // Time when GUI became invisible. 0 if currently visible. private long lastVisibleMillis; // Time when GUI became invisible. 0 if currently visible.
private long lastComputationMillis; // Time when engine last showed that it was computing. private long lastComputationMillis; // Time when engine last showed that it was computing.
@@ -783,10 +782,11 @@ public class DroidFish extends Activity implements GUIInterface {
bookOptions.random = (settings.getInt("bookRandom", 500) - 500) * (3.0 / 500); bookOptions.random = (settings.getInt("bookRandom", 500) - 500) * (3.0 / 500);
setBookOptions(); setBookOptions();
egtbOptions.hints = settings.getBoolean("tbHints", false); engineOptions.hashMB = getHashMB();
egtbOptions.hintsEdit = settings.getBoolean("tbHintsEdit", false); engineOptions.hints = settings.getBoolean("tbHints", false);
egtbOptions.rootProbe = settings.getBoolean("tbRootProbe", true); engineOptions.hintsEdit = settings.getBoolean("tbHintsEdit", false);
egtbOptions.engineProbe = settings.getBoolean("tbEngineProbe", true); engineOptions.rootProbe = settings.getBoolean("tbRootProbe", true);
engineOptions.engineProbe = settings.getBoolean("tbEngineProbe", true);
String gtbPath = settings.getString("gtbPath", ""); String gtbPath = settings.getString("gtbPath", "");
gtbPath = gtbPath.trim(); gtbPath = gtbPath.trim();
if (gtbPath.length() == 0) { if (gtbPath.length() == 0) {
@@ -794,8 +794,8 @@ public class DroidFish extends Activity implements GUIInterface {
String sep = File.separator; String sep = File.separator;
gtbPath = extDir.getAbsolutePath() + sep + gtbDefaultDir; gtbPath = extDir.getAbsolutePath() + sep + gtbDefaultDir;
} }
egtbOptions.gtbPath = gtbPath; engineOptions.gtbPath = gtbPath;
setEgtbOptions(); setEngineOptions();
setEgtbHints(cb.getSelectedSquare()); setEgtbHints(cb.getSelectedSquare());
updateThinkingInfo(); updateThinkingInfo();
@@ -820,6 +820,19 @@ public class DroidFish extends Activity implements GUIInterface {
ctrl.prefsChanged(); ctrl.prefsChanged();
} }
/** Get hash size in MB from settings, but reduce size to an amount that the device can handle. */
private final int getHashMB() {
int hashMB = getIntSetting("hashMB", 16);
if (hashMB > 16) {
int maxMem = (int)(Runtime.getRuntime().maxMemory() / (1024*1024));
if (maxMem < 16)
maxMem = 16;
if (hashMB > maxMem)
hashMB = maxMem;
}
return hashMB;
}
private void updateButtons() { private void updateButtons() {
boolean largeButtons = settings.getBoolean("largeButtons", false); boolean largeButtons = settings.getBoolean("largeButtons", false);
Resources r = getResources(); Resources r = getResources();
@@ -918,14 +931,14 @@ public class DroidFish extends Activity implements GUIInterface {
private boolean egtbForceReload = false; private boolean egtbForceReload = false;
private final void setEgtbOptions() { private final void setEngineOptions() {
ctrl.setEgtbOptions(new EGTBOptions(egtbOptions)); ctrl.setEngineOptions(new EngineOptions(engineOptions));
Probe.getInstance().setPath(egtbOptions.gtbPath, egtbForceReload); Probe.getInstance().setPath(engineOptions.gtbPath, egtbForceReload);
egtbForceReload = false; egtbForceReload = false;
} }
private final void setEgtbHints(int sq) { private final void setEgtbHints(int sq) {
if (!egtbOptions.hints || (sq < 0)) { if (!engineOptions.hints || (sq < 0)) {
cb.setSquareDecorations(null); cb.setSquareDecorations(null);
return; return;
} }

View File

@@ -1,14 +1,16 @@
package org.petero.droidfish; package org.petero.droidfish;
/** Endgame tablebase probing options. */ /** Engine options, including endgame tablebase probing options. */
public final class EGTBOptions { public final class EngineOptions {
public int hashMB; // Engine hash table size in MB
public boolean hints; // Hints when playing/analyzing public boolean hints; // Hints when playing/analyzing
public boolean hintsEdit; // Hints in "edit board" mode public boolean hintsEdit; // Hints in "edit board" mode
public boolean rootProbe; // Only search optimal moves at root public boolean rootProbe; // Only search optimal moves at root
public boolean engineProbe; // Let engine use EGTB public boolean engineProbe; // Let engine use EGTB
public String gtbPath; // GTB directory path public String gtbPath; // GTB directory path
public EGTBOptions() { public EngineOptions() {
hashMB = 16;
hints = false; hints = false;
hintsEdit = false; hintsEdit = false;
rootProbe = false; rootProbe = false;
@@ -16,7 +18,8 @@ public final class EGTBOptions {
gtbPath = ""; gtbPath = "";
} }
public EGTBOptions(EGTBOptions other) { public EngineOptions(EngineOptions other) {
hashMB = other.hashMB;
hints = other.hints; hints = other.hints;
hintsEdit = other.hintsEdit; hintsEdit = other.hintsEdit;
rootProbe = other.rootProbe; rootProbe = other.rootProbe;
@@ -28,9 +31,10 @@ public final class EGTBOptions {
public boolean equals(Object o) { public boolean equals(Object o) {
if ((o == null) || (o.getClass() != this.getClass())) if ((o == null) || (o.getClass() != this.getClass()))
return false; return false;
EGTBOptions other = (EGTBOptions)o; EngineOptions other = (EngineOptions)o;
return ((hints == other.hints) && return ((hashMB == other.hashMB) &&
(hints == other.hints) &&
(hintsEdit == other.hintsEdit) && (hintsEdit == other.hintsEdit) &&
(rootProbe == other.rootProbe) && (rootProbe == other.rootProbe) &&
(engineProbe == other.engineProbe) && (engineProbe == other.engineProbe) &&

View File

@@ -23,7 +23,7 @@ import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import org.petero.droidfish.EGTBOptions; import org.petero.droidfish.EngineOptions;
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.gamelogic.Move; import org.petero.droidfish.gamelogic.Move;
@@ -47,7 +47,7 @@ public class DroidComputerPlayer {
private final Context context; private final Context context;
private final SearchListener listener; private final SearchListener listener;
private final DroidBook book; private final DroidBook book;
private EGTBOptions egtbOptions; private EngineOptions engineOptions;
/** Set when "ucinewgame" needs to be sent. */ /** Set when "ucinewgame" needs to be sent. */
private boolean newGame = false; private boolean newGame = false;
@@ -242,7 +242,7 @@ public class DroidComputerPlayer {
this.context = context; this.context = context;
this.listener = listener; this.listener = listener;
book = DroidBook.getInstance(); book = DroidBook.getInstance();
egtbOptions = new EGTBOptions(); engineOptions = new EngineOptions();
engineState = new EngineState(); engineState = new EngineState();
searchRequest = null; searchRequest = null;
} }
@@ -270,8 +270,8 @@ public class DroidComputerPlayer {
book.setOptions(options); book.setOptions(options);
} }
public final void setEgtbOptions(EGTBOptions options) { public final void setEngineOptions(EngineOptions options) {
egtbOptions = options; engineOptions = options;
} }
/** Return all book moves, both as a formatted string and as a list of moves. */ /** Return all book moves, both as a formatted string and as a list of moves. */
@@ -331,7 +331,7 @@ public class DroidComputerPlayer {
/** Decide what moves to search. Filters out non-optimal moves if tablebases are used. */ /** Decide what moves to search. Filters out non-optimal moves if tablebases are used. */
private final ArrayList<Move> movesToSearch(SearchRequest sr) { private final ArrayList<Move> movesToSearch(SearchRequest sr) {
ArrayList<Move> moves = null; ArrayList<Move> moves = null;
if (egtbOptions.rootProbe) if (engineOptions.rootProbe)
moves = Probe.getInstance().findOptimal(sr.currPos); moves = Probe.getInstance().findOptimal(sr.currPos);
if (moves != null) { if (moves != null) {
sr.searchMoves = moves; sr.searchMoves = moves;
@@ -422,9 +422,14 @@ public class DroidComputerPlayer {
} }
private void killOldEngine(String engine) { private void killOldEngine(String engine) {
if (engine.equals(engineState.engine)) boolean needShutDown = !engine.equals(engineState.engine);
return; if (!needShutDown) {
shutdownEngine(); UCIEngine uci = uciEngine;
if (uci != null)
needShutDown = !uci.optionsOk(engineOptions);
}
if (needShutDown)
shutdownEngine();
} }
/** Tell engine to stop searching. */ /** Tell engine to stop searching. */
@@ -662,7 +667,7 @@ public class DroidComputerPlayer {
switch (engineState.state) { switch (engineState.state) {
case READ_OPTIONS: { case READ_OPTIONS: {
if (readUCIOption(uci, s)) { if (readUCIOption(uci, s)) {
uci.initOptions(egtbOptions); uci.initOptions(engineOptions);
uci.writeLineToEngine("ucinewgame"); uci.writeLineToEngine("ucinewgame");
uci.writeLineToEngine("isready"); uci.writeLineToEngine("isready");
engineState.setState(MainState.WAIT_READY); engineState.setState(MainState.WAIT_READY);

View File

@@ -27,7 +27,7 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import org.petero.droidfish.EGTBOptions; import org.petero.droidfish.EngineOptions;
import org.petero.droidfish.R; import org.petero.droidfish.R;
import android.content.Context; import android.content.Context;
@@ -91,7 +91,9 @@ public class ExternalEngine extends UCIEngineBase {
@Override @Override
public void run() { public void run() {
try { try {
engineProc.waitFor(); Process ep = engineProc;
if (ep != null)
ep.waitFor();
isRunning = false; isRunning = false;
if (!startedOk) if (!startedOk)
report.reportError(context.getString(R.string.failed_to_start_engine)); report.reportError(context.getString(R.string.failed_to_start_engine));
@@ -161,15 +163,34 @@ public class ExternalEngine extends UCIEngineBase {
} }
} }
private int hashMB = -1;
private String gaviotaTbPath = "";
private boolean optionsInitialized = false;
/** @inheritDoc */ /** @inheritDoc */
@Override @Override
public void initOptions(EGTBOptions egtbOptions) { public void initOptions(EngineOptions engineOptions) {
super.initOptions(egtbOptions); super.initOptions(engineOptions);
setOption("Hash", 16); hashMB = engineOptions.hashMB;
if (egtbOptions.engineProbe) { setOption("Hash", engineOptions.hashMB);
setOption("GaviotaTbPath", egtbOptions.gtbPath); if (engineOptions.engineProbe) {
gaviotaTbPath = engineOptions.gtbPath;
setOption("GaviotaTbPath", engineOptions.gtbPath);
setOption("GaviotaTbCache", 8); setOption("GaviotaTbCache", 8);
} }
optionsInitialized = true;
}
/** @inheritDoc */
@Override
public boolean optionsOk(EngineOptions engineOptions) {
if (!optionsInitialized)
return true;
if (hashMB != engineOptions.hashMB)
return false;
if (haveOption("gaviotatbpath") && !gaviotaTbPath.equals(engineOptions.gtbPath))
return false;
return true;
} }
/** @inheritDoc */ /** @inheritDoc */

View File

@@ -18,7 +18,7 @@
package org.petero.droidfish.engine; package org.petero.droidfish.engine;
import org.petero.droidfish.EGTBOptions; import org.petero.droidfish.EngineOptions;
public interface UCIEngine { public interface UCIEngine {
@@ -32,7 +32,11 @@ public interface UCIEngine {
public void initialize(); public void initialize();
/** Initialize default options. */ /** Initialize default options. */
public void initOptions(EGTBOptions egtbOptions); public void initOptions(EngineOptions engineOptions);
/** Return true if engine options have correct values.
* If false is returned, engine will be restarted. */
public boolean optionsOk(EngineOptions engineOptions);
/** Shut down engine. */ /** Shut down engine. */
public void shutDown(); public void shutDown();

View File

@@ -21,7 +21,7 @@ package org.petero.droidfish.engine;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import org.petero.droidfish.EGTBOptions; import org.petero.droidfish.EngineOptions;
import org.petero.droidfish.engine.cuckoochess.CuckooChessEngine; import org.petero.droidfish.engine.cuckoochess.CuckooChessEngine;
import android.content.Context; import android.content.Context;
@@ -62,7 +62,7 @@ public abstract class UCIEngineBase implements UCIEngine {
} }
@Override @Override
public void initOptions(EGTBOptions egtbOptions) { public void initOptions(EngineOptions engineOptions) {
isUCI = true; isUCI = true;
} }
@@ -84,6 +84,11 @@ public abstract class UCIEngineBase implements UCIEngine {
allOptions.add(optName); allOptions.add(optName);
} }
/** Return true if engine has option optName. */
protected boolean haveOption(String optName) {
return allOptions.contains(optName);
}
@Override @Override
public void setOption(String name, int value) { public void setOption(String name, int value) {
setOption(name, String.format("%d", value)); setOption(name, String.format("%d", value));

View File

@@ -25,7 +25,7 @@ import chess.Position;
import chess.TextIO; import chess.TextIO;
import java.util.ArrayList; import java.util.ArrayList;
import org.petero.droidfish.EGTBOptions; import org.petero.droidfish.EngineOptions;
import org.petero.droidfish.engine.LocalPipe; import org.petero.droidfish.engine.LocalPipe;
import org.petero.droidfish.engine.UCIEngineBase; import org.petero.droidfish.engine.UCIEngineBase;
@@ -74,8 +74,14 @@ public class CuckooChessEngine extends UCIEngineBase {
/** @inheritDoc */ /** @inheritDoc */
@Override @Override
public final void initOptions(EGTBOptions egtbOptions) { public final void initOptions(EngineOptions engineOptions) {
super.initOptions(egtbOptions); super.initOptions(engineOptions);
}
/** @inheritDoc */
@Override
public boolean optionsOk(EngineOptions engineOptions) {
return true;
} }
/** @inheritDoc */ /** @inheritDoc */

View File

@@ -22,7 +22,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.petero.droidfish.EGTBOptions; import org.petero.droidfish.EngineOptions;
import org.petero.droidfish.GUIInterface; 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;
@@ -42,7 +42,7 @@ public class DroidChessController {
private DroidComputerPlayer computerPlayer = null; private DroidComputerPlayer computerPlayer = null;
private PgnToken.PgnTokenReceiver gameTextListener = null; private PgnToken.PgnTokenReceiver gameTextListener = null;
private BookOptions bookOptions = new BookOptions(); private BookOptions bookOptions = new BookOptions();
private EGTBOptions egtbOptions = new EGTBOptions(); private EngineOptions engineOptions = new EngineOptions();
private Game game = null; private Game game = null;
private Move ponderMove = null; private Move ponderMove = null;
private GUIInterface gui; private GUIInterface gui;
@@ -84,7 +84,7 @@ public class DroidChessController {
if (computerPlayer == null) { if (computerPlayer == null) {
computerPlayer = new DroidComputerPlayer(gui.getContext(), listener); computerPlayer = new DroidComputerPlayer(gui.getContext(), listener);
computerPlayer.setBookOptions(bookOptions); computerPlayer.setBookOptions(bookOptions);
computerPlayer.setEgtbOptions(egtbOptions); computerPlayer.setEngineOptions(engineOptions);
} }
computerPlayer.queueStartEngine(searchId, engine); computerPlayer.queueStartEngine(searchId, engine);
searchId++; searchId++;
@@ -152,11 +152,11 @@ public class DroidChessController {
} }
} }
public final synchronized void setEgtbOptions(EGTBOptions options) { public final synchronized void setEngineOptions(EngineOptions options) {
if (!egtbOptions.equals(options)) { if (!engineOptions.equals(options)) {
egtbOptions = options; engineOptions = options;
if (computerPlayer != null) if (computerPlayer != null)
computerPlayer.setEgtbOptions(egtbOptions); computerPlayer.setEngineOptions(engineOptions);
} }
} }