/*
DroidFish - An Android chess program.
Copyright (C) 2011 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 java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import org.petero.droidfish.book.DroidBook.BookEntry;
import org.petero.droidfish.gamelogic.Move;
import org.petero.droidfish.gamelogic.Piece;
import org.petero.droidfish.gamelogic.Position;
import org.petero.droidfish.gamelogic.TextIO;
import org.petero.droidfish.gamelogic.UndoInfo;
class CtgBook implements IOpeningBook {
private BookOptions options = new BookOptions();
private File ctgFile;
private File ctbFile;
private File ctoFile;
static boolean canHandle(BookOptions options) {
String filename = options.filename;
return (filename.endsWith(".ctg") ||
filename.endsWith(".ctb") ||
filename.endsWith(".cto"));
}
@Override
public boolean enabled() {
return ctgFile.canRead() &&
ctbFile.canRead() &&
ctoFile.canRead();
}
@Override
public void setOptions(BookOptions options) {
this.options = new BookOptions(options);
String fileName = options.filename;
int len = fileName.length();
ctgFile = new File(fileName.substring(0, len-1) + "g");
ctbFile = new File(fileName.substring(0, len-1) + "b");
ctoFile = new File(fileName.substring(0, len-1) + "o");
}
@Override
public List<BookEntry> getBookEntries(Position pos) {
RandomAccessFile ctgF = null;
RandomAccessFile ctbF = null;
RandomAccessFile ctoF = null;
try {
ctgF = new RandomAccessFile(ctgFile, "r");
ctbF = new RandomAccessFile(ctbFile, "r");
ctoF = new RandomAccessFile(ctoFile, "r");
CtbFile ctb = new CtbFile(ctbF);
CtoFile cto = new CtoFile(ctoF);
CtgFile ctg = new CtgFile(ctgF, ctb, cto);
List<BookEntry> ret = null;
PositionData pd = ctg.getPositionData(pos);
if (pd != null) {
boolean mirrorColor = pd.mirrorColor;
boolean mirrorLeftRight = pd.mirrorLeftRight;
ret = pd.getBookMoves();
UndoInfo ui = new UndoInfo();
for (BookEntry be : ret) {
pd.pos.makeMove(be.move, ui);
PositionData movePd = ctg.getPositionData(pd.pos);
pd.pos.unMakeMove(be.move, ui);
double weight = be.weight;
if (movePd == null) {
// System.out.printf("%s : no pos\n", TextIO.moveToUCIString(be.move));
weight = 0;
} else {
int recom = movePd.getRecommendation();
if ((recom >= 64) && (recom < 128)) {
if (options.tournamentMode)
weight = 0;
} else if (recom >= 128) {
if (options.preferMainLines)
weight *= 10;
}
int score = movePd.getOpponentScore();
// double w0 = weight;
weight = weight * score;
// System.out.printf("%s : w0:%.3f rec:%d score:%d %.3f\n", TextIO.moveToUCIString(be.move),
// w0, recom, score, weight);
}
be.weight = weight;
}
if (mirrorLeftRight) {
for (int i = 0; i < ret.size(); i++)
ret.get(i).move = mirrorMoveLeftRight(ret.get(i).move);
}
if (mirrorColor) {
for (int i = 0; i < ret.size(); i++)
ret.get(i).move = mirrorMoveColor(ret.get(i).move);
}
}
return ret;
} catch (IOException e) {
return null;
} finally {
if (ctgF != null) try { ctgF.close(); } catch (IOException e) { }
if (ctbF != null) try { ctbF.close(); } catch (IOException e) { }
if (ctoF != null) try { ctoF.close(); } catch (IOException e) { }
}
}
/** Read len bytes from offs in file f. */
private final static byte[] readBytes(RandomAccessFile f, int offs, int len) throws IOException {
byte[] ret = new byte[len];
f.seek(offs);
f.readFully(ret);
return ret;
}
/** Convert len bytes starting at offs in buf to an integer. */
private final static int extractInt(byte[] buf, int offs, int len) {
int ret = 0;
for (int i = 0; i < len; i++) {
int b = buf[offs + i];
if (b < 0) b += 256;
ret = (ret << 8) + b;
}
return ret;
}
private final static class CtbFile {
int lowerPageBound;
int upperPageBound;
CtbFile(RandomAccessFile f) throws IOException {
byte[] buf = readBytes(f, 4, 8);
lowerPageBound = extractInt(buf, 0, 4);
upperPageBound = extractInt(buf, 4, 4);
}
}
private final static class BitVector {
private List<Byte> buf = new ArrayList<Byte>();
private int length = 0;
void addBit(boolean value) {
int byteIdx = length / 8;
int bitIdx = 7 - (length & 7);
while (buf.size() <= byteIdx)
buf.add(Byte.valueOf((byte)0));
if (value)
buf.set(byteIdx, (byte)(buf.get(byteIdx) | (1 << bitIdx)));
length++;
}
void addBits(int mask, int numBits) {
for (int i = 0; i < numBits; i++) {
int b = numBits - 1 - i;
addBit((mask & (1 << b)) != 0);
}
}
/** Number of bits left in current byte. */
int padBits() {
int bitIdx = length & 7;
return (bitIdx == 0) ? 0 : 8 - bitIdx;
}
final byte[] toByteArray() {
byte[] ret = new byte[buf.size()];
for (int i = 0; i < buf.size(); i++)
ret[i] = buf.get(i);
return ret;
}
}
/** Converts a position to a byte array. */
private final static byte[] positionToByteArray(Position pos) {
BitVector bits = new BitVector();
bits.addBits(0, 8); // Header byte
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
int p = pos.getPiece(Position.getSquare(x, y));
switch (p) {
case Piece.EMPTY: bits.addBits(0x00, 1); break;
case Piece.WKING: bits.addBits(0x20, 6); break;
case Piece.WQUEEN: bits.addBits(0x22, 6); break;
case Piece.WROOK: bits.addBits(0x16, 5); break;
case Piece.WBISHOP: bits.addBits(0x14, 5); break;
case Piece.WKNIGHT: bits.addBits(0x12, 5); break;
case Piece.WPAWN: bits.addBits(0x06, 3); break;
case Piece.BKING: bits.addBits(0x21, 6); break;
case Piece.BQUEEN: bits.addBits(0x23, 6); break;
case Piece.BROOK: bits.addBits(0x17, 5); break;
case Piece.BBISHOP: bits.addBits(0x15, 5); break;
case Piece.BKNIGHT: bits.addBits(0x13, 5); break;
case Piece.BPAWN: bits.addBits(0x07, 3); break;
}
}
}
TextIO.fixupEPSquare(pos);
boolean ep = pos.getEpSquare() != -1;
boolean cs = pos.getCastleMask() != 0;
if (!ep && !cs)
bits.addBit(false); // At least one pad bit
int specialBits = (ep ? 3 : 0) + (cs ? 4 : 0);
while (bits.padBits() != specialBits)
bits.addBit(false);
if (ep)
bits.addBits(Position.getX(pos.getEpSquare()), 3);
if (cs) {
bits.addBit(pos.h8Castle());
bits.addBit(pos.a8Castle());
bits.addBit(pos.h1Castle());
bits.addBit(pos.a1Castle());
}
if ((bits.length & 7) != 0) throw new RuntimeException();
int header = bits.length / 8;
if (ep) header |= 0x20;
if (cs) header |= 0x40;
byte[] buf = bits.toByteArray();
buf[0] = (byte)header;
return buf;
}
private final static class CtoFile {
RandomAccessFile f;
CtoFile(RandomAccessFile f) {
this.f = f;
}
final static ArrayList<Integer> getHashIndices(byte[] encodedPos, CtbFile ctb) throws IOException {
ArrayList<Integer> ret = new ArrayList<Integer>();
int hash = getHashValue(encodedPos);
for (int n = 0; n < 0x7fffffff; n = 2*n + 1) {
int c = (hash & n) + n;
if (c < ctb.lowerPageBound)
continue;
ret.add(c);
if (c >= ctb.upperPageBound)
break;
}
return ret;
}
final int getPage(int hashIndex) throws IOException {
byte[] buf = readBytes(f, 16 + 4 * hashIndex, 4);
int page = extractInt(buf, 0, 4);
return page;
}
private final static int tbl[] = {
0x3100d2bf, 0x3118e3de, 0x34ab1372, 0x2807a847,
0x1633f566, 0x2143b359, 0x26d56488, 0x3b9e6f59,
0x37755656, 0x3089ca7b, 0x18e92d85, 0x0cd0e9d8,
0x1a9e3b54, 0x3eaa902f, 0x0d9bfaae, 0x2f32b45b,
0x31ed6102, 0x3d3c8398, 0x146660e3, 0x0f8d4b76,
0x02c77a5f, 0x146c8799, 0x1c47f51f, 0x249f8f36,
0x24772043, 0x1fbc1e4d, 0x1e86b3fa, 0x37df36a6,
0x16ed30e4, 0x02c3148e, 0x216e5929, 0x0636b34e,
0x317f9f56, 0x15f09d70, 0x131026fb, 0x38c784b1,
0x29ac3305, 0x2b485dc5, 0x3c049ddc, 0x35a9fbcd,
0x31d5373b, 0x2b246799, 0x0a2923d3, 0x08a96e9d,
0x30031a9f, 0x08f525b5, 0x33611c06, 0x2409db98,
0x0ca4feb2, 0x1000b71e, 0x30566e32, 0x39447d31,
0x194e3752, 0x08233a95, 0x0f38fe36, 0x29c7cd57,
0x0f7b3a39, 0x328e8a16, 0x1e7d1388, 0x0fba78f5,
0x274c7e7c, 0x1e8be65c, 0x2fa0b0bb, 0x1eb6c371
};
private final static int getHashValue(byte[] encodedPos) {
int hash = 0;
int tmp = 0;
for (int i = 0; i < encodedPos.length; i++) {
int ch = encodedPos[i];
tmp += ((0x0f - (ch & 0x0f)) << 2) + 1;
hash += tbl[tmp & 0x3f];
tmp += ((0xf0 - (ch & 0xf0)) >> 2) + 1;
hash += tbl[tmp & 0x3f];
}
return hash;
}
}
private final static class CtgFile {
private RandomAccessFile f;
private CtbFile ctb;
private CtoFile cto;
CtgFile(RandomAccessFile f, CtbFile ctb, CtoFile cto) {
this.f = f;
this.ctb = ctb;
this.cto = cto;
}
final PositionData getPositionData(Position pos) throws IOException {
boolean mirrorColor = !pos.whiteMove;
boolean needCopy = true;
if (mirrorColor) {
pos = mirrorPosColor(pos);
needCopy = false;
}
boolean mirrorLeftRight = false;
if ((pos.getCastleMask() == 0) && (Position.getX(pos.getKingSq(true)) < 4)) {
pos = mirrorPosLeftRight(pos);
mirrorLeftRight = true;
needCopy = false;
}
if (needCopy)
pos = new Position(pos);
byte[] encodedPos = positionToByteArray(pos);
ArrayList<Integer> hashIdxList = CtoFile.getHashIndices(encodedPos, ctb);
PositionData pd = null;
for (int i = 0; i < hashIdxList.size(); i++) {
int page = cto.getPage(hashIdxList.get(i));
if (page < 0)
continue;
pd = findInPage(page, encodedPos);
if (pd != null) {
pd.pos = pos;
pd.mirrorColor = mirrorColor;
pd.mirrorLeftRight = mirrorLeftRight;
break;
}
}
return pd;
}
private final PositionData findInPage(int page, byte[] encodedPos) throws IOException {
byte[] pageBuf = readBytes(f, (page+1)*4096, 4096);
try {
int nPos = extractInt(pageBuf, 0, 2);
int nBytes = extractInt(pageBuf, 2, 2);
for (int i = nBytes; i < 4096; i++)
pageBuf[i] = 0; // Don't depend on trailing garbage
int offs = 4;
for (int p = 0; p < nPos; p++) {
boolean match = true;
for (int i = 0; i < encodedPos.length; i++)
if (encodedPos[i] != pageBuf[offs+i]) {
match = false;
break;
}
if (match)
return new PositionData(pageBuf, offs);
int posLen = pageBuf[offs] & 0x1f;
offs += posLen;
int moveBytes = extractInt(pageBuf, offs, 1);
offs += moveBytes;
offs += PositionData.posInfoBytes;
}
return null;
} catch (ArrayIndexOutOfBoundsException ex) {
return null; // Ignore corrupt book file entries
}
}
}
private final static class PositionData {
private byte[] buf;
private int posLen;
private int moveBytes;
final static int posInfoBytes = 3*4 + 4 + (3+4)*2 + 1 + 1 + 1;
Position pos;
boolean mirrorColor = false;
boolean mirrorLeftRight = false;
PositionData(byte[] pageBuf, int offs) {
posLen = pageBuf[offs] & 0x1f;
moveBytes = extractInt(pageBuf, offs + posLen, 1);
int bufLen = posLen + moveBytes + posInfoBytes;
buf = new byte[bufLen];
for (int i = 0; i < bufLen; i++)
buf[i] = pageBuf[offs + i];
}
final ArrayList<BookEntry> getBookMoves() {
ArrayList<BookEntry> entries = new ArrayList<BookEntry>();
int nMoves = (moveBytes - 1) / 2;
for (int mi = 0; mi < nMoves; mi++) {
int move = extractInt(buf, posLen + 1 + mi * 2, 1);
int flags = extractInt(buf, posLen + 1 + mi * 2 + 1, 1);
Move m = decodeMove(pos, move);
if (m == null)
continue;
// System.out.printf("mi:%d m:%s flags:%d\n", mi, TextIO.moveToUCIString(m), flags);
BookEntry ent = new BookEntry(m);
switch (flags) {
default:
case 0x00: ent.weight = 1; break; // No annotation
case 0x01: ent.weight = 8; break; // !
case 0x02: ent.weight = 0; break; // ?
case 0x03: ent.weight = 32; break; // !!
case 0x04: ent.weight = 0; break; // ??
case 0x05: ent.weight = 0.5; break; // !?
case 0x06: ent.weight = 0.125; break; // ?!
case 0x08: ent.weight = 1000000; break; // Only move
}
entries.add(ent);
}
return entries;
}
/** Return (wins + draws/2) / games. */
final int getOpponentScore() {
int statStart = posLen + moveBytes;
// int wins = extractInt(buf, statStart + 3, 3);
int loss = extractInt(buf, statStart + 6, 3);
int draws = extractInt(buf, statStart + 9, 3);
return loss * 2 + draws;
}
final int getRecommendation() {
int statStart = posLen + moveBytes;
int recom = extractInt(buf, statStart + 30, 1);
return recom;
}
private static final class MoveInfo {
int piece;
int pieceNo;
int dx;
int dy;
}
private final static MoveInfo MI(int piece, int pieceNo, int dx, int dy) {
MoveInfo mi = new MoveInfo();
mi.piece = piece;
mi.pieceNo = pieceNo;
mi.dx = dx;
mi.dy = dy;
return mi;
}
private final static MoveInfo[] moveInfo = new MoveInfo[256];
static {
moveInfo[0x00] = MI(Piece.WPAWN , 4, +1, +1);
moveInfo[0x01] = MI(Piece.WKNIGHT, 1, -2, -1);
moveInfo[0x03] = MI(Piece.WQUEEN , 1, +2, +0);
moveInfo[0x04] = MI(Piece.WPAWN , 1, +0, +1);
moveInfo[0x05] = MI(Piece.WQUEEN , 0, +0, +1);
moveInfo[0x06] = MI(Piece.WPAWN , 3, -1, +1);
moveInfo[0x08] = MI(Piece.WQUEEN , 1, +4, +0);
moveInfo[0x09] = MI(Piece.WBISHOP, 1, +6, +6);
moveInfo[0x0a] = MI(Piece.WKING , 0, +0, -1);
moveInfo[0x0c] = MI(Piece.WPAWN , 0, -1, +1);
moveInfo[0x0d] = MI(Piece.WBISHOP, 0, +3, +3);
moveInfo[0x0e] = MI(Piece.WROOK , 1, +3, +0);
moveInfo[0x0f] = MI(Piece.WKNIGHT, 0, -2, -1);
moveInfo[0x12] = MI(Piece.WBISHOP, 0, +7, +7);
moveInfo[0x13] = MI(Piece.WKING , 0, +0, +1);
moveInfo[0x14] = MI(Piece.WPAWN , 7, +1, +1);
moveInfo[0x15] = MI(Piece.WBISHOP, 0, +5, +5);
moveInfo[0x18] = MI(Piece.WPAWN , 6, +0, +1);
moveInfo[0x1a] = MI(Piece.WQUEEN , 1, +0, +6);
moveInfo[0x1b] = MI(Piece.WBISHOP, 0, -1, +1);
moveInfo[0x1d] = MI(Piece.WBISHOP, 1, +7, +7);
moveInfo[0x21] = MI(Piece.WROOK , 1, +7, +0);
moveInfo[0x22] = MI(Piece.WBISHOP, 1, -2, +2);
moveInfo[0x23] = MI(Piece.WQUEEN , 1, +6, +6);
moveInfo[0x24] = MI(Piece.WPAWN , 7, -1, +1);
moveInfo[0x26] = MI(Piece.WBISHOP, 0, -7, +7);
moveInfo[0x27] = MI(Piece.WPAWN , 2, -1, +1);
moveInfo[0x28] = MI(Piece.WQUEEN , 0, +5, +5);
moveInfo[0x29] = MI(Piece.WQUEEN , 0, +6, +0);
moveInfo[0x2a] = MI(Piece.WKNIGHT, 1, +1, -2);
moveInfo[0x2d] = MI(Piece.WPAWN , 5, +1, +1);
moveInfo[0x2e] = MI(Piece.WBISHOP, 0, +1, +1);
moveInfo[0x2f] = MI(Piece.WQUEEN , 0, +1, +0);
moveInfo[0x30] = MI(Piece.WKNIGHT, 1, -1, -2);
moveInfo[0x31] = MI(Piece.WQUEEN , 0, +3, +0);
moveInfo[0x32] = MI(Piece.WBISHOP, 1, +5, +5);
moveInfo[0x34] = MI(Piece.WKNIGHT, 0, +1, +2);
moveInfo[0x36] = MI(Piece.WKNIGHT, 0, +2, +1);
moveInfo[0x37] = MI(Piece.WQUEEN , 0, +0, +4);
moveInfo[0x38] = MI(Piece.WQUEEN , 1, -4, +4);
moveInfo[0x39] = MI(Piece.WQUEEN , 0, +5, +0);
moveInfo[0x3a] = MI(Piece.WBISHOP, 0, +6, +6);
moveInfo[0x3b] = MI(Piece.WQUEEN , 1, -5, +5);
moveInfo[0x3c] = MI(Piece.WBISHOP, 0, -5, +5);
moveInfo[0x41] = MI(Piece.WQUEEN , 1, +5, +5);
moveInfo[0x42] = MI(Piece.WQUEEN , 0, -7, +7);
moveInfo[0x44] = MI(Piece.WKING , 0, +1, -1);
moveInfo[0x45] = MI(Piece.WQUEEN , 0, +3, +3);
moveInfo[0x4a] = MI(Piece.WPAWN , 7, +0, +2);
moveInfo[0x4b] = MI(Piece.WQUEEN , 0, -5, +5);
moveInfo[0x4c] = MI(Piece.WKNIGHT, 1, +1, +2);
moveInfo[0x4d] = MI(Piece.WQUEEN , 1, +0, +1);
moveInfo[0x50] = MI(Piece.WROOK , 0, +0, +6);
moveInfo[0x52] = MI(Piece.WROOK , 0, +6, +0);
moveInfo[0x54] = MI(Piece.WBISHOP, 1, -1, +1);
moveInfo[0x55] = MI(Piece.WPAWN , 2, +0, +1);
moveInfo[0x5c] = MI(Piece.WPAWN , 6, +1, +1);
moveInfo[0x5f] = MI(Piece.WPAWN , 4, +0, +2);
moveInfo[0x61] = MI(Piece.WQUEEN , 0, +6, +6);
moveInfo[0x62] = MI(Piece.WPAWN , 1, +0, +2);
moveInfo[0x63] = MI(Piece.WQUEEN , 1, -7, +7);
moveInfo[0x66] = MI(Piece.WBISHOP, 0, -3, +3);
moveInfo[0x67] = MI(Piece.WKING , 0, +1, +1);
moveInfo[0x69] = MI(Piece.WROOK , 1, +0, +7);
moveInfo[0x6a] = MI(Piece.WBISHOP, 0, +4, +4);
moveInfo[0x6b] = MI(Piece.WKING , 0, +2, +0);
moveInfo[0x6e] = MI(Piece.WROOK , 0, +5, +0);
moveInfo[0x6f] = MI(Piece.WQUEEN , 1, +7, +7);
moveInfo[0x72] = MI(Piece.WBISHOP, 1, -7, +7);
moveInfo[0x74] = MI(Piece.WQUEEN , 0, +2, +0);
moveInfo[0x79] = MI(Piece.WBISHOP, 1, -6, +6);
moveInfo[0x7a] = MI(Piece.WROOK , 0, +0, +3);
moveInfo[0x7b] = MI(Piece.WROOK , 1, +0, +6);
moveInfo[0x7c] = MI(Piece.WPAWN , 2, +1, +1);
moveInfo[0x7d] = MI(Piece.WROOK , 1, +0, +1);
moveInfo[0x7e] = MI(Piece.WQUEEN , 0, -3, +3);
moveInfo[0x7f] = MI(Piece.WROOK , 0, +1, +0);
moveInfo[0x80] = MI(Piece.WQUEEN , 0, -6, +6);
moveInfo[0x81] = MI(Piece.WROOK , 0, +0, +1);
moveInfo[0x82] = MI(Piece.WPAWN , 5, -1, +1);
moveInfo[0x85] = MI(Piece.WKNIGHT, 0, -1, +2);
moveInfo[0x86] = MI(Piece.WROOK , 0, +7, +0);
moveInfo[0x87] = MI(Piece.WROOK , 0, +0, +5);
moveInfo[0x8a] = MI(Piece.WKNIGHT, 0, +1, -2);
moveInfo[0x8b] = MI(Piece.WPAWN , 0, +1, +1);
moveInfo[0x8c] = MI(Piece.WKING , 0, -1, -1);
moveInfo[0x8e] = MI(Piece.WQUEEN , 1, -2, +2);
moveInfo[0x8f] = MI(Piece.WQUEEN , 0, +7, +0);
moveInfo[0x92] = MI(Piece.WQUEEN , 1, +1, +1);
moveInfo[0x94] = MI(Piece.WQUEEN , 0, +0, +3);
moveInfo[0x96] = MI(Piece.WPAWN , 1, +1, +1);
moveInfo[0x97] = MI(Piece.WKING , 0, -1, +0);
moveInfo[0x98] = MI(Piece.WROOK , 0, +3, +0);
moveInfo[0x99] = MI(Piece.WROOK , 0, +0, +4);
moveInfo[0x9a] = MI(Piece.WQUEEN , 0, +0, +6);
moveInfo[0x9b] = MI(Piece.WPAWN , 2, +0, +2);
moveInfo[0x9d] = MI(Piece.WQUEEN , 0, +0, +2);
moveInfo[0x9f] = MI(Piece.WBISHOP, 1, -4, +4);
moveInfo[0xa0] = MI(Piece.WQUEEN , 1, +0, +3);
moveInfo[0xa2] = MI(Piece.WQUEEN , 0, +2, +2);
moveInfo[0xa3] = MI(Piece.WPAWN , 7, +0, +1);
moveInfo[0xa5] = MI(Piece.WROOK , 1, +0, +5);
moveInfo[0xa9] = MI(Piece.WROOK , 1, +2, +0);
moveInfo[0xab] = MI(Piece.WQUEEN , 1, -6, +6);
moveInfo[0xad] = MI(Piece.WROOK , 1, +4, +0);
moveInfo[0xae] = MI(Piece.WQUEEN , 1, +3, +3);
moveInfo[0xb0] = MI(Piece.WQUEEN , 1, +0, +4);
moveInfo[0xb1] = MI(Piece.WPAWN , 5, +0, +2);
moveInfo[0xb2] = MI(Piece.WBISHOP, 0, -6, +6);
moveInfo[0xb5] = MI(Piece.WROOK , 1, +5, +0);
moveInfo[0xb7] = MI(Piece.WQUEEN , 0, +0, +5);
moveInfo[0xb9] = MI(Piece.WBISHOP, 1, +3, +3);
moveInfo[0xbb] = MI(Piece.WPAWN , 4, +0, +1);
moveInfo[0xbc] = MI(Piece.WQUEEN , 1, +5, +0);
moveInfo[0xbd] = MI(Piece.WQUEEN , 1, +0, +2);
moveInfo[0xbe] = MI(Piece.WKING , 0, +1, +0);
moveInfo[0xc1] = MI(Piece.WBISHOP, 0, +2, +2);
moveInfo[0xc2] = MI(Piece.WBISHOP, 1, +2, +2);
moveInfo[0xc3] = MI(Piece.WBISHOP, 0, -2, +2);
moveInfo[0xc4] = MI(Piece.WROOK , 1, +1, +0);
moveInfo[0xc5] = MI(Piece.WROOK , 1, +0, +4);
moveInfo[0xc6] = MI(Piece.WQUEEN , 1, +0, +5);
moveInfo[0xc7] = MI(Piece.WPAWN , 6, -1, +1);
moveInfo[0xc8] = MI(Piece.WPAWN , 6, +0, +2);
moveInfo[0xc9] = MI(Piece.WQUEEN , 1, +0, +7);
moveInfo[0xca] = MI(Piece.WBISHOP, 1, -3, +3);
moveInfo[0xcb] = MI(Piece.WPAWN , 5, +0, +1);
moveInfo[0xcc] = MI(Piece.WBISHOP, 1, -5, +5);
moveInfo[0xcd] = MI(Piece.WROOK , 0, +2, +0);
moveInfo[0xcf] = MI(Piece.WPAWN , 3, +0, +1);
moveInfo[0xd1] = MI(Piece.WPAWN , 1, -1, +1);
moveInfo[0xd2] = MI(Piece.WKNIGHT, 1, +2, +1);
moveInfo[0xd3] = MI(Piece.WKNIGHT, 1, -2, +1);
moveInfo[0xd7] = MI(Piece.WQUEEN , 0, -1, +1);
moveInfo[0xd8] = MI(Piece.WROOK , 1, +6, +0);
moveInfo[0xd9] = MI(Piece.WQUEEN , 0, -2, +2);
moveInfo[0xda] = MI(Piece.WKNIGHT, 0, -1, -2);
moveInfo[0xdb] = MI(Piece.WPAWN , 0, +0, +2);
moveInfo[0xde] = MI(Piece.WPAWN , 4, -1, +1);
moveInfo[0xdf] = MI(Piece.WKING , 0, -1, +1);
moveInfo[0xe0] = MI(Piece.WKNIGHT, 1, +2, -1);
moveInfo[0xe1] = MI(Piece.WROOK , 0, +0, +7);
moveInfo[0xe3] = MI(Piece.WROOK , 1, +0, +3);
moveInfo[0xe5] = MI(Piece.WQUEEN , 0, +4, +0);
moveInfo[0xe6] = MI(Piece.WPAWN , 3, +0, +2);
moveInfo[0xe7] = MI(Piece.WQUEEN , 0, +4, +4);
moveInfo[0xe8] = MI(Piece.WROOK , 0, +0, +2);
moveInfo[0xe9] = MI(Piece.WKNIGHT, 0, +2, -1);
moveInfo[0xeb] = MI(Piece.WPAWN , 3, +1, +1);
moveInfo[0xec] = MI(Piece.WPAWN , 0, +0, +1);
moveInfo[0xed] = MI(Piece.WQUEEN , 0, +7, +7);
moveInfo[0xee] = MI(Piece.WQUEEN , 1, -1, +1);
moveInfo[0xef] = MI(Piece.WROOK , 0, +4, +0);
moveInfo[0xf0] = MI(Piece.WQUEEN , 1, +7, +0);
moveInfo[0xf1] = MI(Piece.WQUEEN , 0, +1, +1);
moveInfo[0xf3] = MI(Piece.WKNIGHT, 1, -1, +2);
moveInfo[0xf4] = MI(Piece.WROOK , 1, +0, +2);
moveInfo[0xf5] = MI(Piece.WBISHOP, 1, +1, +1);
moveInfo[0xf6] = MI(Piece.WKING , 0, -2, +0);
moveInfo[0xf7] = MI(Piece.WKNIGHT, 0, -2, +1);
moveInfo[0xf8] = MI(Piece.WQUEEN , 1, +1, +0);
moveInfo[0xf9] = MI(Piece.WQUEEN , 1, +0, +6);
moveInfo[0xfa] = MI(Piece.WQUEEN , 1, +3, +0);
moveInfo[0xfb] = MI(Piece.WQUEEN , 1, +2, +2);
moveInfo[0xfd] = MI(Piece.WQUEEN , 0, +0, +7);
moveInfo[0xfe] = MI(Piece.WQUEEN , 1, -3, +3);
}
private final static int findPiece(Position pos, int piece, int pieceNo) {
for (int x = 0; x < 8; x++)
for (int y = 0; y < 8; y++) {
int sq = Position.getSquare(x, y);
if (pos.getPiece(sq) == piece)
if (pieceNo-- == 0)
return sq;
}
return -1;
}
private final Move decodeMove(Position pos, int moveCode) {
MoveInfo mi = moveInfo[moveCode];
if (mi == null)
return null;
int from = findPiece(pos, mi.piece, mi.pieceNo);
if (from < 0)
return null;
int toX = (Position.getX(from) + mi.dx) & 7;
int toY = (Position.getY(from) + mi.dy) & 7;
int to = Position.getSquare(toX, toY);
int promoteTo = Piece.EMPTY;
if ((pos.getPiece(from) == Piece.WPAWN) && (toY == 7))
promoteTo = Piece.WQUEEN;
Move m = new Move(from, to, promoteTo);
return m;
}
}
private final static int mirrorSquareColor(int sq) {
int x = Position.getX(sq);
int y = 7 - Position.getY(sq);
return Position.getSquare(x, y);
}
private final static int mirrorPieceColor(int piece) {
if (Piece.isWhite(piece)) {
piece = Piece.makeBlack(piece);
} else {
piece = Piece.makeWhite(piece);
}
return piece;
}
private final static Position mirrorPosColor(Position pos) {
Position ret = new Position(pos);
for (int sq = 0; sq < 64; sq++) {
int mSq = mirrorSquareColor(sq);
int piece = pos.getPiece(sq);
int mPiece = mirrorPieceColor(piece);
ret.setPiece(mSq, mPiece);
}
ret.setWhiteMove(!pos.whiteMove);
int castleMask = 0;
if (pos.a1Castle()) castleMask |= (1 << Position.A8_CASTLE);
if (pos.h1Castle()) castleMask |= (1 << Position.H8_CASTLE);
if (pos.a8Castle()) castleMask |= (1 << Position.A1_CASTLE);
if (pos.h8Castle()) castleMask |= (1 << Position.H1_CASTLE);
ret.setCastleMask(castleMask);
int epSquare = pos.getEpSquare();
if (epSquare >= 0) {
int mEpSquare = mirrorSquareColor(epSquare);
ret.setEpSquare(mEpSquare);
}
ret.halfMoveClock = pos.halfMoveClock;
ret.fullMoveCounter = pos.fullMoveCounter;
return ret;
}
private final static Move mirrorMoveColor(Move m) {
if (m == null) return null;
Move ret = new Move(m);
ret.from = mirrorSquareColor(m.from);
ret.to = mirrorSquareColor(m.to);
ret.promoteTo = mirrorPieceColor(m.promoteTo);
return ret;
}
private final static int mirrorSquareLeftRight(int sq) {
int x = 7 - Position.getX(sq);
int y = Position.getY(sq);
return Position.getSquare(x, y);
}
private final static Position mirrorPosLeftRight(Position pos) {
Position ret = new Position(pos);
for (int sq = 0; sq < 64; sq++) {
int mSq = mirrorSquareLeftRight(sq);
int piece = pos.getPiece(sq);
ret.setPiece(mSq, piece);
}
int epSquare = pos.getEpSquare();
if (epSquare >= 0) {
int mEpSquare = mirrorSquareLeftRight(epSquare);
ret.setEpSquare(mEpSquare);
}
ret.halfMoveClock = pos.halfMoveClock;
ret.fullMoveCounter = pos.fullMoveCounter;
return ret;
}
private final static Move mirrorMoveLeftRight(Move m) {
if (m == null) return null;
Move ret = new Move(m);
ret.from = mirrorSquareLeftRight(m.from);
ret.to = mirrorSquareLeftRight(m.to);
ret.promoteTo = m.promoteTo;
return ret;
}
}