Skip to content

Commit

Permalink
Add Leela Chess Zero as Internal Engine
Browse files Browse the repository at this point in the history
  • Loading branch information
lealgo committed Mar 1, 2020
1 parent f99e24e commit e273e07
Show file tree
Hide file tree
Showing 20 changed files with 217 additions and 10 deletions.
21 changes: 20 additions & 1 deletion DroidFishApp/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ task copyToAssets(type: Copy, dependsOn: 'externalNativeBuildRelease') {
}
into 'src/main/assets'
}

// Download Url to File
def download(address, directory, file) {
def dest_dir = new File(directory)
if (!dest_dir.exists())
dest_dir.mkdirs()
new File(dest_dir, file).withOutputStream {
os -> os << new URL(address).openStream()
}
}

// Copy Leela binaries to assets directory
task copyLeelaAssets() {
def assetsPath = 'DroidFishApp/src/main/assets/'
download('file:///home/leandro/projects/chess/leela/android-bin/0.25borg/arm64-v8a/lc0', assetsPath + 'arm64-v8a', 'lc0')
download('file:///home/leandro/projects/chess/leela/android-bin/0.25borg/armeabi-v7a/lc0', assetsPath + 'armeabi-v7a', 'lc0')
download('file:///home/leandro/projects/chess/leela/android-bin/0.25borg/networks/weights', assetsPath + 'networks', 'weights')
}

