diff --git a/DroidFishApp/src/main/assets/nn-82215d0fd0df.nnue b/DroidFishApp/src/main/assets/nn-82215d0fd0df.nnue new file mode 100644 index 0000000..d010366 Binary files /dev/null and b/DroidFishApp/src/main/assets/nn-82215d0fd0df.nnue differ diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/engine/ExternalEngine.java b/DroidFishApp/src/main/java/org/petero/droidfish/engine/ExternalEngine.java index 3ec5ca2..b2d58c3 100644 --- a/DroidFishApp/src/main/java/org/petero/droidfish/engine/ExternalEngine.java +++ b/DroidFishApp/src/main/java/org/petero/droidfish/engine/ExternalEngine.java @@ -180,14 +180,17 @@ public class ExternalEngine extends UCIEngineBase { if (files == null) return; for (File f : files) { - if (!f.getCanonicalPath().equals(exePath)) + if (!f.getCanonicalPath().equals(exePath) && !keepExeDirFile(f)) f.delete(); } - new File(context.getFilesDir(), "engine.exe").delete(); } catch (IOException ignore) { } } + private boolean keepExeDirFile(File f) { + return InternalStockFish.keepExeDirFile(f); + } + private int hashMB = -1; private String gaviotaTbPath = ""; private String syzygyPath = ""; @@ -292,9 +295,6 @@ public class ExternalEngine extends UCIEngineBase { new File(internalSFPath()).delete(); if (to.exists() && (from.length() == to.length()) && (from.lastModified() == to.lastModified())) return to.getAbsolutePath(); - if (to.exists()) - to.delete(); - to.createNewFile(); try (FileInputStream fis = new FileInputStream(from); FileChannel inFC = fis.getChannel(); FileOutputStream fos = new FileOutputStream(to); diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/engine/InternalStockFish.java b/DroidFishApp/src/main/java/org/petero/droidfish/engine/InternalStockFish.java index 3ddf57c..dd57422 100644 --- a/DroidFishApp/src/main/java/org/petero/droidfish/engine/InternalStockFish.java +++ b/DroidFishApp/src/main/java/org/petero/droidfish/engine/InternalStockFish.java @@ -32,8 +32,13 @@ import java.util.Locale; import android.os.Environment; +import org.petero.droidfish.EngineOptions; + /** Stockfish engine running as process, started from assets resource. */ public class InternalStockFish extends ExternalEngine { + private static final String defaultNet = "nn-82215d0fd0df.nnue"; + private static final String netOption = "evalfile"; + private File defaultNetFile; // To get the full path of the copied default network file public InternalStockFish(Report report, String workDir) { super("", workDir, report); @@ -106,15 +111,30 @@ public class InternalStockFish extends ExternalEngine { // on the assumption that it will reduce memory wear. long oldCSum = readCheckSum(new File(internalSFPath())); long newCSum = computeAssetsCheckSum(sfExe); - if (oldCSum == newCSum) - return to.getAbsolutePath(); + if (oldCSum != newCSum) { + copyAssetFile(sfExe, to); + writeCheckSum(new File(internalSFPath()), newCSum); + } + copyNetFile(exeDir); + return to.getAbsolutePath(); + } - if (to.exists()) - to.delete(); - to.createNewFile(); + /** Copy the Stockfish default network file to "exeDir" if it is not already there. */ + private void copyNetFile(File exeDir) throws IOException { + defaultNetFile = new File(exeDir, defaultNet); + if (defaultNetFile.exists()) + return; + File tmpFile = new File(exeDir, defaultNet + ".tmp"); + copyAssetFile(defaultNet, tmpFile); + if (!tmpFile.renameTo(defaultNetFile)) + throw new IOException("Rename failed"); + } - try (InputStream is = context.getAssets().open(sfExe); - OutputStream os = new FileOutputStream(to)) { + /** Copy a file resource from the AssetManager to the file system, + * so it can be used by native code like the Stockfish engine. */ + private void copyAssetFile(String assetName, File targetFile) throws IOException { + try (InputStream is = context.getAssets().open(assetName); + OutputStream os = new FileOutputStream(targetFile)) { byte[] buf = new byte[8192]; while (true) { int len = is.read(buf); @@ -123,8 +143,34 @@ public class InternalStockFish extends ExternalEngine { os.write(buf, 0, len); } } + } - writeCheckSum(new File(internalSFPath()), newCSum); - return to.getAbsolutePath(); + /** Return true if file "f" should be kept in the exeDir directory. + * It would be inefficient to remove the network file every time + * an engine different from Stockfish is used, so this is a static + * check performed for all engines. */ + public static boolean keepExeDirFile(File f) { + return defaultNet.equals(f.getName()); + } + + @Override + public void initOptions(EngineOptions engineOptions) { + super.initOptions(engineOptions); + UCIOptions.OptionBase opt = getUCIOptions().getOption(netOption); + if (opt != null) + setOption(netOption, opt.getStringValue()); + } + + /** Handles setting the EvalFile UCI option to a full path if needed, + * pointing to the network file embedded in DroidFish. */ + @Override + public boolean setOption(String name, String value) { + if (name.toLowerCase(Locale.US).equals(netOption) && defaultNet.equals(value)) { + getUCIOptions().getOption(name).setFromString(value); + value = defaultNetFile.getAbsolutePath(); + writeLineToEngine(String.format(Locale.US, "setoption name %s value %s", name, value)); + return true; + } + return super.setOption(name, value); } } diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/engine/UCIEngineBase.java b/DroidFishApp/src/main/java/org/petero/droidfish/engine/UCIEngineBase.java index 08f8a11..4caad1b 100644 --- a/DroidFishApp/src/main/java/org/petero/droidfish/engine/UCIEngineBase.java +++ b/DroidFishApp/src/main/java/org/petero/droidfish/engine/UCIEngineBase.java @@ -290,7 +290,7 @@ public abstract class UCIEngineBase implements UCIEngine { } @Override - public final boolean setOption(String name, String value) { + public boolean setOption(String name, String value) { if (!options.contains(name)) return false; UCIOptions.OptionBase o = options.getOption(name);