diff --git a/DroidFish/src/org/petero/droidfish/ButtonActions.java b/DroidFish/src/org/petero/droidfish/ButtonActions.java index ea8e0b8..102f8ed 100644 --- a/DroidFish/src/org/petero/droidfish/ButtonActions.java +++ b/DroidFish/src/org/petero/droidfish/ButtonActions.java @@ -86,7 +86,7 @@ public class ButtonActions { for (UIAction a : menuActions) { if (a != null) { haveActions = true; - if (a.enabled()) + if (a.enabled()) haveEnabledActions = true; } } diff --git a/DroidFish/src/org/petero/droidfish/DroidFish.java b/DroidFish/src/org/petero/droidfish/DroidFish.java index e05fab6..3d7b705 100644 --- a/DroidFish/src/org/petero/droidfish/DroidFish.java +++ b/DroidFish/src/org/petero/droidfish/DroidFish.java @@ -2897,8 +2897,6 @@ public class DroidFish extends Activity implements GUIInterface { /** Return true if engine UCI options can be set now. */ private final boolean canSetEngineOptions() { - if (!ctrl.computerIdle()) - return false; UCIOptions uciOpts = ctrl.getUCIOptions(); if (uciOpts == null) return false; diff --git a/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java b/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java index ce614e4..32b3deb 100644 --- a/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java +++ b/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java @@ -21,6 +21,7 @@ package org.petero.droidfish.engine; import java.util.ArrayList; import java.util.Locale; import java.util.Map; +import java.util.TreeMap; import org.petero.droidfish.EngineOptions; import org.petero.droidfish.book.BookOptions; @@ -46,7 +47,9 @@ public class DroidComputerPlayer { private final Context context; private final SearchListener listener; private final DroidBook book; - private EngineOptions engineOptions; + private EngineOptions engineOptions = new EngineOptions(); + /** Pending UCI options to send when engine becomes idle. */ + private Map pendingOptions = new TreeMap(); /** Set when "ucinewgame" needs to be sent. */ private boolean newGame = false; @@ -226,8 +229,8 @@ public class DroidComputerPlayer { } } - private EngineState engineState; - private SearchRequest searchRequest; + private EngineState engineState = new EngineState(); + private SearchRequest searchRequest = null; private Thread engineMonitor; /** Constructor. Starts engine process if not already started. */ @@ -235,9 +238,6 @@ public class DroidComputerPlayer { this.context = context; this.listener = listener; book = DroidBook.getInstance(); - engineOptions = new EngineOptions(); - engineState = new EngineState(); - searchRequest = null; } /** Return true if computer player is consuming CPU time. */ @@ -253,16 +253,30 @@ public class DroidComputerPlayer { } } - /** Return true if computer player is in IDLE state. */ - public final synchronized boolean computerIdle() { - return engineState.state == MainState.IDLE; + /** Return true if computer player has been loaded. */ + public final synchronized boolean computerLoaded() { + return (engineState.state != MainState.READ_OPTIONS) && + (engineState.state != MainState.DEAD); } - + public final synchronized UCIOptions getUCIOptions() { UCIEngine uci = uciEngine; if (uci == null) return null; - return uci.getUCIOptions(); + UCIOptions opts = uci.getUCIOptions(); + if (opts == null) + return null; + try { + opts = opts.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + for (Map.Entry e : pendingOptions.entrySet()) { + UCIOptions.OptionBase o = opts.getOption(e.getKey()); + if (o != null) + o.setFromString(e.getValue()); + } + return opts; } /** Return maximum number of PVs supported by engine. */ @@ -279,11 +293,27 @@ public class DroidComputerPlayer { engineOptions = options; } + /** Send pending UCI option changes to the engine. */ + private synchronized boolean applyPendingOptions() { + if (pendingOptions.isEmpty()) + return false; + boolean modified = false; + UCIEngine uci = uciEngine; + if (uci != null) + modified = uci.setUCIOptions(pendingOptions); + pendingOptions.clear(); + return modified; + } + public synchronized void setEngineUCIOptions(Map uciOptions) { - if (engineState.state == MainState.IDLE) { + pendingOptions.putAll(uciOptions); + boolean modified = true; + if (engineState.state == MainState.IDLE) + modified = applyPendingOptions(); + if (modified) { UCIEngine uci = uciEngine; if (uci != null) - uci.setUCIOptions(uciOptions); + uci.saveIniFile(getUCIOptions()); } } @@ -297,8 +327,8 @@ public class DroidComputerPlayer { return engineName; } - /** Clear transposition table. Takes effect when next search started. */ - public final synchronized void clearTT() { + /** Sends "ucinewgame". Takes effect when next search started. */ + public final synchronized void uciNewGame() { newGame = true; } @@ -508,7 +538,7 @@ public class DroidComputerPlayer { return; } - // Send "ucinewgame" (clear hash table) if needed + // Send "ucinewgame" if needed if (newGame) { uciEngine.writeLineToEngine("ucinewgame"); uciEngine.writeLineToEngine("isready"); @@ -517,6 +547,13 @@ public class DroidComputerPlayer { return; } + // Apply pending UCI option changes + if (applyPendingOptions()) { + uciEngine.writeLineToEngine("isready"); + engineState.setState(MainState.WAIT_READY); + return; + } + // Check if only engine start was requested boolean isSearch = sr.isSearch; boolean isAnalyze = sr.isAnalyze; @@ -681,6 +718,7 @@ public class DroidComputerPlayer { switch (engineState.state) { case READ_OPTIONS: { if (readUCIOption(uci, s)) { + pendingOptions.clear(); uci.initOptions(engineOptions); uci.applyIniFile(); uci.writeLineToEngine("ucinewgame"); diff --git a/DroidFish/src/org/petero/droidfish/engine/InternalStockFish.java b/DroidFish/src/org/petero/droidfish/engine/InternalStockFish.java index 15a2e50..c21e47d 100644 --- a/DroidFish/src/org/petero/droidfish/engine/InternalStockFish.java +++ b/DroidFish/src/org/petero/droidfish/engine/InternalStockFish.java @@ -52,9 +52,7 @@ public class InternalStockFish extends ExternalEngine { if (!super.configurableOption(name)) return false; if (name.equals("skill level") || name.equals("write debug log") || - name.equals("write search log") || name.equals("search log filename") || - name.equals("book file") || name.equals("best book move") || - name.equals("ownbook")) + name.equals("write search log")) return false; return true; } diff --git a/DroidFish/src/org/petero/droidfish/engine/UCIEngine.java b/DroidFish/src/org/petero/droidfish/engine/UCIEngine.java index ffaa64e..82b9dc0 100644 --- a/DroidFish/src/org/petero/droidfish/engine/UCIEngine.java +++ b/DroidFish/src/org/petero/droidfish/engine/UCIEngine.java @@ -40,7 +40,10 @@ public interface UCIEngine { public void applyIniFile(); /** Set engine UCI options. */ - public void setUCIOptions(Map uciOptions); + public boolean setUCIOptions(Map uciOptions); + + /** Save non-default UCI option values to file. */ + public void saveIniFile(UCIOptions options); /** Get engine UCI options. */ public UCIOptions getUCIOptions(); diff --git a/DroidFish/src/org/petero/droidfish/engine/UCIEngineBase.java b/DroidFish/src/org/petero/droidfish/engine/UCIEngineBase.java index fdc0810..31a0a8b 100644 --- a/DroidFish/src/org/petero/droidfish/engine/UCIEngineBase.java +++ b/DroidFish/src/org/petero/droidfish/engine/UCIEngineBase.java @@ -35,7 +35,7 @@ import android.content.Context; public abstract class UCIEngineBase implements UCIEngine { private boolean processAlive; - UCIOptions options; + private UCIOptions options; protected boolean isUCI; public static UCIEngine getEngine(Context context, String engine, @@ -99,7 +99,7 @@ public abstract class UCIEngineBase implements UCIEngine { } @Override - public final void setUCIOptions(Map uciOptions) { + public final boolean setUCIOptions(Map uciOptions) { boolean modified = false; for (Map.Entry ent : uciOptions.entrySet()) { String key = ((String)ent.getKey()).toLowerCase(Locale.US); @@ -107,23 +107,26 @@ public abstract class UCIEngineBase implements UCIEngine { if (configurableOption(key)) modified |= setOption(key, value); } - if (modified) { // Save .ini file - Properties iniOptions = new Properties(); - for (String name : options.getOptionNames()) { - UCIOptions.OptionBase o = options.getOption(name); - if (configurableOption(name) && o.modified()) - iniOptions.put(o.name, o.getStringValue()); - } - File optionsFile = getOptionsFile(); - FileOutputStream os = null; - try { - os = new FileOutputStream(optionsFile); - iniOptions.store(os, null); - } catch (IOException ex) { - } finally { - if (os != null) - try { os.close(); } catch (IOException ex) {} - } + return modified; + } + + @Override + public final void saveIniFile(UCIOptions options) { + Properties iniOptions = new Properties(); + for (String name : options.getOptionNames()) { + UCIOptions.OptionBase o = options.getOption(name); + if (configurableOption(name) && o.modified()) + iniOptions.put(o.name, o.getStringValue()); + } + File optionsFile = getOptionsFile(); + FileOutputStream os = null; + try { + os = new FileOutputStream(optionsFile); + iniOptions.store(os, null); + } catch (IOException ex) { + } finally { + if (os != null) + try { os.close(); } catch (IOException ex) {} } } diff --git a/DroidFish/src/org/petero/droidfish/engine/UCIOptions.java b/DroidFish/src/org/petero/droidfish/engine/UCIOptions.java index 041d235..f2b91a2 100644 --- a/DroidFish/src/org/petero/droidfish/engine/UCIOptions.java +++ b/DroidFish/src/org/petero/droidfish/engine/UCIOptions.java @@ -6,7 +6,7 @@ import java.util.Locale; import java.util.Map; import java.util.TreeMap; -public class UCIOptions implements Serializable { +public class UCIOptions implements Serializable, Cloneable { private static final long serialVersionUID = 1L; private ArrayList names; private Map options; @@ -19,12 +19,17 @@ public class UCIOptions implements Serializable { STRING } - public abstract static class OptionBase implements Serializable { + public abstract static class OptionBase implements Serializable, Cloneable { private static final long serialVersionUID = 1L; public String name; public Type type; public boolean visible = true; + @Override + public OptionBase clone() throws CloneNotSupportedException { + return (OptionBase)super.clone(); + } + /** Return true if current value != default value. */ abstract public boolean modified(); @@ -204,7 +209,22 @@ public class UCIOptions implements Serializable { options = new TreeMap(); } + @Override + public UCIOptions clone() throws CloneNotSupportedException { + UCIOptions copy = new UCIOptions(); + + copy.names = new ArrayList(); + copy.names.addAll(names); + + copy.options = new TreeMap(); + for (Map.Entry e : options.entrySet()) + copy.options.put(e.getKey(), e.getValue().clone()); + + return copy; + } + public void clear() { + names.clear(); options.clear(); } diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java b/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java index f07a0fc..2ba56ca 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java @@ -94,7 +94,7 @@ public class DroidChessController { computerPlayer.queueStartEngine(searchId, engine); searchId++; game = new Game(gameTextListener, tcData); - computerPlayer.clearTT(); + computerPlayer.uciNewGame(); setPlayerNames(game); updateGameMode(); } @@ -279,7 +279,7 @@ public class DroidChessController { gameTextListener.clear(); updateGameMode(); abortSearch(); - computerPlayer.clearTT(); + computerPlayer.uciNewGame(); updateComputeThreads(); gui.setSelection(-1); updateGUI(); @@ -297,13 +297,10 @@ public class DroidChessController { return (computerPlayer != null) && computerPlayer.computerBusy(); } - /** Return true if computer player is in IDLE state. */ - public final synchronized boolean computerIdle() { - return (computerPlayer != null) && computerPlayer.computerIdle(); - } - + /** Return engine UCI options if an engine has been loaded and has + * reported its UCI options. */ public final synchronized UCIOptions getUCIOptions() { - if (!computerIdle() || computerPlayer == null) + if (computerPlayer == null || !computerPlayer.computerLoaded()) return null; return computerPlayer.getUCIOptions(); }