tasks.withType(JavaCompile) {
t -> t.dependsOn copyToAssets
t -> t.dependsOn copyToAssets, copyLeelaAssets
}
12 changes: 9 additions & 3 deletions DroidFishApp/src/main/java/org/petero/droidfish/DroidFish.java
Original file line number Diff line number Diff line change
Expand Up @@ -1341,9 +1341,13 @@ private void setEngineTitle(String engine, int strength) {
int idx = engine.lastIndexOf('/');
eName = engine.substring(idx + 1);
} else {
eName = getString("cuckoochess".equals(engine) ?
R.string.cuckoochess_engine :
R.string.stockfish_engine);
if ("cuckoochess".equals(engine))
eName = getString(R.string.cuckoochess_engine);
else if ("stockfish".equals(engine))
eName = getString(R.string.stockfish_engine);
else
eName = getString(R.string.leela_engine);

boolean analysis = (ctrl != null) && ctrl.analysisMode();
if ((strength < 1000) && !analysis)
eName = String.format(Locale.US, "%s: %d%%", eName, strength / 10);
Expand Down Expand Up @@ -2450,13 +2454,15 @@ else if (item == numFiles + 2)
private static boolean reservedEngineName(String name) {
return "cuckoochess".equals(name) ||
"stockfish".equals(name) ||
"leela".equals(name) ||
name.endsWith(".ini");
}

private Dialog selectEngineDialog(final boolean abortOnCancel) {
final ArrayList<String> items = new ArrayList<>();
final ArrayList<String> ids = new ArrayList<>();
ids.add("stockfish"); items.add(getString(R.string.stockfish_engine));
ids.add("leela"); items.add(getString(R.string.leela_engine));
ids.add("cuckoochess"); items.add(getString(R.string.cuckoochess_engine));

if (storageAvailable()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,24 @@ public class EngineUtil {
System.loadLibrary("nativeutil");
}

/** Return file name of the internal stockfish executable. */
public static String internalStockFishName() {
static String getABI() {
String abi = Build.CPU_ABI;
if (!"x86".equals(abi) &&
!"x86_64".equals(abi) &&
!"arm64-v8a".equals(abi)) {
!"x86_64".equals(abi) &&
!"arm64-v8a".equals(abi)) {
abi = "armeabi-v7a"; // Unknown ABI, assume 32-bit arm
}
return abi + "/stockfish";
return abi;
}

/** Return file name of the internal stockfish executable. */
public static String internalStockFishName() {
return getABI() + "/stockfish";
}

/** Return file name of the internal Leela executable. */
public static String internalLeelaName() {
return getABI() + "/lc0";
}

/** Return true if file "engine" is a network engine. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ protected String internalSFPath() {
return context.getFilesDir().getAbsolutePath() + "/internal_sf";
}

protected String internalSWPath() {
return context.getFilesDir().getAbsolutePath() + "/internal_sw";
}

/** @inheritDoc */
@Override
protected void startProcess() {
Expand Down Expand Up @@ -181,7 +185,7 @@ private void cleanUpExeDir(File exeDir, String exePath) {
if (files == null)
return;
for (File f : files) {
if (!f.getCanonicalPath().equals(exePath))
if (!f.getCanonicalPath().equals(exePath) && !f.isDirectory())
f.delete();
}
new File(context.getFilesDir(), "engine.exe").delete();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
DroidFish - An Android chess program.
Copyright (C) 2011-2014 Peter Österlund, [email protected]
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package org.petero.droidfish.engine;

import android.os.Environment;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;

/** Leela engine running as process, started from assets resource. */
public class InternalLeela extends ExternalEngine {

public InternalLeela(Report report, String workDir) {
super("", workDir, report);
}

@Override
protected File getOptionsFile() {
File extDir = Environment.getExternalStorageDirectory();
return new File(extDir, "/DroidFish/uci/leela.ini");
}

private long readCheckSum(File f) {
try (InputStream is = new FileInputStream(f);
DataInputStream dis = new DataInputStream(is)) {
return dis.readLong();
} catch (IOException e) {
return 0;
}
}

private void writeCheckSum(File f, long checkSum) {
try (OutputStream os = new FileOutputStream(f);
DataOutputStream dos = new DataOutputStream(os)) {
dos.writeLong(checkSum);
} catch (IOException ignore) {
}
}

private long computeAssetsCheckSum(String sfExe) {

try (InputStream is = context.getAssets().open(sfExe)) {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] buf = new byte[8192];
while (true) {
int len = is.read(buf);
if (len <= 0)
break;
md.update(buf, 0, len);
}
byte[] digest = md.digest(new byte[]{0});
long ret = 0;
for (int i = 0; i < 8; i++) {
ret ^= ((long)digest[i]) << (i * 8);
}
return ret;
} catch (IOException e) {
return -1;
} catch (NoSuchAlgorithmException e) {
return -1;
}
}

@Override
protected String copyFile(File from, File exeDir) throws IOException {
File to = new File(exeDir, "engine.exe");
final String leelaExe = EngineUtil.internalLeelaName();

// The checksum test is to avoid writing to /data unless necessary,
// on the assumption that it will reduce memory wear.
long oldCSum = readCheckSum(new File(internalSFPath()));
long newCSum = computeAssetsCheckSum(leelaExe);
if (oldCSum == newCSum)
return to.getAbsolutePath();

// Copy engine
if (to.exists())
to.delete();
to.createNewFile();

try (InputStream is = context.getAssets().open(leelaExe);
OutputStream os = new FileOutputStream(to)) {
byte[] buf = new byte[8192];
while (true) {
int len = is.read(buf);
if (len <= 0)
break;
os.write(buf, 0, len);
}
}

writeCheckSum(new File(internalSFPath()), newCSum);
final String exePath = to.getAbsolutePath();

// Check if weights file changed
final String networkPath = "networks/";
final String weightsFile = "weights";

oldCSum = readCheckSum(new File(internalSWPath()));
newCSum = computeAssetsCheckSum(networkPath + weightsFile);
if (oldCSum == newCSum)
return exePath;

// Copy weights file
File networksDir = new File(exeDir, networkPath);
if (!networksDir.exists())
networksDir.mkdir();
to = new File(networksDir, weightsFile);
if (to.exists())
to.delete();
to.createNewFile();

try (InputStream is = context.getAssets().open(networkPath + weightsFile);
OutputStream os = new FileOutputStream(to)) {
byte[] buf = new byte[8192];
while (true) {
int len = is.read(buf);
if (len <= 0)
break;
os.write(buf, 0, len);
}
}

writeCheckSum(new File(internalSWPath()), newCSum);

return exePath;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public static UCIEngine getEngine(String engine,
return new CuckooChessEngine();
else if ("stockfish".equals(engine))
return new InternalStockFish(report, engineOptions.workDir);
else if ("leela".equals(engine))
return new InternalLeela(report, engineOptions.workDir);
else if (EngineUtil.isOpenExchangeEngine(engine))
return new OpenExchangeEngine(engine, engineOptions.workDir, report);
else if (EngineUtil.isNetEngine(engine))
Expand Down
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-be/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@
<item>Па-нямецку</item>
<item>Па-іспанску</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@ Um gegebenenfalls Strom zu sparen, ist es empfehlenswert, dass Sie diese Paramet
<item>Zugansage auf Deutsch</item>
<item>Zugansage auf Spanisch</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@ Si está usted utilizando la batería, se recomienda que cambie los ajustes para
<item>Texto hablado en alemán</item>
<item>Texto hablado en español</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@ Lorsque que vous êtes sur batterie, il est recommandé de changer les paramètr
<item>German Speech</item>
<item>Spanish Speech</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-it/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@ Se l\'alimentazione è a batteria, è consigliabile modificare le impostazioni p
<item>Annuncio vocale in tedesco</item>
<item>Annuncio vocale in Spagnolo</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-ko/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -806,4 +806,5 @@ DroidFish는 백그라운드에 실행 중인 상태에서 다음과 같이 설
<item>독일어 말하기</item>
<item>스페인어 말하기</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-nl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@ Als uw telefoon op batterij werkt is het aan te raden om deze instellingen te wi
<item>German Speech</item>
<item>Spanish Speech</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-pl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@ Jeśli pracujesz na baterii, zalecana jest zmiana ustawień, w celu oszczędzani
<item>Mowa niemiecka</item>
<item>Mowa hiszpańska</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-pt/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@ Se você está usando somente a bateria, recomenda-se que você mude as configur
<item>Voz em alemão</item>
<item>Voz em espanhol</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@
<item>По-немецки</item>
<item>По-испански</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-tr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@ Pil gücüyle çalışıyorsanız, pil gücünden tasarruf etmek için ayarları
<item>Almanca konuşma</item>
<item>İspanyolca konuşma</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-uk/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@
<item>Німецька вимова</item>
<item>Іспанська вимова</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,4 +802,5 @@
<item>德语语音</item>
<item>西班牙语音</item>
</string-array>
<string name="leela_engine">Leela Chess Zero</string>
</resources>
1 change: 1 addition & 0 deletions DroidFishApp/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ If you are running on battery power, it is recommended that you change settings
<string name="slash_not_allowed">Character &#47; not allowed</string>
<string name="engine_error">Engine error</string>
<string name="stockfish_engine">Stockfish</string>
<string name="leela_engine">Leela Chess Zero</string>
<string name="cuckoochess_engine">CuckooChess</string>
<string name="select_engine">Select Engine</string>
<string name="engine_options">Engine Options</string>
Expand Down

0 comments on commit e273e07

Please sign in to comment.