mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2024-11-23 11:31:33 +01:00
DroidFish: Changed representation of the ECO database.
This makes it around 10% smaller.
This commit is contained in:
parent
4408669833
commit
7dd20129ee
|
@ -48,9 +48,32 @@ public class EcoDb {
|
|||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
public final String eco; // The ECO code
|
||||
public final String opn; // The opening name, or null
|
||||
public final String var; // The variation name, or null
|
||||
public final int distToEcoTree;
|
||||
Result(String eco, String opn, String var, int d) {
|
||||
this.eco = eco;
|
||||
this.opn = opn;
|
||||
this.var = var;
|
||||
distToEcoTree = d;
|
||||
}
|
||||
/** Return string formatted as "eco: opn, var". */
|
||||
public String getName() {
|
||||
String s = eco;
|
||||
if (!opn.isEmpty()) {
|
||||
s = s + ": " + opn;
|
||||
if (!var.isEmpty())
|
||||
s = s + ", " + var;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get ECO classification for a given tree node. Also returns distance in plies to "ECO tree". */
|
||||
public Pair<String,Integer> getEco(GameTree gt) {
|
||||
public Result getEco(GameTree gt) {
|
||||
ArrayList<Integer> treePath = new ArrayList<Integer>(); // Path to restore gt to original node
|
||||
ArrayList<Pair<GameTree.Node,Boolean>> toCache = new ArrayList<Pair<GameTree.Node,Boolean>>();
|
||||
|
||||
|
@ -74,7 +97,7 @@ public class EcoDb {
|
|||
|
||||
if (idx != null) {
|
||||
Node ecoNode = readNode(idx);
|
||||
if (ecoNode.nameIdx != -1) {
|
||||
if (ecoNode.ecoIdx != -1) {
|
||||
nodeIdx = idx;
|
||||
break;
|
||||
}
|
||||
|
@ -142,10 +165,18 @@ public class EcoDb {
|
|||
|
||||
if (nodeIdx != -1) {
|
||||
Node n = readNode(nodeIdx);
|
||||
if (n.nameIdx >= 0)
|
||||
return new Pair<String, Integer>(ecoNames[n.nameIdx], distToEcoTree);
|
||||
String eco = "", opn = "", var = "";
|
||||
if (n.ecoIdx >= 0) {
|
||||
eco = strPool[n.ecoIdx];
|
||||
if (n.opnIdx >= 0) {
|
||||
opn = strPool[n.opnIdx];
|
||||
if (n.varIdx >= 0)
|
||||
var = strPool[n.varIdx];
|
||||
}
|
||||
return new Result(eco, opn, var, distToEcoTree);
|
||||
}
|
||||
}
|
||||
return new Pair<String, Integer>("", 0);
|
||||
return new Result("", "", "", 0);
|
||||
}
|
||||
|
||||
/** Get all moves in the ECO tree from a given position. */
|
||||
|
@ -182,13 +213,15 @@ public class EcoDb {
|
|||
|
||||
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 ecoIdx; // Index in string array, or -1
|
||||
int opnIdx; // Index in string array, or -1
|
||||
int varIdx; // Index in string array, or -1
|
||||
int firstChild;
|
||||
int nextSibling;
|
||||
}
|
||||
|
||||
private byte[] nodesBuffer;
|
||||
private String[] ecoNames;
|
||||
private String[] strPool;
|
||||
private HashMap<Long, Short> posHashToNodeIdx;
|
||||
private HashMap<Long, ArrayList<Short>> posHashToNodeIdx2; // Handles collisions
|
||||
private final long startPosHash; // Zobrist hash for standard starting position
|
||||
|
@ -239,11 +272,11 @@ public class EcoDb {
|
|||
break;
|
||||
nNodes++;
|
||||
}
|
||||
nodesBuffer = new byte[nNodes * 8];
|
||||
System.arraycopy(buf, 0, nodesBuffer, 0, nNodes * 8);
|
||||
nodesBuffer = new byte[nNodes * 12];
|
||||
System.arraycopy(buf, 0, nodesBuffer, 0, nNodes * 12);
|
||||
|
||||
ArrayList<String> names = new ArrayList<String>();
|
||||
int idx = (nNodes + 1) * 8;
|
||||
int idx = (nNodes + 1) * 12;
|
||||
int start = idx;
|
||||
for (int i = idx; i < buf.length; i++) {
|
||||
if (buf[i] == 0) {
|
||||
|
@ -251,7 +284,7 @@ public class EcoDb {
|
|||
start = i + 1;
|
||||
}
|
||||
}
|
||||
ecoNames = names.toArray(new String[names.size()]);
|
||||
strPool = names.toArray(new String[names.size()]);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("Can't read ECO database");
|
||||
}
|
||||
|
@ -272,7 +305,7 @@ public class EcoDb {
|
|||
long hash = pos.zobristHash();
|
||||
if (posHashToNodeIdx.get(hash) == null) {
|
||||
posHashToNodeIdx.put(hash, (short)nodeIdx);
|
||||
} else if (node.nameIdx != -1) {
|
||||
} else if (node.ecoIdx != -1) {
|
||||
ArrayList<Short> lst = null;
|
||||
if (posHashToNodeIdx2.get(hash) == null) {
|
||||
lst = new ArrayList<Short>();
|
||||
|
@ -300,11 +333,13 @@ public class EcoDb {
|
|||
|
||||
private static Node readNode(int index, byte[] buf) {
|
||||
Node n = new Node();
|
||||
int o = index * 8;
|
||||
int o = index * 12;
|
||||
n.move = getU16(buf, o);
|
||||
n.nameIdx = getS16(buf, o + 2);
|
||||
n.firstChild = getS16(buf, o + 4);
|
||||
n.nextSibling = getS16(buf, o + 6);
|
||||
n.ecoIdx = getS16(buf, o + 2);
|
||||
n.opnIdx = getS16(buf, o + 4);
|
||||
n.varIdx = getS16(buf, o + 6);
|
||||
n.firstChild = getS16(buf, o + 8);
|
||||
n.nextSibling = getS16(buf, o + 10);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,23 +41,28 @@ public class EcoBuilder {
|
|||
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
|
||||
int ecoIdx; // Index in string array, or -1
|
||||
int opnIdx; // Index in string array, or -1
|
||||
int varIdx; // Index in string array, or -1
|
||||
ArrayList<Node> children = new ArrayList<Node>();
|
||||
Node parent;
|
||||
}
|
||||
private ArrayList<Node> nodes;
|
||||
private ArrayList<String> names;
|
||||
private HashMap<String, Integer> nameToIndex;
|
||||
private ArrayList<String> strs;
|
||||
private HashMap<String, Integer> strToIndex;
|
||||
|
||||
|
||||
/** Constructor. */
|
||||
private EcoBuilder() {
|
||||
nodes = new ArrayList<Node>();
|
||||
names = new ArrayList<String>();
|
||||
nameToIndex = new HashMap<String, Integer>();
|
||||
strs = new ArrayList<String>();
|
||||
strToIndex = new HashMap<String, Integer>();
|
||||
Node rootNode = new Node();
|
||||
rootNode.index = 0;
|
||||
rootNode.move = new Move(0, 0, 0);
|
||||
rootNode.nameIdx = -1;
|
||||
rootNode.ecoIdx = -1;
|
||||
rootNode.opnIdx = -1;
|
||||
rootNode.varIdx = -1;
|
||||
nodes.add(rootNode);
|
||||
}
|
||||
|
||||
|
@ -94,27 +99,18 @@ public class EcoBuilder {
|
|||
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 ecoIdx = addData(headers, "ECO");
|
||||
int opnIdx = addData(headers, "Opening");
|
||||
int varIdx = addData(headers, "Variation");
|
||||
|
||||
// Add corresponding moves to data structures
|
||||
Node parent = nodes.get(0);
|
||||
while (true) {
|
||||
ArrayList<Move> moves = tree.variations();
|
||||
if (moves.isEmpty()) {
|
||||
parent.nameIdx = nameIdx;
|
||||
parent.ecoIdx = ecoIdx;
|
||||
parent.opnIdx = opnIdx;
|
||||
parent.varIdx = varIdx;
|
||||
break;
|
||||
}
|
||||
Move m = moves.get(0);
|
||||
|
@ -130,7 +126,9 @@ public class EcoBuilder {
|
|||
Node node = new Node();
|
||||
node.index = nodes.size();
|
||||
node.move = m;
|
||||
node.nameIdx = -1;
|
||||
node.ecoIdx = -1;
|
||||
node.opnIdx = -1;
|
||||
node.varIdx = -1;
|
||||
node.parent = parent;
|
||||
nodes.add(node);
|
||||
parent.children.add(node);
|
||||
|
@ -141,24 +139,42 @@ public class EcoBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/** Add ECO, opening or variation data to string pool. */
|
||||
private int addData(HashMap<String, String> headers, String hdrName) {
|
||||
String s = headers.get(hdrName);
|
||||
if (s == null)
|
||||
return -1;
|
||||
Integer idx = strToIndex.get(s);
|
||||
if (idx == null) {
|
||||
idx = strToIndex.size();
|
||||
strToIndex.put(s, idx);
|
||||
strs.add(s);
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
/** 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];
|
||||
byte[] buf = new byte[12];
|
||||
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
|
||||
buf[2] = (byte)(n.ecoIdx >> 8); // Index, high byte
|
||||
buf[3] = (byte)(n.ecoIdx & 255); // Index, low byte
|
||||
buf[4] = (byte)(n.opnIdx >> 8); // Index, high byte
|
||||
buf[5] = (byte)(n.opnIdx & 255); // Index, low byte
|
||||
buf[6] = (byte)(n.varIdx >> 8); // Index, high byte
|
||||
buf[7] = (byte)(n.varIdx & 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);
|
||||
buf[8] = (byte)(firstChild >> 8);
|
||||
buf[9] = (byte)(firstChild & 255);
|
||||
int nextSibling = -1;
|
||||
if (n.parent != null) {
|
||||
ArrayList<Node> siblings = n.parent.children;
|
||||
|
@ -169,17 +185,17 @@ public class EcoBuilder {
|
|||
}
|
||||
}
|
||||
}
|
||||
buf[6] = (byte)(nextSibling >> 8);
|
||||
buf[7] = (byte)(nextSibling & 255);
|
||||
buf[10] = (byte)(nextSibling >> 8);
|
||||
buf[11] = (byte)(nextSibling & 255);
|
||||
out.write(buf);
|
||||
}
|
||||
for (int i = 0; i < buf.length; i++)
|
||||
buf[i] = -1;
|
||||
out.write(buf);
|
||||
|
||||
// Write names
|
||||
// Write strings
|
||||
buf = new byte[]{0};
|
||||
for (String name : names) {
|
||||
for (String name : strs) {
|
||||
out.write(name.getBytes("UTF-8"));
|
||||
out.write(buf);
|
||||
}
|
||||
|
|
|
@ -933,10 +933,9 @@ public class DroidChessController {
|
|||
private final void updateBookHints() {
|
||||
if (game != null) {
|
||||
Pair<String, ArrayList<Move>> bi = computerPlayer.getBookHints(game.currPos(), localPt());
|
||||
Pair<String, Integer> ecoData =
|
||||
EcoDb.getInstance().getEco(game.tree);
|
||||
String eco = ecoData.first;
|
||||
listener.notifyBookInfo(searchId, bi.first, bi.second, eco, ecoData.second);
|
||||
EcoDb.Result ecoData = EcoDb.getInstance().getEco(game.tree);
|
||||
String eco = ecoData.getName();
|
||||
listener.notifyBookInfo(searchId, bi.first, bi.second, eco, ecoData.distToEcoTree);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -976,10 +975,9 @@ public class DroidChessController {
|
|||
computerPlayer.queueAnalyzeRequest(sr);
|
||||
} else if (computersTurn || ponder) {
|
||||
listener.clearSearchInfo(searchId);
|
||||
Pair<String, Integer> ecoData =
|
||||
EcoDb.getInstance().getEco(game.tree);
|
||||
String eco = ecoData.first;
|
||||
listener.notifyBookInfo(searchId, "", null, eco, ecoData.second);
|
||||
EcoDb.Result ecoData = EcoDb.getInstance().getEco(game.tree);
|
||||
String eco = ecoData.getName();
|
||||
listener.notifyBookInfo(searchId, "", null, eco, ecoData.distToEcoTree);
|
||||
final Pair<Position, ArrayList<Move>> ph = game.getUCIHistory();
|
||||
Position currPos = new Position(game.currPos());
|
||||
long now = System.currentTimeMillis();
|
||||
|
|
|
@ -36,27 +36,27 @@ public class EcoTest extends AndroidTestCase {
|
|||
{
|
||||
String pgn = "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1";
|
||||
GameTree gt = readPGN(pgn);
|
||||
String eco = ecoDb.getEco(gt).first;
|
||||
String eco = ecoDb.getEco(gt).getName();
|
||||
assertEquals("", eco);
|
||||
|
||||
gt.goForward(0);
|
||||
eco = ecoDb.getEco(gt).first;
|
||||
eco = ecoDb.getEco(gt).getName();
|
||||
assertEquals("B00: King's pawn opening", eco);
|
||||
|
||||
gt.goForward(0);
|
||||
eco = ecoDb.getEco(gt).first;
|
||||
eco = ecoDb.getEco(gt).getName();
|
||||
assertEquals("C20: King's pawn game", eco);
|
||||
|
||||
gt.goForward(0);
|
||||
eco = ecoDb.getEco(gt).first;
|
||||
eco = ecoDb.getEco(gt).getName();
|
||||
assertEquals("C40: King's knight opening", eco);
|
||||
|
||||
gt.goForward(0);
|
||||
eco = ecoDb.getEco(gt).first;
|
||||
eco = ecoDb.getEco(gt).getName();
|
||||
assertEquals("C44: King's pawn game", eco);
|
||||
|
||||
gt.goForward(0);
|
||||
eco = ecoDb.getEco(gt).first;
|
||||
eco = ecoDb.getEco(gt).getName();
|
||||
assertEquals("C60: Ruy Lopez (Spanish opening)", eco);
|
||||
}
|
||||
{
|
||||
|
@ -65,85 +65,85 @@ public class EcoTest extends AndroidTestCase {
|
|||
game.processString("e5");
|
||||
game.processString("Nf3");
|
||||
game.processString("Nf6");
|
||||
String eco = ecoDb.getEco(game.tree).first;
|
||||
String eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("C42: Petrov's defence", eco);
|
||||
|
||||
game.processString("Nxe5");
|
||||
eco = ecoDb.getEco(game.tree).first;
|
||||
eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("C42: Petrov's defence", eco);
|
||||
|
||||
game.processString("d6");
|
||||
eco = ecoDb.getEco(game.tree).first;
|
||||
eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("C42: Petrov's defence", eco);
|
||||
|
||||
game.processString("Nxf7");
|
||||
eco = ecoDb.getEco(game.tree).first;
|
||||
eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("C42: Petrov, Cochrane gambit", eco);
|
||||
|
||||
game.undoMove();
|
||||
eco = ecoDb.getEco(game.tree).first;
|
||||
eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("C42: Petrov's defence", eco);
|
||||
|
||||
game.processString("Nf3");
|
||||
game.processString("Nxe4");
|
||||
game.processString("d4");
|
||||
eco = ecoDb.getEco(game.tree).first;
|
||||
eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("C42: Petrov, classical attack", eco);
|
||||
}
|
||||
{
|
||||
Game game = new Game(null, new TimeControlData());
|
||||
game.processString("e4");
|
||||
game.processString("c5");
|
||||
String eco = ecoDb.getEco(game.tree).first;
|
||||
String eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("B20: Sicilian defence", eco);
|
||||
|
||||
game.processString("h3");
|
||||
eco = ecoDb.getEco(game.tree).first;
|
||||
eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("B20: Sicilian defence", eco);
|
||||
|
||||
game.processString("Nc6");
|
||||
eco = ecoDb.getEco(game.tree).first;
|
||||
eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("B20: Sicilian defence", eco);
|
||||
|
||||
game.processString("g3");
|
||||
eco = ecoDb.getEco(game.tree).first;
|
||||
eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("B20: Sicilian defence", eco);
|
||||
}
|
||||
{
|
||||
Game game = new Game(null, new TimeControlData());
|
||||
for (String m : new String[]{"d4", "d5", "c4", "c6", "Nf3", "Nf6", "Nc3", "g6"})
|
||||
game.processString(m);
|
||||
String eco = ecoDb.getEco(game.tree).first;
|
||||
String eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("D15: QGD Slav, Schlechter variation", eco);
|
||||
assertEquals(0, ecoDb.getEco(game.tree).second.intValue());
|
||||
assertEquals(0, ecoDb.getEco(game.tree).distToEcoTree);
|
||||
game.processString("a4");
|
||||
assertEquals("D15: QGD Slav, Schlechter variation", eco);
|
||||
assertEquals(1, ecoDb.getEco(game.tree).second.intValue());
|
||||
assertEquals(1, ecoDb.getEco(game.tree).distToEcoTree);
|
||||
}
|
||||
{
|
||||
Game game = new Game(null, new TimeControlData());
|
||||
for (String m : new String[]{"d4", "Nf6", "c4", "g6", "Nc3", "d5", "Nf3", "c6"})
|
||||
game.processString(m);
|
||||
String eco = ecoDb.getEco(game.tree).first;
|
||||
String eco = ecoDb.getEco(game.tree).getName();
|
||||
assertEquals("D90: Gruenfeld, Schlechter variation", eco);
|
||||
assertEquals(0, ecoDb.getEco(game.tree).second.intValue());
|
||||
assertEquals(0, ecoDb.getEco(game.tree).distToEcoTree);
|
||||
game.processString("h4");
|
||||
assertEquals("D90: Gruenfeld, Schlechter variation", eco);
|
||||
assertEquals(1, ecoDb.getEco(game.tree).second.intValue());
|
||||
assertEquals(1, ecoDb.getEco(game.tree).distToEcoTree);
|
||||
game.processString("h5");
|
||||
assertEquals("D90: Gruenfeld, Schlechter variation", eco);
|
||||
assertEquals(2, ecoDb.getEco(game.tree).second.intValue());
|
||||
assertEquals(2, ecoDb.getEco(game.tree).distToEcoTree);
|
||||
}
|
||||
}
|
||||
|
||||
public void testEcoFromFEN() throws Throwable {
|
||||
EcoDb ecoDb = EcoDb.getInstance();
|
||||
GameTree gt = gtFromFEN("rnbqkbnr/ppp2ppp/4p3/3P4/3P4/8/PPP2PPP/RNBQKBNR b KQkq - 0 3");
|
||||
String eco = ecoDb.getEco(gt).first;
|
||||
String eco = ecoDb.getEco(gt).getName();
|
||||
assertEquals("C01: French, exchange variation", eco);
|
||||
|
||||
gt = gtFromFEN("rnbqk1nr/ppppppbp/6p1/8/3PP3/8/PPP2PPP/RNBQKBNR w KQkq - 1 3");
|
||||
eco = ecoDb.getEco(gt).first;
|
||||
eco = ecoDb.getEco(gt).getName();
|
||||
assertEquals("B06: Robatsch (modern) defence", eco);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user