mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2024-11-27 06:10:28 +01:00
Merge branch 'eco-code'.
Re-implemented functionality to be more CPU efficient.
This commit is contained in:
commit
ab601027b1
11
DroidFish/.externalToolBuilders/ECO_Builder.launch
Normal file
11
DroidFish/.externalToolBuilders/ECO_Builder.launch
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
|
||||
<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <resources> <item path="/DroidFish/assets/eco.dat" type="1"/> </resources>}"/>
|
||||
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <resources> <item path="/DroidFish/src/org/petero/droidfish" type="2"/> </resources>}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="/usr/bin/java"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="org.petero.droidfish/buildtools/EcoBuilder ${workspace_loc:/DroidFish/src/org/petero/droidfish/buildtools/eco.pgn} ${workspace_loc:/DroidFish/assets/eco.dat}"/>
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/DroidFish/bin/classes}"/>
|
||||
</launchConfiguration>
|
|
@ -40,6 +40,16 @@
|
|||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>auto,full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/ECO_Builder.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.andmore.ApkBuilder</name>
|
||||
<arguments>
|
||||
|
|
1
DroidFish/assets/.gitignore
vendored
1
DroidFish/assets/.gitignore
vendored
|
@ -9,3 +9,4 @@
|
|||
/stockfish-x86
|
||||
/stockfish-x86_64
|
||||
/stockfish-x86-nopie
|
||||
/eco.dat
|
||||
|
|
|
@ -162,7 +162,6 @@ public class DroidFish extends Activity
|
|||
|
||||
// FIXME!!! Show extended book info. (Win percent, number of games, performance rating, etc.)
|
||||
// FIXME!!! Green color for "main move". Red color for "don't play in tournaments" moves.
|
||||
// FIXME!!! ECO opening codes
|
||||
|
||||
// FIXME!!! Option to display coordinates in border outside chess board.
|
||||
|
||||
|
@ -1450,7 +1449,7 @@ public class DroidFish extends Activity
|
|||
try {
|
||||
String engine = settings.getString("engine", "stockfish");
|
||||
if (EngineUtil.isNetEngine(engine)) {
|
||||
String[] lines = Util.readFile(engine);
|
||||
String[] lines = FileUtil.readFile(engine);
|
||||
if (lines.length >= 3)
|
||||
id = lines[1] + ":" + lines[2];
|
||||
}
|
||||
|
@ -1948,6 +1947,7 @@ public class DroidFish extends Activity
|
|||
private String thinkingStr1 = "";
|
||||
private String thinkingStr2 = "";
|
||||
private String bookInfoStr = "";
|
||||
private String ecoInfoStr = "";
|
||||
private String variantStr = "";
|
||||
private ArrayList<ArrayList<Move>> pvMoves = new ArrayList<ArrayList<Move>>();
|
||||
private ArrayList<Move> bookMoves = null;
|
||||
|
@ -1958,8 +1958,9 @@ public class DroidFish extends Activity
|
|||
thinkingStr1 = ti.pvStr;
|
||||
thinkingStr2 = ti.statStr;
|
||||
bookInfoStr = ti.bookInfo;
|
||||
this.pvMoves = ti.pvMoves;
|
||||
this.bookMoves = ti.bookMoves;
|
||||
ecoInfoStr = ti.eco;
|
||||
pvMoves = ti.pvMoves;
|
||||
bookMoves = ti.bookMoves;
|
||||
updateThinkingInfo();
|
||||
|
||||
if (ctrl.computerBusy()) {
|
||||
|
@ -1986,18 +1987,20 @@ public class DroidFish extends Activity
|
|||
}
|
||||
thinking.setText(s, TextView.BufferType.SPANNABLE);
|
||||
}
|
||||
if (mShowBookHints && (bookInfoStr.length() > 0)) {
|
||||
String s = "";
|
||||
if (!thinkingEmpty)
|
||||
s += "<br>";
|
||||
if (mShowBookHints && !ecoInfoStr.isEmpty()) {
|
||||
String s = thinkingEmpty ? "" : "<br>";
|
||||
s += ecoInfoStr;
|
||||
thinking.append(Html.fromHtml(s));
|
||||
thinkingEmpty = false;
|
||||
}
|
||||
if (mShowBookHints && !bookInfoStr.isEmpty()) {
|
||||
String s = thinkingEmpty ? "" : "<br>";
|
||||
s += Util.boldStart + getString(R.string.book) + Util.boldStop + bookInfoStr;
|
||||
thinking.append(Html.fromHtml(s));
|
||||
thinkingEmpty = false;
|
||||
}
|
||||
if (showVariationLine && (variantStr.indexOf(' ') >= 0)) {
|
||||
String s = "";
|
||||
if (!thinkingEmpty)
|
||||
s += "<br>";
|
||||
String s = thinkingEmpty ? "" : "<br>";
|
||||
s += Util.boldStart + getString(R.string.variation) + Util.boldStop + variantStr;
|
||||
thinking.append(Html.fromHtml(s));
|
||||
thinkingEmpty = false;
|
||||
|
@ -2340,7 +2343,7 @@ public class DroidFish extends Activity
|
|||
WebView wv = new WebView(this);
|
||||
builder.setView(wv);
|
||||
InputStream is = getResources().openRawResource(R.raw.about);
|
||||
String data = Util.readFromStream(is);
|
||||
String data = FileUtil.readFromStream(is);
|
||||
if (data == null)
|
||||
data = "";
|
||||
try { is.close(); } catch (IOException e1) {}
|
||||
|
@ -3251,7 +3254,7 @@ public class DroidFish extends Activity
|
|||
String port = "0";
|
||||
try {
|
||||
if (EngineUtil.isNetEngine(networkEngineToConfig)) {
|
||||
String[] lines = Util.readFile(networkEngineToConfig);
|
||||
String[] lines = FileUtil.readFile(networkEngineToConfig);
|
||||
if (lines.length > 1)
|
||||
hostName = lines[1];
|
||||
if (lines.length > 2)
|
||||
|
@ -3754,6 +3757,7 @@ public class DroidFish extends Activity
|
|||
return currPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpToDate() {
|
||||
return upToDate;
|
||||
}
|
||||
|
@ -3818,6 +3822,7 @@ public class DroidFish extends Activity
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processToken(Node node, int type, String token) {
|
||||
if ( (prevType == PgnToken.RIGHT_BRACKET) &&
|
||||
(type != PgnToken.LEFT_BRACKET)) {
|
||||
|
|
45
DroidFish/src/org/petero/droidfish/FileUtil.java
Normal file
45
DroidFish/src/org/petero/droidfish/FileUtil.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
package org.petero.droidfish;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class FileUtil {
|
||||
/** Read a text file. Return string array with one string per line. */
|
||||
public static String[] readFile(String filename) throws IOException {
|
||||
ArrayList<String> ret = new ArrayList<String>();
|
||||
InputStream inStream = new FileInputStream(filename);
|
||||
InputStreamReader inFile = new InputStreamReader(inStream);
|
||||
BufferedReader inBuf = new BufferedReader(inFile);
|
||||
String line;
|
||||
while ((line = inBuf.readLine()) != null)
|
||||
ret.add(line);
|
||||
inBuf.close();
|
||||
return ret.toArray(new String[ret.size()]);
|
||||
}
|
||||
|
||||
/** Read all data from an input stream. Return null if IO error. */
|
||||
public static String readFromStream(InputStream is) {
|
||||
InputStreamReader isr;
|
||||
try {
|
||||
isr = new InputStreamReader(is, "UTF-8");
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line);
|
||||
sb.append('\n');
|
||||
}
|
||||
br.close();
|
||||
return sb.toString();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,6 +59,7 @@ public interface GUIInterface {
|
|||
public String bookInfo;
|
||||
public ArrayList<ArrayList<Move>> pvMoves;
|
||||
public ArrayList<Move> bookMoves;
|
||||
public String eco;
|
||||
}
|
||||
|
||||
/** Update the computer thinking information. */
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
package org.petero.droidfish;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.petero.droidfish.gamelogic.Piece;
|
||||
import org.petero.droidfish.gamelogic.Position;
|
||||
|
||||
|
@ -41,40 +33,6 @@ public final class Util {
|
|||
}
|
||||
}
|
||||
|
||||
/** Read a text file. Return string array with one string per line. */
|
||||
public static String[] readFile(String networkEngineToConfig) throws IOException {
|
||||
ArrayList<String> ret = new ArrayList<String>();
|
||||
InputStream inStream = new FileInputStream(networkEngineToConfig);
|
||||
InputStreamReader inFile = new InputStreamReader(inStream);
|
||||
BufferedReader inBuf = new BufferedReader(inFile);
|
||||
String line;
|
||||
while ((line = inBuf.readLine()) != null)
|
||||
ret.add(line);
|
||||
inBuf.close();
|
||||
return ret.toArray(new String[ret.size()]);
|
||||
}
|
||||
|
||||
/** Read all data from an input stream. Return null if IO error. */
|
||||
public static String readFromStream(InputStream is) {
|
||||
InputStreamReader isr;
|
||||
try {
|
||||
isr = new InputStreamReader(is, "UTF-8");
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line);
|
||||
sb.append('\n');
|
||||
}
|
||||
br.close();
|
||||
return sb.toString();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Represent material difference as two unicode strings. */
|
||||
public final static class MaterialDiff {
|
||||
public CharSequence white;
|
||||
|
|
265
DroidFish/src/org/petero/droidfish/book/EcoDb.java
Normal file
265
DroidFish/src/org/petero/droidfish/book/EcoDb.java
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2016 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
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.book;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import org.petero.droidfish.gamelogic.ChessParseError;
|
||||
import org.petero.droidfish.gamelogic.GameTree;
|
||||
import org.petero.droidfish.gamelogic.Move;
|
||||
import org.petero.droidfish.gamelogic.Position;
|
||||
import org.petero.droidfish.gamelogic.TextIO;
|
||||
import org.petero.droidfish.gamelogic.UndoInfo;
|
||||
|
||||
/** ECO code database. */
|
||||
@SuppressLint("UseSparseArrays")
|
||||
public class EcoDb {
|
||||
private static EcoDb instance;
|
||||
|
||||
/** Get singleton instance. */
|
||||
public static EcoDb getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
instance = new EcoDb(context);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/** Get ECO classification for a given tree node. */
|
||||
public String getEco(GameTree gt, GameTree.Node node) {
|
||||
ArrayList<GameTree.Node> gtNodePath = new ArrayList<GameTree.Node>();
|
||||
int nodeIdx = -1;
|
||||
while (node != null) {
|
||||
nodeIdx = findNode(node);
|
||||
if (nodeIdx != -1)
|
||||
break;
|
||||
if (node == gt.rootNode) {
|
||||
Short idx = posHashToNodeIdx.get(gt.startPos.zobristHash());
|
||||
if (idx != null) {
|
||||
nodeIdx = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
gtNodePath.add(node);
|
||||
node = node.getParent();
|
||||
}
|
||||
if (nodeIdx != -1) {
|
||||
Node ecoNode = readNode(nodeIdx);
|
||||
boolean childFound = true;
|
||||
for (int i = gtNodePath.size() - 1; i >= 0; i--) {
|
||||
GameTree.Node gtNode = gtNodePath.get(i);
|
||||
int m = gtNode.move.getCompressedMove();
|
||||
int child = childFound ? ecoNode.firstChild : -1;
|
||||
while (child != -1) {
|
||||
Node cNode = readNode(child);
|
||||
if (cNode.move == m)
|
||||
break;
|
||||
child = cNode.nextSibling;
|
||||
}
|
||||
if (child != -1) {
|
||||
nodeIdx = child;
|
||||
ecoNode = readNode(nodeIdx);
|
||||
} else
|
||||
childFound = false;
|
||||
cacheNode(gtNode, nodeIdx);
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeIdx != -1) {
|
||||
Node n = readNode(nodeIdx);
|
||||
if (n.nameIdx >= 0)
|
||||
return ecoNames[n.nameIdx];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
private static class Node {
|
||||
int move; // Move (compressed) leading to the position corresponding to this node
|
||||
int nameIdx; // Index in names array, or -1
|
||||
int firstChild;
|
||||
int nextSibling;
|
||||
}
|
||||
|
||||
private byte[] nodesBuffer;
|
||||
private String[] ecoNames;
|
||||
private HashMap<Long, Short> posHashToNodeIdx;
|
||||
private WeakLRUCache<GameTree.Node, Integer> gtNodeToIdx;
|
||||
|
||||
/** Return cached Node index corresponding to a GameTree.Node, or -1 if not found. */
|
||||
private int findNode(GameTree.Node node) {
|
||||
Integer idx = gtNodeToIdx.get(node);
|
||||
return idx == null ? -1 : idx;
|
||||
}
|
||||
|
||||
/** Store GameTree.Node to Node index in cache. */
|
||||
private void cacheNode(GameTree.Node node, int nodeIdx) {
|
||||
gtNodeToIdx.put(node, nodeIdx);
|
||||
}
|
||||
|
||||
/** Constructor. */
|
||||
private EcoDb(Context context) {
|
||||
posHashToNodeIdx = new HashMap<Long, Short>();
|
||||
gtNodeToIdx = new WeakLRUCache<GameTree.Node, Integer>(50);
|
||||
try {
|
||||
ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
|
||||
InputStream inStream = context.getAssets().open("eco.dat");
|
||||
if (inStream == null)
|
||||
throw new IOException("Can't read ECO database");
|
||||
byte[] buf = new byte[1024];
|
||||
while (true) {
|
||||
int len = inStream.read(buf);
|
||||
if (len <= 0) break;
|
||||
bufStream.write(buf, 0, len);
|
||||
}
|
||||
inStream.close();
|
||||
bufStream.flush();
|
||||
buf = bufStream.toByteArray();
|
||||
int nNodes = 0;
|
||||
while (true) {
|
||||
Node n = readNode(nNodes, buf);
|
||||
if (n.move == 0xffff)
|
||||
break;
|
||||
nNodes++;
|
||||
}
|
||||
nodesBuffer = new byte[nNodes * 8];
|
||||
System.arraycopy(buf, 0, nodesBuffer, 0, nNodes * 8);
|
||||
|
||||
ArrayList<String> names = new ArrayList<String>();
|
||||
int idx = (nNodes + 1) * 8;
|
||||
int start = idx;
|
||||
for (int i = idx; i < buf.length; i++) {
|
||||
if (buf[i] == 0) {
|
||||
names.add(new String(buf, start, i - start, "UTF-8"));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
ecoNames = names.toArray(new String[names.size()]);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("Can't read ECO database");
|
||||
}
|
||||
try {
|
||||
if (nodesBuffer.length > 0) {
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
populateCache(pos, 0);
|
||||
}
|
||||
} catch (ChessParseError e) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialize popHashToNodeIdx. */
|
||||
private void populateCache(Position pos, int nodeIdx) {
|
||||
if (posHashToNodeIdx.get(pos.zobristHash()) == null)
|
||||
posHashToNodeIdx.put(pos.zobristHash(), (short)nodeIdx);
|
||||
Node node = readNode(nodeIdx);
|
||||
int child = node.firstChild;
|
||||
UndoInfo ui = new UndoInfo();
|
||||
while (child != -1) {
|
||||
node = readNode(child);
|
||||
Move m = Move.fromCompressed(node.move);
|
||||
pos.makeMove(m, ui);
|
||||
populateCache(pos, child);
|
||||
pos.unMakeMove(m, ui);
|
||||
child = node.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
private Node readNode(int index) {
|
||||
return readNode(index, nodesBuffer);
|
||||
}
|
||||
|
||||
private static Node readNode(int index, byte[] buf) {
|
||||
Node n = new Node();
|
||||
int o = index * 8;
|
||||
n.move = getU16(buf, o);
|
||||
n.nameIdx = getS16(buf, o + 2);
|
||||
n.firstChild = getS16(buf, o + 4);
|
||||
n.nextSibling = getS16(buf, o + 6);
|
||||
return n;
|
||||
}
|
||||
|
||||
private static int getU16(byte[] buf, int offs) {
|
||||
int b0 = buf[offs] & 255;
|
||||
int b1 = buf[offs + 1] & 255;
|
||||
return (b0 << 8) + b1;
|
||||
}
|
||||
|
||||
private static int getS16(byte[] buf, int offs) {
|
||||
int ret = getU16(buf, offs);
|
||||
if (ret >= 0x8000)
|
||||
ret -= 0x10000;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** A Cache where the keys are weak references and the cache automatically
|
||||
* shrinks when it becomes too large, using approximate LRU ordering.
|
||||
* This cache is not designed to store null values. */
|
||||
private static class WeakLRUCache<K, V> {
|
||||
private WeakHashMap<K, V> mapNew; // Most recently used entries
|
||||
private WeakHashMap<K, V> mapOld; // Older entries
|
||||
private int maxSize;
|
||||
|
||||
public WeakLRUCache(int maxSize) {
|
||||
mapNew = new WeakHashMap<K, V>();
|
||||
mapOld = new WeakHashMap<K, V>();
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
/** Insert a value in the map, replacing any old value with the same key. */
|
||||
public void put(K key, V val) {
|
||||
if (mapNew.containsKey(key)) {
|
||||
mapNew.put(key, val);
|
||||
} else {
|
||||
if (mapOld.containsKey(key))
|
||||
mapOld.remove(key);
|
||||
insertNew(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the value corresponding to key, or null if not found. */
|
||||
public V get(K key) {
|
||||
V val = mapNew.get(key);
|
||||
if (val != null)
|
||||
return val;
|
||||
val = mapOld.get(key);
|
||||
if (val != null) {
|
||||
mapOld.remove(key);
|
||||
insertNew(key, val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
private void insertNew(K key, V val) {
|
||||
if (mapNew.size() >= maxSize) {
|
||||
WeakHashMap<K, V> tmp = mapNew;
|
||||
mapNew = mapOld;
|
||||
mapOld = tmp;
|
||||
mapNew.clear();
|
||||
}
|
||||
mapNew.put(key, val);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -87,7 +87,7 @@ final class InternalBook implements IOpeningBook {
|
|||
try {
|
||||
InputStream inStream = getClass().getResourceAsStream("/book.bin");
|
||||
if (inStream == null)
|
||||
throw new IOException();
|
||||
throw new IOException("Can't read internal opening book");
|
||||
List<Byte> buf = new ArrayList<Byte>(8192);
|
||||
byte[] tmpBuf = new byte[1024];
|
||||
while (true) {
|
||||
|
@ -120,8 +120,7 @@ final class InternalBook implements IOpeningBook {
|
|||
} catch (ChessParseError ex) {
|
||||
throw new RuntimeException();
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Can't read opening book resource");
|
||||
throw new RuntimeException();
|
||||
throw new RuntimeException("Can't read internal opening book");
|
||||
}
|
||||
/* {
|
||||
long t1 = System.currentTimeMillis();
|
||||
|
|
204
DroidFish/src/org/petero/droidfish/buildtools/EcoBuilder.java
Normal file
204
DroidFish/src/org/petero/droidfish/buildtools/EcoBuilder.java
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2016 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
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.buildtools;
|
||||
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.petero.droidfish.FileUtil;
|
||||
import org.petero.droidfish.PGNOptions;
|
||||
import org.petero.droidfish.gamelogic.Game;
|
||||
import org.petero.droidfish.gamelogic.GameTree;
|
||||
import org.petero.droidfish.gamelogic.Move;
|
||||
import org.petero.droidfish.gamelogic.TimeControlData;
|
||||
|
||||
/** Build the ECO data file from eco.pgn. */
|
||||
public class EcoBuilder {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
String ecoPgnFile = args[0];
|
||||
String ecoDatFile = args[1];
|
||||
(new EcoBuilder()).createECOFile(ecoPgnFile, ecoDatFile);
|
||||
}
|
||||
|
||||
private static class Node {
|
||||
int index; // Index in nodes array
|
||||
Move move; // Move leading to the position corresponding to this node
|
||||
int nameIdx; // Index in names array, or -1
|
||||
ArrayList<Node> children = new ArrayList<Node>();
|
||||
Node parent;
|
||||
int lineLength; // Length in plies of line this node came from
|
||||
}
|
||||
private ArrayList<Node> nodes;
|
||||
private ArrayList<String> names;
|
||||
private HashMap<String, Integer> nameToIndex;
|
||||
|
||||
/** Constructor. */
|
||||
private EcoBuilder() {
|
||||
nodes = new ArrayList<Node>();
|
||||
names = new ArrayList<String>();
|
||||
nameToIndex = new HashMap<String, Integer>();
|
||||
Node rootNode = new Node();
|
||||
rootNode.index = 0;
|
||||
rootNode.move = new Move(0, 0, 0);
|
||||
rootNode.nameIdx = -1;
|
||||
rootNode.lineLength = 0;
|
||||
nodes.add(rootNode);
|
||||
}
|
||||
|
||||
/** Read pgn text file, write binary file. */
|
||||
private void createECOFile(String ecoPgnFile, String ecoDatFile) throws Throwable {
|
||||
String[] ecoPgn = FileUtil.readFile(ecoPgnFile);
|
||||
StringBuilder pgn = new StringBuilder();
|
||||
boolean gotMoves = false;
|
||||
for (String line : ecoPgn) {
|
||||
boolean isHeader = line.startsWith("[");
|
||||
if (gotMoves && isHeader) {
|
||||
readGame(pgn.toString());
|
||||
pgn = new StringBuilder();
|
||||
gotMoves = false;
|
||||
}
|
||||
pgn.append(line);
|
||||
pgn.append('\n');
|
||||
gotMoves |= !isHeader;
|
||||
}
|
||||
readGame(pgn.toString());
|
||||
|
||||
writeDataFile(ecoDatFile);
|
||||
}
|
||||
|
||||
/** Read and process one game. */
|
||||
private void readGame(String pgn) throws Throwable {
|
||||
if (pgn.isEmpty())
|
||||
return;
|
||||
Game game = new Game(null, new TimeControlData());
|
||||
PGNOptions options = new PGNOptions();
|
||||
game.readPGN(pgn, options);
|
||||
|
||||
// Determine name of opening
|
||||
HashMap<String,String> headers = new HashMap<String,String>();
|
||||
GameTree tree = game.tree;
|
||||
tree.getHeaders(headers);
|
||||
String eco = headers.get("ECO");
|
||||
String opening = headers.get("Opening");
|
||||
String variation = headers.get("Variation");
|
||||
String name = eco + ": " + opening;
|
||||
if (variation != null)
|
||||
name = name + ", " + variation;
|
||||
|
||||
// Add name to data structures
|
||||
Integer nameIdx = nameToIndex.get(name);
|
||||
if (nameIdx == null) {
|
||||
nameIdx = nameToIndex.size();
|
||||
nameToIndex.put(name, nameIdx);
|
||||
names.add(name);
|
||||
}
|
||||
|
||||
int lineLength = 0;
|
||||
while (true) {
|
||||
if (tree.variations().isEmpty())
|
||||
break;
|
||||
lineLength++;
|
||||
tree.goForward(0);
|
||||
}
|
||||
while (tree.currentNode.getParent() != null)
|
||||
tree.goBack();
|
||||
|
||||
// Add corresponding moves to data structures
|
||||
Node parent = nodes.get(0);
|
||||
while (true) {
|
||||
ArrayList<Move> moves = tree.variations();
|
||||
if (moves.isEmpty())
|
||||
break;
|
||||
Move m = moves.get(0);
|
||||
tree.goForward(0);
|
||||
int oldIdx = -1;
|
||||
for (int i = 0; i < parent.children.size(); i++) {
|
||||
if (parent.children.get(i).move.equals(m)) {
|
||||
oldIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (oldIdx == -1) {
|
||||
Node node = new Node();
|
||||
node.index = nodes.size();
|
||||
node.move = m;
|
||||
node.nameIdx = nameIdx;
|
||||
node.parent = parent;
|
||||
node.lineLength = lineLength;
|
||||
nodes.add(node);
|
||||
parent.children.add(node);
|
||||
parent = node;
|
||||
} else {
|
||||
parent = parent.children.get(oldIdx);
|
||||
if (parent.lineLength > lineLength) {
|
||||
parent.lineLength = lineLength;
|
||||
parent.nameIdx = nameIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Write the binary ECO code data file. */
|
||||
private void writeDataFile(String ecoDatFile) throws Throwable {
|
||||
FileOutputStream out = new FileOutputStream(ecoDatFile);
|
||||
|
||||
// Write nodes
|
||||
byte[] buf = new byte[8];
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
Node n = nodes.get(i);
|
||||
int cm = n.move == null ? 0 : n.move.getCompressedMove();
|
||||
buf[0] = (byte)(cm >> 8); // Move, high byte
|
||||
buf[1] = (byte)(cm & 255); // Move, low byte
|
||||
buf[2] = (byte)(n.nameIdx >> 8); // Index, high byte
|
||||
buf[3] = (byte)(n.nameIdx & 255); // Index, low byte
|
||||
int firstChild = -1;
|
||||
if (n.children.size() > 0)
|
||||
firstChild = n.children.get(0).index;
|
||||
buf[4] = (byte)(firstChild >> 8);
|
||||
buf[5] = (byte)(firstChild & 255);
|
||||
int nextSibling = -1;
|
||||
if (n.parent != null) {
|
||||
ArrayList<Node> siblings = n.parent.children;
|
||||
for (int j = 0; j < siblings.size()-1; j++) {
|
||||
if (siblings.get(j).move.equals(n.move)) {
|
||||
nextSibling = siblings.get(j+1).index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
buf[6] = (byte)(nextSibling >> 8);
|
||||
buf[7] = (byte)(nextSibling & 255);
|
||||
out.write(buf);
|
||||
}
|
||||
for (int i = 0; i < buf.length; i++)
|
||||
buf[i] = -1;
|
||||
out.write(buf);
|
||||
|
||||
// Write names
|
||||
buf = new byte[]{0};
|
||||
for (String name : names) {
|
||||
out.write(name.getBytes("UTF-8"));
|
||||
out.write(buf);
|
||||
}
|
||||
|
||||
out.close();
|
||||
}
|
||||
}
|
11716
DroidFish/src/org/petero/droidfish/buildtools/eco.pgn
Normal file
11716
DroidFish/src/org/petero/droidfish/buildtools/eco.pgn
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -27,8 +27,8 @@ import java.net.Socket;
|
|||
import java.net.UnknownHostException;
|
||||
|
||||
import org.petero.droidfish.EngineOptions;
|
||||
import org.petero.droidfish.FileUtil;
|
||||
import org.petero.droidfish.R;
|
||||
import org.petero.droidfish.Util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -71,7 +71,7 @@ public class NetworkEngine extends UCIEngineBase {
|
|||
boolean ok = false;
|
||||
if (EngineUtil.isNetEngine(fileName)) {
|
||||
try {
|
||||
String[] lines = Util.readFile(fileName);
|
||||
String[] lines = FileUtil.readFile(fileName);
|
||||
if (lines.length >= 3) {
|
||||
host = lines[1];
|
||||
port = lines[2];
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.petero.droidfish.GameMode;
|
|||
import org.petero.droidfish.PGNOptions;
|
||||
import org.petero.droidfish.Util;
|
||||
import org.petero.droidfish.book.BookOptions;
|
||||
import org.petero.droidfish.book.EcoDb;
|
||||
import org.petero.droidfish.engine.DroidComputerPlayer;
|
||||
import org.petero.droidfish.engine.UCIOptions;
|
||||
import org.petero.droidfish.engine.DroidComputerPlayer.SearchRequest;
|
||||
|
@ -682,6 +683,7 @@ public class DroidChessController {
|
|||
private boolean whiteMove = true;
|
||||
private String bookInfo = "";
|
||||
private ArrayList<Move> bookMoves = null;
|
||||
private String eco = ""; // ECO classification
|
||||
|
||||
private Move ponderMove = null;
|
||||
private ArrayList<PvInfo> pvInfoV = new ArrayList<PvInfo>();
|
||||
|
@ -694,6 +696,7 @@ public class DroidChessController {
|
|||
currDepth = 0;
|
||||
bookInfo = "";
|
||||
bookMoves = null;
|
||||
eco = "";
|
||||
setSearchInfo(id);
|
||||
}
|
||||
|
||||
|
@ -766,7 +769,6 @@ public class DroidChessController {
|
|||
}
|
||||
final String statStr = statStrTmp.toString();
|
||||
final String newPV = buf.toString();
|
||||
final String newBookInfo = bookInfo;
|
||||
final ArrayList<ArrayList<Move>> pvMoves = new ArrayList<ArrayList<Move>>();
|
||||
for (int i = 0; i < pvInfoV.size(); i++) {
|
||||
if (ponderMove != null) {
|
||||
|
@ -783,7 +785,8 @@ public class DroidChessController {
|
|||
ti.id = id;
|
||||
ti.pvStr = newPV;
|
||||
ti.statStr = statStr;
|
||||
ti.bookInfo = newBookInfo;
|
||||
ti.bookInfo = bookInfo;
|
||||
ti.eco = eco;
|
||||
ti.pvMoves = pvMoves;
|
||||
ti.bookMoves = bookMoves;
|
||||
latestThinkingInfo = ti;
|
||||
|
@ -856,9 +859,11 @@ public class DroidChessController {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void notifyBookInfo(int id, String bookInfo, ArrayList<Move> moveList) {
|
||||
public void notifyBookInfo(int id, String bookInfo, ArrayList<Move> moveList,
|
||||
String eco) {
|
||||
this.bookInfo = bookInfo;
|
||||
bookMoves = moveList;
|
||||
this.eco = eco;
|
||||
setSearchInfo(id);
|
||||
}
|
||||
|
||||
|
@ -922,7 +927,8 @@ public class DroidChessController {
|
|||
private final void updateBookHints() {
|
||||
if (humansTurn()) {
|
||||
Pair<String, ArrayList<Move>> bi = computerPlayer.getBookHints(game.currPos(), localPt());
|
||||
listener.notifyBookInfo(searchId, bi.first, bi.second);
|
||||
String eco = EcoDb.getInstance(gui.getContext()).getEco(game.tree, game.tree.currentNode);
|
||||
listener.notifyBookInfo(searchId, bi.first, bi.second, eco);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -962,7 +968,7 @@ public class DroidChessController {
|
|||
computerPlayer.queueAnalyzeRequest(sr);
|
||||
} else if (computersTurn || ponder) {
|
||||
listener.clearSearchInfo(searchId);
|
||||
listener.notifyBookInfo(searchId, "", null);
|
||||
listener.notifyBookInfo(searchId, "", null, "");
|
||||
final Pair<Position, ArrayList<Move>> ph = game.getUCIHistory();
|
||||
Position currPos = new Position(game.currPos());
|
||||
long now = System.currentTimeMillis();
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.petero.droidfish.gamelogic.GameTree.Node;
|
|||
*/
|
||||
public class Game {
|
||||
boolean pendingDrawOffer;
|
||||
GameTree tree;
|
||||
public GameTree tree;
|
||||
TimeControl timeController;
|
||||
private boolean gamePaused;
|
||||
/** If true, add new moves as mainline moves. */
|
||||
|
@ -92,7 +92,7 @@ public class Game {
|
|||
updateTimeControl(false);
|
||||
}
|
||||
|
||||
final boolean readPGN(String pgn, PGNOptions options) throws ChessParseError {
|
||||
final public boolean readPGN(String pgn, PGNOptions options) throws ChessParseError {
|
||||
boolean ret = tree.readPGN(pgn, options);
|
||||
if (ret) {
|
||||
TimeControlData tcData = tree.getTimeControlData();
|
||||
|
|
|
@ -39,7 +39,7 @@ public class GameTree {
|
|||
String event, site, date, round, white, black;
|
||||
// Result is the last tag pair in the STR, but it is computed on demand from the game tree.
|
||||
|
||||
Position startPos;
|
||||
public Position startPos;
|
||||
private String timeControl, whiteTimeControl, blackTimeControl;
|
||||
|
||||
// Non-standard tags
|
||||
|
@ -49,8 +49,8 @@ public class GameTree {
|
|||
}
|
||||
private List<TagPair> tagPairs;
|
||||
|
||||
Node rootNode;
|
||||
Node currentNode;
|
||||
public Node rootNode;
|
||||
public Node currentNode;
|
||||
Position currentPos; // Cached value. Computable from "currentNode".
|
||||
|
||||
private final PgnToken.PgnTokenReceiver gameStateListener;
|
||||
|
@ -1001,7 +1001,7 @@ public class GameTree {
|
|||
public static class Node {
|
||||
String moveStr; // String representation of move leading to this node. Empty string in root node.
|
||||
String moveStrLocal; // Localized version of moveStr
|
||||
Move move; // Computed on demand for better PGN parsing performance.
|
||||
public Move move; // Computed on demand for better PGN parsing performance.
|
||||
// Subtrees of invalid moves will be dropped when detected.
|
||||
// Always valid for current node.
|
||||
private UndoInfo ui; // Computed when move is computed
|
||||
|
@ -1014,7 +1014,7 @@ public class GameTree {
|
|||
|
||||
private Node parent; // Null if root node
|
||||
int defaultChild;
|
||||
private List<Node> children;
|
||||
private ArrayList<Node> children;
|
||||
|
||||
public Node() {
|
||||
this.moveStr = "";
|
||||
|
@ -1073,7 +1073,7 @@ public class GameTree {
|
|||
}
|
||||
}
|
||||
if (anyToRemove) {
|
||||
List<Node> validChildren = new ArrayList<Node>();
|
||||
ArrayList<Node> validChildren = new ArrayList<Node>();
|
||||
for (Node child : children)
|
||||
if (child.move != null)
|
||||
validChildren.add(child);
|
||||
|
@ -1510,7 +1510,7 @@ public class GameTree {
|
|||
}
|
||||
|
||||
/** Get PGN header tags and values. */
|
||||
void getHeaders(Map<String,String> headers) {
|
||||
public void getHeaders(Map<String,String> headers) {
|
||||
headers.put("Event", event);
|
||||
headers.put("Site", site);
|
||||
headers.put("Date", date);
|
||||
|
|
|
@ -45,6 +45,11 @@ public class Move {
|
|||
this.promoteTo = m.promoteTo;
|
||||
}
|
||||
|
||||
/** Create object from compressed representation. */
|
||||
public static Move fromCompressed(int cm) {
|
||||
return new Move((cm >> 10) & 63, (cm >> 4) & 63, cm & 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ((o == null) || (o.getClass() != this.getClass()))
|
||||
|
@ -60,6 +65,11 @@ public class Move {
|
|||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getCompressedMove();
|
||||
}
|
||||
|
||||
/** Get move as a 16-bit value. */
|
||||
public int getCompressedMove() {
|
||||
return (from * 64 + to) * 16 + promoteTo;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ public interface SearchListener {
|
|||
public void notifyStats(int id, long nodes, int nps, long tbHits, int hash, int time);
|
||||
|
||||
/** Report opening book information. */
|
||||
public void notifyBookInfo(int id, String bookInfo, ArrayList<Move> moveList);
|
||||
public void notifyBookInfo(int id, String bookInfo, ArrayList<Move> moveList, String eco);
|
||||
|
||||
/** Report move (or command, such as "resign") played by the engine. */
|
||||
public void notifySearchResult(int id, String cmd, Move ponder);
|
||||
|
|
Loading…
Reference in New Issue
Block a user