Merge branch 'eco-code'.

Re-implemented functionality to be more CPU efficient.
This commit is contained in:
Peter Osterlund 2016-11-06 21:49:25 +01:00
commit ab601027b1
17 changed files with 12306 additions and 75 deletions

View 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:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/DroidFish/assets/eco.dat&quot; type=&quot;1&quot;/&gt;&#10;&lt;/resources&gt;}"/>
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/DroidFish/src/org/petero/droidfish&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
<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>

View File

@ -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>&lt;project&gt;/.externalToolBuilders/ECO_Builder.launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.andmore.ApkBuilder</name>
<arguments>

View File

@ -9,3 +9,4 @@
/stockfish-x86
/stockfish-x86_64
/stockfish-x86-nopie
/eco.dat

View File

@ -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)) {

View 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;
}
}
}

View File

@ -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. */

View File

@ -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;

View 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);
}
}
}

View File

@ -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();

View 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();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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];

View File

@ -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();

View File

@ -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();

View File

@ -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);

View File

@ -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;
}

View File

@ -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);