diff --git a/DroidFish/res/values/strings.xml b/DroidFish/res/values/strings.xml index b28ae83..f0aef07 100644 --- a/DroidFish/res/values/strings.xml +++ b/DroidFish/res/values/strings.xml @@ -2,6 +2,7 @@ DroidFish 1 + 16 60 120000 0 @@ -220,6 +221,8 @@ you are not actively using the program.\ Let engine think while waiting for opponent\'s move. Supported by most engines. Threads Number of engine threads (CPU cores) to use. Not supported by all engines. + Hash Table + Hash table size in megabytes Time Control Moves Number of moves between time controls @@ -364,6 +367,18 @@ you are not actively using the program.\ 3 4 + + 16 MB + 32 MB + 64 MB + 128 MB + + + 16 + 32 + 64 + 128 + Whole Game 1 move diff --git a/DroidFish/res/xml/preferences.xml b/DroidFish/res/xml/preferences.xml index 0141490..da8bf6e 100644 --- a/DroidFish/res/xml/preferences.xml +++ b/DroidFish/res/xml/preferences.xml @@ -44,7 +44,15 @@ android:entries="@array/engine_threads_texts" android:defaultValue="@string/engine_threads_default"> - + + + 16) { + int maxMem = (int)(Runtime.getRuntime().maxMemory() / (1024*1024)); + if (maxMem < 16) + maxMem = 16; + if (hashMB > maxMem) + hashMB = maxMem; + } + return hashMB; + } + private void updateButtons() { boolean largeButtons = settings.getBoolean("largeButtons", false); Resources r = getResources(); @@ -918,14 +931,14 @@ public class DroidFish extends Activity implements GUIInterface { private boolean egtbForceReload = false; - private final void setEgtbOptions() { - ctrl.setEgtbOptions(new EGTBOptions(egtbOptions)); - Probe.getInstance().setPath(egtbOptions.gtbPath, egtbForceReload); + private final void setEngineOptions() { + ctrl.setEngineOptions(new EngineOptions(engineOptions)); + Probe.getInstance().setPath(engineOptions.gtbPath, egtbForceReload); egtbForceReload = false; } private final void setEgtbHints(int sq) { - if (!egtbOptions.hints || (sq < 0)) { + if (!engineOptions.hints || (sq < 0)) { cb.setSquareDecorations(null); return; } diff --git a/DroidFish/src/org/petero/droidfish/EGTBOptions.java b/DroidFish/src/org/petero/droidfish/EngineOptions.java similarity index 71% rename from DroidFish/src/org/petero/droidfish/EGTBOptions.java rename to DroidFish/src/org/petero/droidfish/EngineOptions.java index 73f902d..d57d2f6 100644 --- a/DroidFish/src/org/petero/droidfish/EGTBOptions.java +++ b/DroidFish/src/org/petero/droidfish/EngineOptions.java @@ -1,14 +1,16 @@ package org.petero.droidfish; -/** Endgame tablebase probing options. */ -public final class EGTBOptions { +/** Engine options, including endgame tablebase probing options. */ +public final class EngineOptions { + public int hashMB; // Engine hash table size in MB public boolean hints; // Hints when playing/analyzing public boolean hintsEdit; // Hints in "edit board" mode public boolean rootProbe; // Only search optimal moves at root public boolean engineProbe; // Let engine use EGTB public String gtbPath; // GTB directory path - public EGTBOptions() { + public EngineOptions() { + hashMB = 16; hints = false; hintsEdit = false; rootProbe = false; @@ -16,7 +18,8 @@ public final class EGTBOptions { gtbPath = ""; } - public EGTBOptions(EGTBOptions other) { + public EngineOptions(EngineOptions other) { + hashMB = other.hashMB; hints = other.hints; hintsEdit = other.hintsEdit; rootProbe = other.rootProbe; @@ -28,9 +31,10 @@ public final class EGTBOptions { public boolean equals(Object o) { if ((o == null) || (o.getClass() != this.getClass())) return false; - EGTBOptions other = (EGTBOptions)o; + EngineOptions other = (EngineOptions)o; - return ((hints == other.hints) && + return ((hashMB == other.hashMB) && + (hints == other.hints) && (hintsEdit == other.hintsEdit) && (rootProbe == other.rootProbe) && (engineProbe == other.engineProbe) && diff --git a/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java b/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java index 4429ee9..7a4ac11 100644 --- a/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java +++ b/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java @@ -23,7 +23,7 @@ import java.io.FileReader; import java.io.IOException; 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.DroidBook; import org.petero.droidfish.gamelogic.Move; @@ -47,7 +47,7 @@ public class DroidComputerPlayer { private final Context context; private final SearchListener listener; private final DroidBook book; - private EGTBOptions egtbOptions; + private EngineOptions engineOptions; /** Set when "ucinewgame" needs to be sent. */ private boolean newGame = false; @@ -242,7 +242,7 @@ public class DroidComputerPlayer { this.context = context; this.listener = listener; book = DroidBook.getInstance(); - egtbOptions = new EGTBOptions(); + engineOptions = new EngineOptions(); engineState = new EngineState(); searchRequest = null; } @@ -270,8 +270,8 @@ public class DroidComputerPlayer { book.setOptions(options); } - public final void setEgtbOptions(EGTBOptions options) { - egtbOptions = options; + public final void setEngineOptions(EngineOptions options) { + engineOptions = options; } /** 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. */ private final ArrayList movesToSearch(SearchRequest sr) { ArrayList moves = null; - if (egtbOptions.rootProbe) + if (engineOptions.rootProbe) moves = Probe.getInstance().findOptimal(sr.currPos); if (moves != null) { sr.searchMoves = moves; @@ -422,9 +422,14 @@ public class DroidComputerPlayer { } private void killOldEngine(String engine) { - if (engine.equals(engineState.engine)) - return; - shutdownEngine(); + boolean needShutDown = !engine.equals(engineState.engine); + if (!needShutDown) { + UCIEngine uci = uciEngine; + if (uci != null) + needShutDown = !uci.optionsOk(engineOptions); + } + if (needShutDown) + shutdownEngine(); } /** Tell engine to stop searching. */ @@ -662,7 +667,7 @@ public class DroidComputerPlayer { switch (engineState.state) { case READ_OPTIONS: { if (readUCIOption(uci, s)) { - uci.initOptions(egtbOptions); + uci.initOptions(engineOptions); uci.writeLineToEngine("ucinewgame"); uci.writeLineToEngine("isready"); engineState.setState(MainState.WAIT_READY); diff --git a/DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java b/DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java index 5e82137..42e213a 100644 --- a/DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java +++ b/DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java @@ -27,7 +27,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.channels.FileChannel; -import org.petero.droidfish.EGTBOptions; +import org.petero.droidfish.EngineOptions; import org.petero.droidfish.R; import android.content.Context; @@ -91,7 +91,9 @@ public class ExternalEngine extends UCIEngineBase { @Override public void run() { try { - engineProc.waitFor(); + Process ep = engineProc; + if (ep != null) + ep.waitFor(); isRunning = false; if (!startedOk) 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 */ @Override - public void initOptions(EGTBOptions egtbOptions) { - super.initOptions(egtbOptions); - setOption("Hash", 16); - if (egtbOptions.engineProbe) { - setOption("GaviotaTbPath", egtbOptions.gtbPath); + public void initOptions(EngineOptions engineOptions) { + super.initOptions(engineOptions); + hashMB = engineOptions.hashMB; + setOption("Hash", engineOptions.hashMB); + if (engineOptions.engineProbe) { + gaviotaTbPath = engineOptions.gtbPath; + setOption("GaviotaTbPath", engineOptions.gtbPath); 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 */ diff --git a/DroidFish/src/org/petero/droidfish/engine/UCIEngine.java b/DroidFish/src/org/petero/droidfish/engine/UCIEngine.java index c5bedd5..eb2655f 100644 --- a/DroidFish/src/org/petero/droidfish/engine/UCIEngine.java +++ b/DroidFish/src/org/petero/droidfish/engine/UCIEngine.java @@ -18,7 +18,7 @@ package org.petero.droidfish.engine; -import org.petero.droidfish.EGTBOptions; +import org.petero.droidfish.EngineOptions; public interface UCIEngine { @@ -32,7 +32,11 @@ public interface UCIEngine { public void initialize(); /** 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. */ public void shutDown(); diff --git a/DroidFish/src/org/petero/droidfish/engine/UCIEngineBase.java b/DroidFish/src/org/petero/droidfish/engine/UCIEngineBase.java index e916d75..0bce0ff 100644 --- a/DroidFish/src/org/petero/droidfish/engine/UCIEngineBase.java +++ b/DroidFish/src/org/petero/droidfish/engine/UCIEngineBase.java @@ -21,7 +21,7 @@ package org.petero.droidfish.engine; import java.util.HashMap; import java.util.HashSet; -import org.petero.droidfish.EGTBOptions; +import org.petero.droidfish.EngineOptions; import org.petero.droidfish.engine.cuckoochess.CuckooChessEngine; import android.content.Context; @@ -62,7 +62,7 @@ public abstract class UCIEngineBase implements UCIEngine { } @Override - public void initOptions(EGTBOptions egtbOptions) { + public void initOptions(EngineOptions engineOptions) { isUCI = true; } @@ -84,6 +84,11 @@ public abstract class UCIEngineBase implements UCIEngine { allOptions.add(optName); } + /** Return true if engine has option optName. */ + protected boolean haveOption(String optName) { + return allOptions.contains(optName); + } + @Override public void setOption(String name, int value) { setOption(name, String.format("%d", value)); diff --git a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/CuckooChessEngine.java b/DroidFish/src/org/petero/droidfish/engine/cuckoochess/CuckooChessEngine.java index 08c06d7..9e62502 100644 --- a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/CuckooChessEngine.java +++ b/DroidFish/src/org/petero/droidfish/engine/cuckoochess/CuckooChessEngine.java @@ -25,7 +25,7 @@ import chess.Position; import chess.TextIO; 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.UCIEngineBase; @@ -74,8 +74,14 @@ public class CuckooChessEngine extends UCIEngineBase { /** @inheritDoc */ @Override - public final void initOptions(EGTBOptions egtbOptions) { - super.initOptions(egtbOptions); + public final void initOptions(EngineOptions engineOptions) { + super.initOptions(engineOptions); + } + + /** @inheritDoc */ + @Override + public boolean optionsOk(EngineOptions engineOptions) { + return true; } /** @inheritDoc */ diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java b/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java index 9df68ce..8d2b93e 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java @@ -22,7 +22,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.petero.droidfish.EGTBOptions; +import org.petero.droidfish.EngineOptions; import org.petero.droidfish.GUIInterface; import org.petero.droidfish.GameMode; import org.petero.droidfish.PGNOptions; @@ -42,7 +42,7 @@ public class DroidChessController { private DroidComputerPlayer computerPlayer = null; private PgnToken.PgnTokenReceiver gameTextListener = null; private BookOptions bookOptions = new BookOptions(); - private EGTBOptions egtbOptions = new EGTBOptions(); + private EngineOptions engineOptions = new EngineOptions(); private Game game = null; private Move ponderMove = null; private GUIInterface gui; @@ -84,7 +84,7 @@ public class DroidChessController { if (computerPlayer == null) { computerPlayer = new DroidComputerPlayer(gui.getContext(), listener); computerPlayer.setBookOptions(bookOptions); - computerPlayer.setEgtbOptions(egtbOptions); + computerPlayer.setEngineOptions(engineOptions); } computerPlayer.queueStartEngine(searchId, engine); searchId++; @@ -152,11 +152,11 @@ public class DroidChessController { } } - public final synchronized void setEgtbOptions(EGTBOptions options) { - if (!egtbOptions.equals(options)) { - egtbOptions = options; + public final synchronized void setEngineOptions(EngineOptions options) { + if (!engineOptions.equals(options)) { + engineOptions = options; if (computerPlayer != null) - computerPlayer.setEgtbOptions(egtbOptions); + computerPlayer.setEngineOptions(engineOptions); } }