mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-08 15:12:40 +01:00
Remove unneeded code from buildSrc
This commit is contained in:
@@ -20,99 +20,13 @@ package chess;
|
|||||||
|
|
||||||
public class BitBoard {
|
public class BitBoard {
|
||||||
|
|
||||||
/** Squares attacked by a king on a given square. */
|
|
||||||
public static final long[] kingAttacks;
|
|
||||||
public static final long[] knightAttacks;
|
|
||||||
public static final long[] wPawnAttacks, bPawnAttacks;
|
|
||||||
|
|
||||||
// Squares preventing a pawn from being a passed pawn, if occupied by enemy pawn
|
|
||||||
static final long[] wPawnBlockerMask, bPawnBlockerMask;
|
|
||||||
|
|
||||||
public static final long maskAToGFiles = 0x7F7F7F7F7F7F7F7FL;
|
public static final long maskAToGFiles = 0x7F7F7F7F7F7F7F7FL;
|
||||||
public static final long maskBToHFiles = 0xFEFEFEFEFEFEFEFEL;
|
public static final long maskBToHFiles = 0xFEFEFEFEFEFEFEFEL;
|
||||||
public static final long maskAToFFiles = 0x3F3F3F3F3F3F3F3FL;
|
public static final long maskAToFFiles = 0x3F3F3F3F3F3F3F3FL;
|
||||||
public static final long maskCToHFiles = 0xFCFCFCFCFCFCFCFCL;
|
public static final long maskCToHFiles = 0xFCFCFCFCFCFCFCFCL;
|
||||||
|
|
||||||
public static final long[] maskFile = {
|
|
||||||
0x0101010101010101L,
|
|
||||||
0x0202020202020202L,
|
|
||||||
0x0404040404040404L,
|
|
||||||
0x0808080808080808L,
|
|
||||||
0x1010101010101010L,
|
|
||||||
0x2020202020202020L,
|
|
||||||
0x4040404040404040L,
|
|
||||||
0x8080808080808080L
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final long maskRow1 = 0x00000000000000FFL;
|
|
||||||
public static final long maskRow2 = 0x000000000000FF00L;
|
|
||||||
public static final long maskRow3 = 0x0000000000FF0000L;
|
|
||||||
public static final long maskRow4 = 0x00000000FF000000L;
|
|
||||||
public static final long maskRow5 = 0x000000FF00000000L;
|
|
||||||
public static final long maskRow6 = 0x0000FF0000000000L;
|
|
||||||
public static final long maskRow7 = 0x00FF000000000000L;
|
|
||||||
public static final long maskRow8 = 0xFF00000000000000L;
|
|
||||||
public static final long maskRow1Row8 = 0xFF000000000000FFL;
|
|
||||||
|
|
||||||
public static final long maskDarkSq = 0xAA55AA55AA55AA55L;
|
|
||||||
public static final long maskLightSq = 0x55AA55AA55AA55AAL;
|
|
||||||
|
|
||||||
public static final long maskCorners = 0x8100000000000081L;
|
public static final long maskCorners = 0x8100000000000081L;
|
||||||
|
|
||||||
static {
|
|
||||||
// Compute king attacks
|
|
||||||
kingAttacks = new long[64];
|
|
||||||
|
|
||||||
for (int sq = 0; sq < 64; sq++) {
|
|
||||||
long m = 1L << sq;
|
|
||||||
long mask = (((m >>> 1) | (m << 7) | (m >>> 9)) & maskAToGFiles) |
|
|
||||||
(((m << 1) | (m << 9) | (m >>> 7)) & maskBToHFiles) |
|
|
||||||
(m << 8) | (m >>> 8);
|
|
||||||
kingAttacks[sq] = mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute knight attacks
|
|
||||||
knightAttacks = new long[64];
|
|
||||||
for (int sq = 0; sq < 64; sq++) {
|
|
||||||
long m = 1L << sq;
|
|
||||||
long mask = (((m << 6) | (m >>> 10)) & maskAToFFiles) |
|
|
||||||
(((m << 15) | (m >>> 17)) & maskAToGFiles) |
|
|
||||||
(((m << 17) | (m >>> 15)) & maskBToHFiles) |
|
|
||||||
(((m << 10) | (m >>> 6)) & maskCToHFiles);
|
|
||||||
knightAttacks[sq] = mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute pawn attacks
|
|
||||||
wPawnAttacks = new long[64];
|
|
||||||
bPawnAttacks = new long[64];
|
|
||||||
wPawnBlockerMask = new long[64];
|
|
||||||
bPawnBlockerMask = new long[64];
|
|
||||||
for (int sq = 0; sq < 64; sq++) {
|
|
||||||
long m = 1L << sq;
|
|
||||||
long mask = ((m << 7) & maskAToGFiles) | ((m << 9) & maskBToHFiles);
|
|
||||||
wPawnAttacks[sq] = mask;
|
|
||||||
mask = ((m >>> 9) & maskAToGFiles) | ((m >>> 7) & maskBToHFiles);
|
|
||||||
bPawnAttacks[sq] = mask;
|
|
||||||
|
|
||||||
int x = Position.getX(sq);
|
|
||||||
int y = Position.getY(sq);
|
|
||||||
m = 0;
|
|
||||||
for (int y2 = y+1; y2 < 8; y2++) {
|
|
||||||
if (x > 0) m |= 1L << Position.getSquare(x-1, y2);
|
|
||||||
m |= 1L << Position.getSquare(x , y2);
|
|
||||||
if (x < 7) m |= 1L << Position.getSquare(x+1, y2);
|
|
||||||
}
|
|
||||||
wPawnBlockerMask[sq] = m;
|
|
||||||
m = 0;
|
|
||||||
for (int y2 = y-1; y2 >= 0; y2--) {
|
|
||||||
if (x > 0) m |= 1L << Position.getSquare(x-1, y2);
|
|
||||||
m |= 1L << Position.getSquare(x , y2);
|
|
||||||
if (x < 7) m |= 1L << Position.getSquare(x+1, y2);
|
|
||||||
}
|
|
||||||
bPawnBlockerMask[sq] = m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final static long[][] rTables;
|
private final static long[][] rTables;
|
||||||
private final static long[] rMasks;
|
private final static long[] rMasks;
|
||||||
private final static int[] rBits = { 12, 11, 11, 11, 11, 11, 11, 12,
|
private final static int[] rBits = { 12, 11, 11, 11, 11, 11, 11, 12,
|
||||||
@@ -277,106 +191,4 @@ public class BitBoard {
|
|||||||
public static long rookAttacks(int sq, long occupied) {
|
public static long rookAttacks(int sq, long occupied) {
|
||||||
return rTables[sq][(int)(((occupied & rMasks[sq]) * rMagics[sq]) >>> (64 - rBits[sq]))];
|
return rTables[sq][(int)(((occupied & rMasks[sq]) * rMagics[sq]) >>> (64 - rBits[sq]))];
|
||||||
}
|
}
|
||||||
|
|
||||||
static public final long[][] squaresBetween;
|
|
||||||
static {
|
|
||||||
squaresBetween = new long[64][];
|
|
||||||
for (int sq1 = 0; sq1 < 64; sq1++) {
|
|
||||||
squaresBetween[sq1] = new long[64];
|
|
||||||
for (int j = 0; j < 64; j++)
|
|
||||||
squaresBetween[sq1][j] = 0;
|
|
||||||
for (int dx = -1; dx <= 1; dx++) {
|
|
||||||
for (int dy = -1; dy <= 1; dy++) {
|
|
||||||
if ((dx == 0) && (dy == 0))
|
|
||||||
continue;
|
|
||||||
long m = 0;
|
|
||||||
int x = Position.getX(sq1);
|
|
||||||
int y = Position.getY(sq1);
|
|
||||||
while (true) {
|
|
||||||
x += dx; y += dy;
|
|
||||||
if ((x < 0) || (x > 7) || (y < 0) || (y > 7))
|
|
||||||
break;
|
|
||||||
int sq2 = Position.getSquare(x, y);
|
|
||||||
squaresBetween[sq1][sq2] = m;
|
|
||||||
m |= 1L << sq2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final byte dirTable[] = {
|
|
||||||
-9, 0, 0, 0, 0, 0, 0, -8, 0, 0, 0, 0, 0, 0, -7,
|
|
||||||
0, 0, -9, 0, 0, 0, 0, 0, -8, 0, 0, 0, 0, 0, -7, 0,
|
|
||||||
0, 0, 0, -9, 0, 0, 0, 0, -8, 0, 0, 0, 0, -7, 0, 0,
|
|
||||||
0, 0, 0, 0, -9, 0, 0, 0, -8, 0, 0, 0, -7, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, -9, 0, 0, -8, 0, 0, -7, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, -9,-17, -8,-15, -7, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,-10, -9, -8, -7, -6, 0, 0, 0, 0, 0,
|
|
||||||
0, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 7, 15, 8, 17, 9, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 7, 0, 0, 8, 0, 0, 9, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0,
|
|
||||||
0, 0, 0, 7, 0, 0, 0, 0, 8, 0, 0, 0, 0, 9, 0, 0,
|
|
||||||
0, 0, 7, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 9, 0,
|
|
||||||
0, 7, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9
|
|
||||||
};
|
|
||||||
|
|
||||||
static public final int getDirection(int from, int to) {
|
|
||||||
int offs = to + (to|7) - from - (from|7) + 0x77;
|
|
||||||
return dirTable[offs];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final byte distTable[] = {
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
||||||
0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
|
||||||
0, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 4, 3, 2, 1, 1, 1, 2, 3, 4, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 4, 3, 2, 1, 1, 1, 2, 3, 4, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 6, 7,
|
|
||||||
0, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7,
|
|
||||||
0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
|
||||||
0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
|
||||||
};
|
|
||||||
|
|
||||||
public static int getDistance(int from, int to) {
|
|
||||||
int offs = to + (to|7) - from - (from|7) + 0x77;
|
|
||||||
return distTable[offs];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long southFill(long mask) {
|
|
||||||
mask |= (mask >>> 8);
|
|
||||||
mask |= (mask >>> 16);
|
|
||||||
mask |= (mask >>> 32);
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long northFill(long mask) {
|
|
||||||
mask |= (mask << 8);
|
|
||||||
mask |= (mask << 16);
|
|
||||||
mask |= (mask << 32);
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int trailingZ[] = {
|
|
||||||
63, 0, 58, 1, 59, 47, 53, 2,
|
|
||||||
60, 39, 48, 27, 54, 33, 42, 3,
|
|
||||||
61, 51, 37, 40, 49, 18, 28, 20,
|
|
||||||
55, 30, 34, 11, 43, 14, 22, 4,
|
|
||||||
62, 57, 46, 52, 38, 26, 32, 41,
|
|
||||||
50, 36, 17, 19, 29, 10, 13, 21,
|
|
||||||
56, 45, 25, 31, 35, 16, 9, 12,
|
|
||||||
44, 24, 15, 8, 23, 7, 6, 5
|
|
||||||
};
|
|
||||||
|
|
||||||
static public final int numberOfTrailingZeros(long mask) {
|
|
||||||
return trailingZ[(int)(((mask & -mask) * 0x07EDD5E59A4E28C2L) >>> 58)];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,153 +34,6 @@ import java.util.Random;
|
|||||||
|
|
||||||
/** Implements an opening book. */
|
/** Implements an opening book. */
|
||||||
public class Book {
|
public class Book {
|
||||||
public static class BookEntry {
|
|
||||||
Move move;
|
|
||||||
int count;
|
|
||||||
BookEntry(Move move) {
|
|
||||||
this.move = move;
|
|
||||||
count = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private static Map<Long, List<BookEntry>> bookMap;
|
|
||||||
private static Random rndGen;
|
|
||||||
private static int numBookMoves = -1;
|
|
||||||
private boolean verbose;
|
|
||||||
|
|
||||||
public Book(boolean verbose) {
|
|
||||||
this.verbose = verbose;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initBook() {
|
|
||||||
if (numBookMoves >= 0)
|
|
||||||
return;
|
|
||||||
long t0 = System.currentTimeMillis();
|
|
||||||
bookMap = new HashMap<>();
|
|
||||||
rndGen = new SecureRandom();
|
|
||||||
rndGen.setSeed(System.currentTimeMillis());
|
|
||||||
numBookMoves = 0;
|
|
||||||
try (InputStream inStream = getClass().getResourceAsStream("/book.bin")) {
|
|
||||||
List<Byte> buf = new ArrayList<>(8192);
|
|
||||||
byte[] tmpBuf = new byte[1024];
|
|
||||||
while (true) {
|
|
||||||
int len = inStream.read(tmpBuf);
|
|
||||||
if (len <= 0) break;
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
buf.add(tmpBuf[i]);
|
|
||||||
}
|
|
||||||
Position startPos = TextIO.readFEN(TextIO.startPosFEN);
|
|
||||||
Position pos = new Position(startPos);
|
|
||||||
UndoInfo ui = new UndoInfo();
|
|
||||||
int len = buf.size();
|
|
||||||
for (int i = 0; i < len; i += 2) {
|
|
||||||
int b0 = buf.get(i); if (b0 < 0) b0 += 256;
|
|
||||||
int b1 = buf.get(i+1); if (b1 < 0) b1 += 256;
|
|
||||||
int move = (b0 << 8) + b1;
|
|
||||||
if (move == 0) {
|
|
||||||
pos = new Position(startPos);
|
|
||||||
} else {
|
|
||||||
boolean bad = ((move >> 15) & 1) != 0;
|
|
||||||
int prom = (move >> 12) & 7;
|
|
||||||
Move m = new Move(move & 63, (move >> 6) & 63,
|
|
||||||
promToPiece(prom, pos.whiteMove));
|
|
||||||
if (!bad)
|
|
||||||
addToBook(pos, m);
|
|
||||||
pos.makeMove(m, ui);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ChessParseError ex) {
|
|
||||||
throw new RuntimeException();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println("Can't read opening book resource");
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
if (verbose) {
|
|
||||||
long t1 = System.currentTimeMillis();
|
|
||||||
System.out.printf("Book moves:%d (parse time:%.3f)%n", numBookMoves,
|
|
||||||
(t1 - t0) / 1000.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Add a move to a position in the opening book. */
|
|
||||||
private void addToBook(Position pos, Move moveToAdd) {
|
|
||||||
List<BookEntry> ent = bookMap.get(pos.zobristHash());
|
|
||||||
if (ent == null) {
|
|
||||||
ent = new ArrayList<>();
|
|
||||||
bookMap.put(pos.zobristHash(), ent);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < ent.size(); i++) {
|
|
||||||
BookEntry be = ent.get(i);
|
|
||||||
if (be.move.equals(moveToAdd)) {
|
|
||||||
be.count++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BookEntry be = new BookEntry(moveToAdd);
|
|
||||||
ent.add(be);
|
|
||||||
numBookMoves++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return a random book move for a position, or null if out of book. */
|
|
||||||
public final Move getBookMove(Position pos) {
|
|
||||||
initBook();
|
|
||||||
List<BookEntry> bookMoves = bookMap.get(pos.zobristHash());
|
|
||||||
if (bookMoves == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<Move> legalMoves = MoveGen.instance.legalMoves(pos);
|
|
||||||
int sum = 0;
|
|
||||||
for (int i = 0; i < bookMoves.size(); i++) {
|
|
||||||
BookEntry be = bookMoves.get(i);
|
|
||||||
boolean contains = false;
|
|
||||||
for (Move m : legalMoves)
|
|
||||||
if (m.equals(be.move)) {
|
|
||||||
contains = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!contains) {
|
|
||||||
// If an illegal move was found, it means there was a hash collision.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
sum += getWeight(bookMoves.get(i).count);
|
|
||||||
}
|
|
||||||
if (sum <= 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int rnd = rndGen.nextInt(sum);
|
|
||||||
sum = 0;
|
|
||||||
for (int i = 0; i < bookMoves.size(); i++) {
|
|
||||||
sum += getWeight(bookMoves.get(i).count);
|
|
||||||
if (rnd < sum) {
|
|
||||||
return bookMoves.get(i).move;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Should never get here
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getWeight(int count) {
|
|
||||||
double tmp = Math.sqrt(count);
|
|
||||||
return (int)(tmp * Math.sqrt(tmp) * 100 + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return a string describing all book moves. */
|
|
||||||
public final String getAllBookMoves(Position pos) {
|
|
||||||
initBook();
|
|
||||||
StringBuilder ret = new StringBuilder();
|
|
||||||
List<BookEntry> bookMoves = bookMap.get(pos.zobristHash());
|
|
||||||
if (bookMoves != null) {
|
|
||||||
for (BookEntry be : bookMoves) {
|
|
||||||
String moveStr = TextIO.moveToString(pos, be.move, false);
|
|
||||||
ret.append(moveStr);
|
|
||||||
ret.append("(");
|
|
||||||
ret.append(be.count);
|
|
||||||
ret.append(") ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates the book.bin file. */
|
/** Creates the book.bin file. */
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
String inFile = args[0];
|
String inFile = args[0];
|
||||||
@@ -265,14 +118,4 @@ public class Book {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int promToPiece(int prom, boolean whiteMove) {
|
|
||||||
switch (prom) {
|
|
||||||
case 1: return whiteMove ? Piece.WQUEEN : Piece.BQUEEN;
|
|
||||||
case 2: return whiteMove ? Piece.WROOK : Piece.BROOK;
|
|
||||||
case 3: return whiteMove ? Piece.WBISHOP : Piece.BBISHOP;
|
|
||||||
case 4: return whiteMove ? Piece.WKNIGHT : Piece.BKNIGHT;
|
|
||||||
default: return Piece.EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,13 +87,11 @@ public class EcoBuilder {
|
|||||||
private void readGame(String pgn) throws Throwable {
|
private void readGame(String pgn) throws Throwable {
|
||||||
if (pgn.isEmpty())
|
if (pgn.isEmpty())
|
||||||
return;
|
return;
|
||||||
Game game = new Game(null, new TimeControlData());
|
GameTree tree = new GameTree();
|
||||||
PGNOptions options = new PGNOptions();
|
tree.readPGN(pgn);
|
||||||
game.readPGN(pgn, options);
|
|
||||||
|
|
||||||
// Determine name of opening
|
// Determine name of opening
|
||||||
HashMap<String,String> headers = new HashMap<>();
|
HashMap<String,String> headers = new HashMap<>();
|
||||||
GameTree tree = game.tree;
|
|
||||||
tree.getHeaders(headers);
|
tree.getHeaders(headers);
|
||||||
int ecoIdx = addData(headers, "ECO");
|
int ecoIdx = addData(headers, "ECO");
|
||||||
int opnIdx = addData(headers, "Opening");
|
int opnIdx = addData(headers, "Opening");
|
||||||
|
|||||||
@@ -1,538 +0,0 @@
|
|||||||
/*
|
|
||||||
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 chess;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import chess.GameTree.Node;
|
|
||||||
|
|
||||||
public class Game {
|
|
||||||
boolean pendingDrawOffer;
|
|
||||||
public GameTree tree;
|
|
||||||
TimeControl timeController;
|
|
||||||
private boolean gamePaused;
|
|
||||||
/** If true, add new moves as mainline moves. */
|
|
||||||
private AddMoveBehavior addMoveBehavior;
|
|
||||||
|
|
||||||
private PgnToken.PgnTokenReceiver gameTextListener;
|
|
||||||
|
|
||||||
public Game(PgnToken.PgnTokenReceiver gameTextListener, TimeControlData tcData) {
|
|
||||||
this.gameTextListener = gameTextListener;
|
|
||||||
timeController = new TimeControl();
|
|
||||||
timeController.setTimeControl(tcData);
|
|
||||||
gamePaused = false;
|
|
||||||
newGame();
|
|
||||||
tree.setTimeControlData(tcData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** De-serialize from input stream. */
|
|
||||||
final void readFromStream(DataInputStream dis, int version) throws IOException, ChessParseError {
|
|
||||||
tree.readFromStream(dis, version);
|
|
||||||
if (version >= 3)
|
|
||||||
timeController.readFromStream(dis, version);
|
|
||||||
updateTimeControl(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Serialize to output stream. */
|
|
||||||
final synchronized void writeToStream(DataOutputStream dos) throws IOException {
|
|
||||||
tree.writeToStream(dos);
|
|
||||||
timeController.writeToStream(dos);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setGamePaused(boolean gamePaused) {
|
|
||||||
if (gamePaused != this.gamePaused) {
|
|
||||||
this.gamePaused = gamePaused;
|
|
||||||
updateTimeControl(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Controls behavior when a new move is added to the game.*/
|
|
||||||
public static enum AddMoveBehavior {
|
|
||||||
/** Add the new move first in the list of variations. */
|
|
||||||
ADD_FIRST,
|
|
||||||
/** Add the new move last in the list of variations. */
|
|
||||||
ADD_LAST,
|
|
||||||
/** Remove all variations not matching the new move. */
|
|
||||||
REPLACE
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set whether new moves are entered as mainline moves or variations. */
|
|
||||||
public final void setAddFirst(AddMoveBehavior amb) {
|
|
||||||
addMoveBehavior = amb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets start position and discards the whole game tree. */
|
|
||||||
final void setPos(Position pos) {
|
|
||||||
tree.setStartPos(new Position(pos));
|
|
||||||
updateTimeControl(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set game state from a PGN string. */
|
|
||||||
final public boolean readPGN(String pgn, PGNOptions options) throws ChessParseError {
|
|
||||||
boolean ret = tree.readPGN(pgn, options);
|
|
||||||
if (ret) {
|
|
||||||
TimeControlData tcData = tree.getTimeControlData();
|
|
||||||
if (tcData != null)
|
|
||||||
timeController.setTimeControl(tcData);
|
|
||||||
updateTimeControl(tcData != null);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Position currPos() {
|
|
||||||
return tree.currentPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Position prevPos() {
|
|
||||||
Move m = tree.currentNode.move;
|
|
||||||
if (m != null) {
|
|
||||||
tree.goBack();
|
|
||||||
Position ret = new Position(currPos());
|
|
||||||
tree.goForward(-1);
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
return currPos();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Move getNextMove() {
|
|
||||||
if (canRedoMove()) {
|
|
||||||
tree.goForward(-1);
|
|
||||||
Move ret = tree.currentNode.move;
|
|
||||||
tree.goBack();
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the game state according to move/command string from a player.
|
|
||||||
* @param str The move or command to process.
|
|
||||||
* @return Pair where first item is true if str was understood, false otherwise.
|
|
||||||
* Second item is move played, or null if no move was played. */
|
|
||||||
public final Pair<Boolean, Move> processString(String str) {
|
|
||||||
if (getGameState() != GameState.ALIVE)
|
|
||||||
return new Pair<>(false, null);
|
|
||||||
if (str.startsWith("draw ")) {
|
|
||||||
String drawCmd = str.substring(str.indexOf(" ") + 1);
|
|
||||||
Move m = handleDrawCmd(drawCmd, true);
|
|
||||||
return new Pair<>(true, m);
|
|
||||||
} else if (str.equals("resign")) {
|
|
||||||
addToGameTree(new Move(0, 0, 0), "resign");
|
|
||||||
return new Pair<>(true, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
Move m = TextIO.UCIstringToMove(str);
|
|
||||||
if (m != null)
|
|
||||||
if (!TextIO.isValid(currPos(), m))
|
|
||||||
m = null;
|
|
||||||
if (m == null) {
|
|
||||||
m = TextIO.stringToMove(currPos(), str);
|
|
||||||
if (!TextIO.isValid(currPos(), m))
|
|
||||||
m = null;
|
|
||||||
}
|
|
||||||
if (m == null)
|
|
||||||
return new Pair<>(false, null);
|
|
||||||
|
|
||||||
addToGameTree(m, pendingDrawOffer ? "draw offer" : "");
|
|
||||||
return new Pair<>(true, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Try claim a draw using a command string. Does not play the move involved
|
|
||||||
* in the draw claim if the draw claim is invalid. */
|
|
||||||
public final void tryClaimDraw(String str) {
|
|
||||||
if (str.startsWith("draw ")) {
|
|
||||||
String drawCmd = str.substring(str.indexOf(" ") + 1);
|
|
||||||
handleDrawCmd(drawCmd, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addToGameTree(Move m, String playerAction) {
|
|
||||||
if (m.equals(new Move(0, 0, 0))) { // Don't create more than one game-ending move at a node
|
|
||||||
List<Move> varMoves = tree.variations();
|
|
||||||
for (int i = varMoves.size() - 1; i >= 0; i--)
|
|
||||||
if (varMoves.get(i).equals(m))
|
|
||||||
tree.deleteVariation(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean movePresent = false;
|
|
||||||
int varNo;
|
|
||||||
{
|
|
||||||
ArrayList<Move> varMoves = tree.variations();
|
|
||||||
int nVars = varMoves.size();
|
|
||||||
if (addMoveBehavior == AddMoveBehavior.REPLACE) {
|
|
||||||
boolean modified = false;
|
|
||||||
for (int i = nVars-1; i >= 0; i--) {
|
|
||||||
if (!m.equals(varMoves.get(i))) {
|
|
||||||
tree.deleteVariation(i);
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (modified) {
|
|
||||||
varMoves = tree.variations();
|
|
||||||
nVars = varMoves.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Boolean gameEndingMove = null;
|
|
||||||
for (varNo = 0; varNo < nVars; varNo++) {
|
|
||||||
if (varMoves.get(varNo).equals(m)) {
|
|
||||||
boolean match = true;
|
|
||||||
if (playerAction.isEmpty()) {
|
|
||||||
if (gameEndingMove == null)
|
|
||||||
gameEndingMove = gameEndingMove(m);
|
|
||||||
if (!gameEndingMove) {
|
|
||||||
tree.goForward(varNo, false);
|
|
||||||
match = tree.getGameState() == GameState.ALIVE;
|
|
||||||
tree.goBack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (match) {
|
|
||||||
movePresent = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!movePresent) {
|
|
||||||
String moveStr = TextIO.moveToUCIString(m);
|
|
||||||
varNo = tree.addMove(moveStr, playerAction, 0, "", "");
|
|
||||||
}
|
|
||||||
int newPos = 0;
|
|
||||||
if (addMoveBehavior == AddMoveBehavior.ADD_LAST)
|
|
||||||
newPos = varNo;
|
|
||||||
tree.reorderVariation(varNo, newPos);
|
|
||||||
tree.goForward(newPos);
|
|
||||||
int remaining = timeController.moveMade(System.currentTimeMillis(), !gamePaused);
|
|
||||||
tree.setRemainingTime(remaining);
|
|
||||||
updateTimeControl(true);
|
|
||||||
pendingDrawOffer = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return true if move "m" in the current position ends the game (mate or stalemate). */
|
|
||||||
private boolean gameEndingMove(Move m) {
|
|
||||||
Position pos = currPos();
|
|
||||||
UndoInfo ui = new UndoInfo();
|
|
||||||
pos.makeMove(m, ui);
|
|
||||||
boolean gameEnd = MoveGen.instance.legalMoves(pos).isEmpty();
|
|
||||||
pos.unMakeMove(m, ui);
|
|
||||||
return gameEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTimeControl(boolean discardElapsed) {
|
|
||||||
Position currPos = currPos();
|
|
||||||
int move = currPos.fullMoveCounter;
|
|
||||||
boolean wtm = currPos.whiteMove;
|
|
||||||
if (discardElapsed || (move != timeController.currentMove) || (wtm != timeController.whiteToMove)) {
|
|
||||||
int whiteBaseTime = tree.getRemainingTime(true, timeController.getInitialTime(true));
|
|
||||||
int blackBaseTime = tree.getRemainingTime(false, timeController.getInitialTime(false));
|
|
||||||
timeController.setCurrentMove(move, wtm, whiteBaseTime, blackBaseTime);
|
|
||||||
}
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
boolean stopTimer = gamePaused || (getGameState() != GameState.ALIVE);
|
|
||||||
if (!stopTimer) {
|
|
||||||
try {
|
|
||||||
if (TextIO.readFEN(TextIO.startPosFEN).equals(currPos))
|
|
||||||
stopTimer = true;
|
|
||||||
} catch (ChessParseError e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stopTimer) {
|
|
||||||
timeController.stopTimer(now);
|
|
||||||
} else {
|
|
||||||
timeController.startTimer(now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the last played move, or null if no moves played yet.
|
|
||||||
*/
|
|
||||||
public final Move getLastMove() {
|
|
||||||
return tree.currentNode.move;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return true if there is a move to redo. */
|
|
||||||
public final boolean canRedoMove() {
|
|
||||||
int nVar = tree.variations().size();
|
|
||||||
return nVar > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get number of variations in current game position. */
|
|
||||||
public final int numVariations() {
|
|
||||||
if (tree.currentNode == tree.rootNode)
|
|
||||||
return 1;
|
|
||||||
tree.goBack();
|
|
||||||
int nChildren = tree.variations().size();
|
|
||||||
tree.goForward(-1);
|
|
||||||
return nChildren;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get current variation in current position. */
|
|
||||||
public final int currVariation() {
|
|
||||||
if (tree.currentNode == tree.rootNode)
|
|
||||||
return 0;
|
|
||||||
tree.goBack();
|
|
||||||
int defChild = tree.currentNode.defaultChild;
|
|
||||||
tree.goForward(-1);
|
|
||||||
return defChild;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Go to a new variation in the game tree. */
|
|
||||||
public final void changeVariation(int delta) {
|
|
||||||
if (tree.currentNode == tree.rootNode)
|
|
||||||
return;
|
|
||||||
tree.goBack();
|
|
||||||
int defChild = tree.currentNode.defaultChild;
|
|
||||||
int nChildren = tree.variations().size();
|
|
||||||
int newChild = defChild + delta;
|
|
||||||
newChild = Math.max(newChild, 0);
|
|
||||||
newChild = Math.min(newChild, nChildren - 1);
|
|
||||||
tree.goForward(newChild);
|
|
||||||
pendingDrawOffer = false;
|
|
||||||
updateTimeControl(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Move current variation up/down in the game tree. */
|
|
||||||
public final void moveVariation(int delta) {
|
|
||||||
int nBack = 0;
|
|
||||||
boolean found = false;
|
|
||||||
while (tree.currentNode != tree.rootNode) {
|
|
||||||
tree.goBack();
|
|
||||||
nBack++;
|
|
||||||
if (((delta < 0) && tree.currentNode.defaultChild > 0) ||
|
|
||||||
((delta > 0) && tree.currentNode.defaultChild < tree.variations().size() - 1)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found) {
|
|
||||||
int varNo = tree.currentNode.defaultChild;
|
|
||||||
int nChildren = tree.variations().size();
|
|
||||||
int newPos = varNo + delta;
|
|
||||||
newPos = Math.max(newPos, 0);
|
|
||||||
newPos = Math.min(newPos, nChildren - 1);
|
|
||||||
tree.reorderVariation(varNo, newPos);
|
|
||||||
tree.goForward(newPos);
|
|
||||||
nBack--;
|
|
||||||
}
|
|
||||||
while (nBack > 0) {
|
|
||||||
tree.goForward(-1);
|
|
||||||
nBack--;
|
|
||||||
}
|
|
||||||
pendingDrawOffer = false;
|
|
||||||
updateTimeControl(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return true if the current variation can be moved up/down. */
|
|
||||||
public final boolean canMoveVariation(int delta) {
|
|
||||||
int nBack = 0;
|
|
||||||
boolean found = false;
|
|
||||||
while (tree.currentNode != tree.rootNode) {
|
|
||||||
tree.goBack();
|
|
||||||
nBack++;
|
|
||||||
if (((delta < 0) && tree.currentNode.defaultChild > 0) ||
|
|
||||||
((delta > 0) && tree.currentNode.defaultChild < tree.variations().size() - 1)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (nBack > 0) {
|
|
||||||
tree.goForward(-1);
|
|
||||||
nBack--;
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Delete whole game sub-tree rooted at current position. */
|
|
||||||
public final void removeSubTree() {
|
|
||||||
if (getLastMove() != null) {
|
|
||||||
tree.goBack();
|
|
||||||
int defChild = tree.currentNode.defaultChild;
|
|
||||||
tree.deleteVariation(defChild);
|
|
||||||
} else {
|
|
||||||
while (canRedoMove())
|
|
||||||
tree.deleteVariation(0);
|
|
||||||
}
|
|
||||||
pendingDrawOffer = false;
|
|
||||||
updateTimeControl(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static enum GameState {
|
|
||||||
ALIVE,
|
|
||||||
WHITE_MATE, // White mates
|
|
||||||
BLACK_MATE, // Black mates
|
|
||||||
WHITE_STALEMATE, // White is stalemated
|
|
||||||
BLACK_STALEMATE, // Black is stalemated
|
|
||||||
DRAW_REP, // Draw by 3-fold repetition
|
|
||||||
DRAW_50, // Draw by 50 move rule
|
|
||||||
DRAW_NO_MATE, // Draw by impossibility of check mate
|
|
||||||
DRAW_AGREE, // Draw by agreement
|
|
||||||
RESIGN_WHITE, // White resigns
|
|
||||||
RESIGN_BLACK // Black resigns
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current state (draw, mate, ongoing, etc) of the game.
|
|
||||||
*/
|
|
||||||
public final GameState getGameState() {
|
|
||||||
return tree.getGameState();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a draw offer is available.
|
|
||||||
* @return True if the current player has the option to accept a draw offer.
|
|
||||||
*/
|
|
||||||
public final boolean haveDrawOffer() {
|
|
||||||
return tree.currentNode.playerAction.equals("draw offer");
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void undoMove() {
|
|
||||||
Move m = tree.currentNode.move;
|
|
||||||
if (m != null) {
|
|
||||||
tree.goBack();
|
|
||||||
pendingDrawOffer = false;
|
|
||||||
updateTimeControl(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void redoMove() {
|
|
||||||
if (canRedoMove()) {
|
|
||||||
tree.goForward(-1);
|
|
||||||
pendingDrawOffer = false;
|
|
||||||
updateTimeControl(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Go to given node in game tree.
|
|
||||||
* @return True if current node changed, false otherwise. */
|
|
||||||
public final boolean goNode(Node node) {
|
|
||||||
if (!tree.goNode(node))
|
|
||||||
return false;
|
|
||||||
pendingDrawOffer = false;
|
|
||||||
updateTimeControl(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void newGame() {
|
|
||||||
tree = new GameTree(gameTextListener);
|
|
||||||
timeController.reset();
|
|
||||||
pendingDrawOffer = false;
|
|
||||||
updateTimeControl(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the last zeroing position and a list of moves
|
|
||||||
* to go from that position to the current position.
|
|
||||||
*/
|
|
||||||
public final Pair<Position, ArrayList<Move>> getUCIHistory() {
|
|
||||||
Pair<List<Node>, Integer> ml = tree.getMoveList();
|
|
||||||
List<Node> moveList = ml.first;
|
|
||||||
Position pos = new Position(tree.startPos);
|
|
||||||
ArrayList<Move> mList = new ArrayList<>();
|
|
||||||
Position currPos = new Position(pos);
|
|
||||||
UndoInfo ui = new UndoInfo();
|
|
||||||
int nMoves = ml.second;
|
|
||||||
for (int i = 0; i < nMoves; i++) {
|
|
||||||
Node n = moveList.get(i);
|
|
||||||
mList.add(n.move);
|
|
||||||
currPos.makeMove(n.move, ui);
|
|
||||||
if (currPos.halfMoveClock == 0) {
|
|
||||||
pos = new Position(currPos);
|
|
||||||
mList.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Pair<>(pos, mList);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Move handleDrawCmd(String drawCmd, boolean playDrawMove) {
|
|
||||||
Move ret = null;
|
|
||||||
Position pos = tree.currentPos;
|
|
||||||
if (drawCmd.startsWith("rep") || drawCmd.startsWith("50")) {
|
|
||||||
boolean rep = drawCmd.startsWith("rep");
|
|
||||||
Move m = null;
|
|
||||||
String ms = null;
|
|
||||||
int firstSpace = drawCmd.indexOf(" ");
|
|
||||||
if (firstSpace >= 0) {
|
|
||||||
ms = drawCmd.substring(firstSpace + 1);
|
|
||||||
if (ms.length() > 0) {
|
|
||||||
m = TextIO.stringToMove(pos, ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean valid;
|
|
||||||
if (rep) {
|
|
||||||
valid = false;
|
|
||||||
UndoInfo ui = new UndoInfo();
|
|
||||||
int repetitions = 0;
|
|
||||||
Position posToCompare = new Position(tree.currentPos);
|
|
||||||
if (m != null) {
|
|
||||||
posToCompare.makeMove(m, ui);
|
|
||||||
repetitions = 1;
|
|
||||||
}
|
|
||||||
Pair<List<Node>, Integer> ml = tree.getMoveList();
|
|
||||||
List<Node> moveList = ml.first;
|
|
||||||
Position tmpPos = new Position(tree.startPos);
|
|
||||||
if (tmpPos.drawRuleEquals(posToCompare))
|
|
||||||
repetitions++;
|
|
||||||
int nMoves = ml.second;
|
|
||||||
for (int i = 0; i < nMoves; i++) {
|
|
||||||
Node n = moveList.get(i);
|
|
||||||
tmpPos.makeMove(n.move, ui);
|
|
||||||
TextIO.fixupEPSquare(tmpPos);
|
|
||||||
if (tmpPos.drawRuleEquals(posToCompare))
|
|
||||||
repetitions++;
|
|
||||||
}
|
|
||||||
if (repetitions >= 3)
|
|
||||||
valid = true;
|
|
||||||
} else {
|
|
||||||
Position tmpPos = new Position(pos);
|
|
||||||
if (m != null) {
|
|
||||||
UndoInfo ui = new UndoInfo();
|
|
||||||
tmpPos.makeMove(m, ui);
|
|
||||||
}
|
|
||||||
valid = tmpPos.halfMoveClock >= 100;
|
|
||||||
}
|
|
||||||
if (valid) {
|
|
||||||
String playerAction = rep ? "draw rep" : "draw 50";
|
|
||||||
if (m != null)
|
|
||||||
playerAction += " " + TextIO.moveToString(pos, m, false);
|
|
||||||
addToGameTree(new Move(0, 0, 0), playerAction);
|
|
||||||
} else {
|
|
||||||
pendingDrawOffer = true;
|
|
||||||
if (m != null && playDrawMove) {
|
|
||||||
ret = processString(ms).second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (drawCmd.startsWith("offer ")) {
|
|
||||||
pendingDrawOffer = true;
|
|
||||||
String ms = drawCmd.substring(drawCmd.indexOf(" ") + 1);
|
|
||||||
if (TextIO.stringToMove(pos, ms) != null) {
|
|
||||||
ret = processString(ms).second;
|
|
||||||
}
|
|
||||||
} else if (drawCmd.equals("accept")) {
|
|
||||||
if (haveDrawOffer())
|
|
||||||
addToGameTree(new Move(0, 0, 0), "draw accept");
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -30,58 +30,13 @@ public class Move {
|
|||||||
/** Promotion piece. */
|
/** Promotion piece. */
|
||||||
public int promoteTo;
|
public int promoteTo;
|
||||||
|
|
||||||
public int score;
|
|
||||||
|
|
||||||
/** Create a move object. */
|
/** Create a move object. */
|
||||||
public Move(int from, int to, int promoteTo) {
|
public Move(int from, int to, int promoteTo) {
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
this.promoteTo = promoteTo;
|
this.promoteTo = promoteTo;
|
||||||
this.score = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Move(int from, int to, int promoteTo, int score) {
|
|
||||||
this.from = from;
|
|
||||||
this.to = to;
|
|
||||||
this.promoteTo = promoteTo;
|
|
||||||
this.score = score;
|
|
||||||
}
|
|
||||||
|
|
||||||
static public class SortByScore implements Comparator<Move> {
|
|
||||||
public int compare(Move sm1, Move sm2) {
|
|
||||||
return sm2.score - sm1.score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Move(Move m) {
|
|
||||||
this.from = m.from;
|
|
||||||
this.to = m.to;
|
|
||||||
this.promoteTo = m.promoteTo;
|
|
||||||
this.score = m.score;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void copyFrom(Move m) {
|
|
||||||
from = m.from;
|
|
||||||
to = m.to;
|
|
||||||
promoteTo = m.promoteTo;
|
|
||||||
// score = m.score;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void clear() {
|
|
||||||
from = 0;
|
|
||||||
to = 0;
|
|
||||||
promoteTo = 0;
|
|
||||||
score = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setMove(int from, int to, int promoteTo, int score) {
|
|
||||||
this.from = from;
|
|
||||||
this.to = to;
|
|
||||||
this.promoteTo = promoteTo;
|
|
||||||
this.score = score;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Note that score is not included in the comparison. */
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if ((o == null) || (o.getClass() != this.getClass()))
|
if ((o == null) || (o.getClass() != this.getClass()))
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
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 chess;
|
|
||||||
|
|
||||||
/** Settings controlling PGN import/export */
|
|
||||||
public class PGNOptions {
|
|
||||||
/** Pieces displayed as English letters. */
|
|
||||||
public static final int PT_ENGLISH = 0;
|
|
||||||
/** Pieces displayed as local language letters. */
|
|
||||||
public static final int PT_LOCAL = 1;
|
|
||||||
/** Piece displayed in figurine notation, by using UniCode characters
|
|
||||||
* and a special font. */
|
|
||||||
public static final int PT_FIGURINE = 2;
|
|
||||||
|
|
||||||
public static class Viewer {
|
|
||||||
public boolean variations;
|
|
||||||
public boolean comments;
|
|
||||||
public boolean nag;
|
|
||||||
public boolean headers;
|
|
||||||
public int pieceType;
|
|
||||||
}
|
|
||||||
public static class Import {
|
|
||||||
public boolean variations;
|
|
||||||
public boolean comments;
|
|
||||||
public boolean nag;
|
|
||||||
}
|
|
||||||
public static class Export {
|
|
||||||
public boolean variations;
|
|
||||||
public boolean comments;
|
|
||||||
public boolean nag;
|
|
||||||
public boolean playerAction;
|
|
||||||
public boolean clockInfo;
|
|
||||||
public boolean pgnPromotions;
|
|
||||||
public boolean moveNrAfterNag;
|
|
||||||
public int pieceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Viewer view;
|
|
||||||
public Import imp;
|
|
||||||
public Export exp;
|
|
||||||
|
|
||||||
public PGNOptions() {
|
|
||||||
view = new Viewer();
|
|
||||||
imp = new Import();
|
|
||||||
exp = new Export();
|
|
||||||
exp.moveNrAfterNag = true;
|
|
||||||
exp.pieceType = PT_ENGLISH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -45,19 +45,4 @@ public class PgnToken {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** PGN parser visitor interface. */
|
|
||||||
public interface PgnTokenReceiver {
|
|
||||||
/** If this method returns false, the object needs a full re-initialization, using clear() and processToken(). */
|
|
||||||
boolean isUpToDate();
|
|
||||||
|
|
||||||
/** Clear object state. */
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/** Update object state with one token from a PGN game. */
|
|
||||||
void processToken(GameTree.Node node, int type, String token);
|
|
||||||
|
|
||||||
/** Change current move number. */
|
|
||||||
void setCurrent(GameTree.Node node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,10 +45,4 @@ public class Piece {
|
|||||||
public static boolean isWhite(int pType) {
|
public static boolean isWhite(int pType) {
|
||||||
return pType < BKING;
|
return pType < BKING;
|
||||||
}
|
}
|
||||||
public static int makeWhite(int pType) {
|
|
||||||
return pType < BKING ? pType : pType - (BKING - WKING);
|
|
||||||
}
|
|
||||||
public static int makeBlack(int pType) {
|
|
||||||
return ((pType > EMPTY) && (pType < BKING)) ? pType + (BKING - WKING) : pType;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ public class Position {
|
|||||||
|
|
||||||
// Bitboards
|
// Bitboards
|
||||||
public long[] pieceTypeBB;
|
public long[] pieceTypeBB;
|
||||||
public long whiteBB, blackBB;
|
|
||||||
|
|
||||||
public boolean whiteMove;
|
public boolean whiteMove;
|
||||||
|
|
||||||
@@ -66,7 +65,6 @@ public class Position {
|
|||||||
for (int i = 0; i < Piece.nPieceTypes; i++) {
|
for (int i = 0; i < Piece.nPieceTypes; i++) {
|
||||||
pieceTypeBB[i] = 0L;
|
pieceTypeBB[i] = 0L;
|
||||||
}
|
}
|
||||||
whiteBB = blackBB = 0L;
|
|
||||||
whiteMove = true;
|
whiteMove = true;
|
||||||
castleMask = 0;
|
castleMask = 0;
|
||||||
epSquare = -1;
|
epSquare = -1;
|
||||||
@@ -84,8 +82,6 @@ public class Position {
|
|||||||
for (int i = 0; i < Piece.nPieceTypes; i++) {
|
for (int i = 0; i < Piece.nPieceTypes; i++) {
|
||||||
pieceTypeBB[i] = other.pieceTypeBB[i];
|
pieceTypeBB[i] = other.pieceTypeBB[i];
|
||||||
}
|
}
|
||||||
whiteBB = other.whiteBB;
|
|
||||||
blackBB = other.blackBB;
|
|
||||||
whiteMove = other.whiteMove;
|
whiteMove = other.whiteMove;
|
||||||
castleMask = other.castleMask;
|
castleMask = other.castleMask;
|
||||||
epSquare = other.epSquare;
|
epSquare = other.epSquare;
|
||||||
@@ -126,22 +122,7 @@ public class Position {
|
|||||||
public final long zobristHash() {
|
public final long zobristHash() {
|
||||||
return hashKey;
|
return hashKey;
|
||||||
}
|
}
|
||||||
public final long pawnZobristHash() {
|
|
||||||
return pHashKey;
|
|
||||||
}
|
|
||||||
public final long kingZobristHash() {
|
|
||||||
return psHashKeys[Piece.WKING][wKingSq] ^
|
|
||||||
psHashKeys[Piece.BKING][bKingSq];
|
|
||||||
}
|
|
||||||
|
|
||||||
public final long historyHash() {
|
|
||||||
long ret = hashKey;
|
|
||||||
if (halfMoveClock >= 80) {
|
|
||||||
ret ^= moveCntKeys[Math.min(halfMoveClock, 100)];
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decide if two positions are equal in the sense of the draw by repetition rule.
|
* Decide if two positions are equal in the sense of the draw by repetition rule.
|
||||||
* @return True if positions are equal, false otherwise.
|
* @return True if positions are equal, false otherwise.
|
||||||
@@ -204,13 +185,9 @@ public class Position {
|
|||||||
pieceTypeBB[piece] &= ~sqMaskF;
|
pieceTypeBB[piece] &= ~sqMaskF;
|
||||||
pieceTypeBB[piece] |= sqMaskT;
|
pieceTypeBB[piece] |= sqMaskT;
|
||||||
if (Piece.isWhite(piece)) {
|
if (Piece.isWhite(piece)) {
|
||||||
whiteBB &= ~sqMaskF;
|
|
||||||
whiteBB |= sqMaskT;
|
|
||||||
if (piece == Piece.WKING)
|
if (piece == Piece.WKING)
|
||||||
wKingSq = to;
|
wKingSq = to;
|
||||||
} else {
|
} else {
|
||||||
blackBB &= ~sqMaskF;
|
|
||||||
blackBB |= sqMaskT;
|
|
||||||
if (piece == Piece.BKING)
|
if (piece == Piece.BKING)
|
||||||
bKingSq = to;
|
bKingSq = to;
|
||||||
}
|
}
|
||||||
@@ -232,12 +209,10 @@ public class Position {
|
|||||||
|
|
||||||
if (removedPiece != Piece.EMPTY) {
|
if (removedPiece != Piece.EMPTY) {
|
||||||
if (Piece.isWhite(removedPiece)) {
|
if (Piece.isWhite(removedPiece)) {
|
||||||
whiteBB &= ~sqMask;
|
|
||||||
if (removedPiece == Piece.WPAWN) {
|
if (removedPiece == Piece.WPAWN) {
|
||||||
pHashKey ^= psHashKeys[Piece.WPAWN][square];
|
pHashKey ^= psHashKeys[Piece.WPAWN][square];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
blackBB &= ~sqMask;
|
|
||||||
if (removedPiece == Piece.BPAWN) {
|
if (removedPiece == Piece.BPAWN) {
|
||||||
pHashKey ^= psHashKeys[Piece.BPAWN][square];
|
pHashKey ^= psHashKeys[Piece.BPAWN][square];
|
||||||
}
|
}
|
||||||
@@ -246,14 +221,12 @@ public class Position {
|
|||||||
|
|
||||||
if (piece != Piece.EMPTY) {
|
if (piece != Piece.EMPTY) {
|
||||||
if (Piece.isWhite(piece)) {
|
if (Piece.isWhite(piece)) {
|
||||||
whiteBB |= sqMask;
|
|
||||||
if (piece == Piece.WPAWN) {
|
if (piece == Piece.WPAWN) {
|
||||||
pHashKey ^= psHashKeys[Piece.WPAWN][square];
|
pHashKey ^= psHashKeys[Piece.WPAWN][square];
|
||||||
}
|
}
|
||||||
if (piece == Piece.WKING)
|
if (piece == Piece.WKING)
|
||||||
wKingSq = square;
|
wKingSq = square;
|
||||||
} else {
|
} else {
|
||||||
blackBB |= sqMask;
|
|
||||||
if (piece == Piece.BPAWN) {
|
if (piece == Piece.BPAWN) {
|
||||||
pHashKey ^= psHashKeys[Piece.BPAWN][square];
|
pHashKey ^= psHashKeys[Piece.BPAWN][square];
|
||||||
}
|
}
|
||||||
@@ -263,50 +236,6 @@ public class Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a square to a piece value.
|
|
||||||
* Special version that only updates enough of the state for the SEE function to be happy.
|
|
||||||
*/
|
|
||||||
public final void setSEEPiece(int square, int piece) {
|
|
||||||
int removedPiece = squares[square];
|
|
||||||
|
|
||||||
// Update board
|
|
||||||
squares[square] = piece;
|
|
||||||
|
|
||||||
// Update bitboards
|
|
||||||
long sqMask = 1L << square;
|
|
||||||
pieceTypeBB[removedPiece] &= ~sqMask;
|
|
||||||
pieceTypeBB[piece] |= sqMask;
|
|
||||||
if (removedPiece != Piece.EMPTY) {
|
|
||||||
if (Piece.isWhite(removedPiece))
|
|
||||||
whiteBB &= ~sqMask;
|
|
||||||
else
|
|
||||||
blackBB &= ~sqMask;
|
|
||||||
}
|
|
||||||
if (piece != Piece.EMPTY) {
|
|
||||||
if (Piece.isWhite(piece))
|
|
||||||
whiteBB |= sqMask;
|
|
||||||
else
|
|
||||||
blackBB |= sqMask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return true if white long castling right has not been lost. */
|
|
||||||
public final boolean a1Castle() {
|
|
||||||
return (castleMask & (1 << A1_CASTLE)) != 0;
|
|
||||||
}
|
|
||||||
/** Return true if white short castling right has not been lost. */
|
|
||||||
public final boolean h1Castle() {
|
|
||||||
return (castleMask & (1 << H1_CASTLE)) != 0;
|
|
||||||
}
|
|
||||||
/** Return true if black long castling right has not been lost. */
|
|
||||||
public final boolean a8Castle() {
|
|
||||||
return (castleMask & (1 << A8_CASTLE)) != 0;
|
|
||||||
}
|
|
||||||
/** Return true if black short castling right has not been lost. */
|
|
||||||
public final boolean h8Castle() {
|
|
||||||
return (castleMask & (1 << H8_CASTLE)) != 0;
|
|
||||||
}
|
|
||||||
/** Bitmask describing castling rights. */
|
/** Bitmask describing castling rights. */
|
||||||
public final int getCastleMask() {
|
public final int getCastleMask() {
|
||||||
return castleMask;
|
return castleMask;
|
||||||
@@ -334,15 +263,6 @@ public class Position {
|
|||||||
return white ? wKingSq : bKingSq;
|
return white ? wKingSq : bKingSq;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Count number of pieces of a certain type. */
|
|
||||||
public final int nPieces(int pType) {
|
|
||||||
int ret = 0;
|
|
||||||
for (int sq = 0; sq < 64; sq++)
|
|
||||||
if (squares[sq] == pType)
|
|
||||||
ret++;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Apply a move to the current position. */
|
/** Apply a move to the current position. */
|
||||||
public final void makeMove(Move move, UndoInfo ui) {
|
public final void makeMove(Move move, UndoInfo ui) {
|
||||||
ui.capturedPiece = squares[move.to];
|
ui.capturedPiece = squares[move.to];
|
||||||
@@ -491,46 +411,6 @@ public class Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply a move to the current position.
|
|
||||||
* Special version that only updates enough of the state for the SEE function to be happy.
|
|
||||||
*/
|
|
||||||
public final void makeSEEMove(Move move, UndoInfo ui) {
|
|
||||||
ui.capturedPiece = squares[move.to];
|
|
||||||
|
|
||||||
int p = squares[move.from];
|
|
||||||
|
|
||||||
// Handle en passant
|
|
||||||
if (move.to == epSquare) {
|
|
||||||
if (p == Piece.WPAWN) {
|
|
||||||
setSEEPiece(move.to - 8, Piece.EMPTY);
|
|
||||||
} else if (p == Piece.BPAWN) {
|
|
||||||
setSEEPiece(move.to + 8, Piece.EMPTY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform move
|
|
||||||
setSEEPiece(move.from, Piece.EMPTY);
|
|
||||||
setSEEPiece(move.to, p);
|
|
||||||
whiteMove = !whiteMove;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void unMakeSEEMove(Move move, UndoInfo ui) {
|
|
||||||
whiteMove = !whiteMove;
|
|
||||||
int p = squares[move.to];
|
|
||||||
setSEEPiece(move.from, p);
|
|
||||||
setSEEPiece(move.to, ui.capturedPiece);
|
|
||||||
|
|
||||||
// Handle en passant
|
|
||||||
if (move.to == epSquare) {
|
|
||||||
if (p == Piece.WPAWN) {
|
|
||||||
setSEEPiece(move.to - 8, Piece.BPAWN);
|
|
||||||
} else if (p == Piece.BPAWN) {
|
|
||||||
setSEEPiece(move.to + 8, Piece.WPAWN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeCastleRights(int square) {
|
private void removeCastleRights(int square) {
|
||||||
if (square == Position.getSquare(0, 0)) {
|
if (square == Position.getSquare(0, 0)) {
|
||||||
setCastleMask(castleMask & ~(1 << Position.A1_CASTLE));
|
setCastleMask(castleMask & ~(1 << Position.A1_CASTLE));
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ public class TextIO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos.setCastleMask(castleMask);
|
pos.setCastleMask(castleMask);
|
||||||
removeBogusCastleFlags(pos);
|
|
||||||
|
|
||||||
if (words.length > 3) {
|
if (words.length > 3) {
|
||||||
// En passant target square
|
// En passant target square
|
||||||
@@ -182,21 +181,6 @@ public class TextIO {
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeBogusCastleFlags(Position pos) {
|
|
||||||
int castleMask = pos.getCastleMask();
|
|
||||||
int validCastle = 0;
|
|
||||||
if (pos.getPiece(4) == Piece.WKING) {
|
|
||||||
if (pos.getPiece(0) == Piece.WROOK) validCastle |= (1 << Position.A1_CASTLE);
|
|
||||||
if (pos.getPiece(7) == Piece.WROOK) validCastle |= (1 << Position.H1_CASTLE);
|
|
||||||
}
|
|
||||||
if (pos.getPiece(60) == Piece.BKING) {
|
|
||||||
if (pos.getPiece(56) == Piece.BROOK) validCastle |= (1 << Position.A8_CASTLE);
|
|
||||||
if (pos.getPiece(63) == Piece.BROOK) validCastle |= (1 << Position.H8_CASTLE);
|
|
||||||
}
|
|
||||||
castleMask &= validCastle;
|
|
||||||
pos.setCastleMask(castleMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Remove pseudo-legal EP square if it is not legal, ie would leave king in check. */
|
/** Remove pseudo-legal EP square if it is not legal, ie would leave king in check. */
|
||||||
public static void fixupEPSquare(Position pos) {
|
public static void fixupEPSquare(Position pos) {
|
||||||
int epSquare = pos.getEpSquare();
|
int epSquare = pos.getEpSquare();
|
||||||
@@ -226,91 +210,6 @@ public class TextIO {
|
|||||||
pos.setPiece(Position.getSquare(col, row), p);
|
pos.setPiece(Position.getSquare(col, row), p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a FEN string corresponding to a chess Position object. */
|
|
||||||
public static String toFEN(Position pos) {
|
|
||||||
StringBuilder ret = new StringBuilder();
|
|
||||||
// Piece placement
|
|
||||||
for (int r = 7; r >=0; r--) {
|
|
||||||
int numEmpty = 0;
|
|
||||||
for (int c = 0; c < 8; c++) {
|
|
||||||
int p = pos.getPiece(Position.getSquare(c, r));
|
|
||||||
if (p == Piece.EMPTY) {
|
|
||||||
numEmpty++;
|
|
||||||
} else {
|
|
||||||
if (numEmpty > 0) {
|
|
||||||
ret.append(numEmpty);
|
|
||||||
numEmpty = 0;
|
|
||||||
}
|
|
||||||
switch (p) {
|
|
||||||
case Piece.WKING: ret.append('K'); break;
|
|
||||||
case Piece.WQUEEN: ret.append('Q'); break;
|
|
||||||
case Piece.WROOK: ret.append('R'); break;
|
|
||||||
case Piece.WBISHOP: ret.append('B'); break;
|
|
||||||
case Piece.WKNIGHT: ret.append('N'); break;
|
|
||||||
case Piece.WPAWN: ret.append('P'); break;
|
|
||||||
case Piece.BKING: ret.append('k'); break;
|
|
||||||
case Piece.BQUEEN: ret.append('q'); break;
|
|
||||||
case Piece.BROOK: ret.append('r'); break;
|
|
||||||
case Piece.BBISHOP: ret.append('b'); break;
|
|
||||||
case Piece.BKNIGHT: ret.append('n'); break;
|
|
||||||
case Piece.BPAWN: ret.append('p'); break;
|
|
||||||
default: throw new RuntimeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (numEmpty > 0) {
|
|
||||||
ret.append(numEmpty);
|
|
||||||
}
|
|
||||||
if (r > 0) {
|
|
||||||
ret.append('/');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret.append(pos.whiteMove ? " w " : " b ");
|
|
||||||
|
|
||||||
// Castling rights
|
|
||||||
boolean anyCastle = false;
|
|
||||||
if (pos.h1Castle()) {
|
|
||||||
ret.append('K');
|
|
||||||
anyCastle = true;
|
|
||||||
}
|
|
||||||
if (pos.a1Castle()) {
|
|
||||||
ret.append('Q');
|
|
||||||
anyCastle = true;
|
|
||||||
}
|
|
||||||
if (pos.h8Castle()) {
|
|
||||||
ret.append('k');
|
|
||||||
anyCastle = true;
|
|
||||||
}
|
|
||||||
if (pos.a8Castle()) {
|
|
||||||
ret.append('q');
|
|
||||||
anyCastle = true;
|
|
||||||
}
|
|
||||||
if (!anyCastle) {
|
|
||||||
ret.append('-');
|
|
||||||
}
|
|
||||||
|
|
||||||
// En passant target square
|
|
||||||
{
|
|
||||||
ret.append(' ');
|
|
||||||
if (pos.getEpSquare() >= 0) {
|
|
||||||
int x = Position.getX(pos.getEpSquare());
|
|
||||||
int y = Position.getY(pos.getEpSquare());
|
|
||||||
ret.append((char)(x + 'a'));
|
|
||||||
ret.append((char)(y + '1'));
|
|
||||||
} else {
|
|
||||||
ret.append('-');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move counters
|
|
||||||
ret.append(' ');
|
|
||||||
ret.append(pos.halfMoveClock);
|
|
||||||
ret.append(' ');
|
|
||||||
ret.append(pos.fullMoveCounter);
|
|
||||||
|
|
||||||
return ret.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a chess move to human readable form.
|
* Convert a chess move to human readable form.
|
||||||
* @param pos The chess position.
|
* @param pos The chess position.
|
||||||
@@ -425,22 +324,6 @@ public class TextIO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Decide if move is valid in position pos.
|
|
||||||
* @param pos Position for which to test move.
|
|
||||||
* @param move The move to check for validity.
|
|
||||||
* @return True if move is valid in position pos, false otherwise.
|
|
||||||
*/
|
|
||||||
public static boolean isValid(Position pos, Move move) {
|
|
||||||
if (move == null)
|
|
||||||
return false;
|
|
||||||
ArrayList<Move> moves = new MoveGen().legalMoves(pos);
|
|
||||||
for (int i = 0; i < moves.size(); i++)
|
|
||||||
if (move.equals(moves.get(i)))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final static class MoveInfo {
|
private final static class MoveInfo {
|
||||||
int piece; // -1 for unspecified
|
int piece; // -1 for unspecified
|
||||||
int fromX, fromY, toX, toY; // -1 for unspecified
|
int fromX, fromY, toX, toY; // -1 for unspecified
|
||||||
@@ -580,82 +463,6 @@ public class TextIO {
|
|||||||
return move;
|
return move;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert a move object to UCI string format. */
|
|
||||||
public static String moveToUCIString(Move m) {
|
|
||||||
String ret = squareToString(m.from);
|
|
||||||
ret += squareToString(m.to);
|
|
||||||
switch (m.promoteTo) {
|
|
||||||
case Piece.WQUEEN:
|
|
||||||
case Piece.BQUEEN:
|
|
||||||
ret += "q";
|
|
||||||
break;
|
|
||||||
case Piece.WROOK:
|
|
||||||
case Piece.BROOK:
|
|
||||||
ret += "r";
|
|
||||||
break;
|
|
||||||
case Piece.WBISHOP:
|
|
||||||
case Piece.BBISHOP:
|
|
||||||
ret += "b";
|
|
||||||
break;
|
|
||||||
case Piece.WKNIGHT:
|
|
||||||
case Piece.BKNIGHT:
|
|
||||||
ret += "n";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a string in UCI move format to a Move object.
|
|
||||||
* @return A move object, or null if move has invalid syntax
|
|
||||||
*/
|
|
||||||
public static Move UCIstringToMove(String move) {
|
|
||||||
Move m = null;
|
|
||||||
if ((move.length() < 4) || (move.length() > 5))
|
|
||||||
return m;
|
|
||||||
int fromSq = TextIO.getSquare(move.substring(0, 2));
|
|
||||||
int toSq = TextIO.getSquare(move.substring(2, 4));
|
|
||||||
if ((fromSq < 0) || (toSq < 0)) {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
char prom = ' ';
|
|
||||||
boolean white = true;
|
|
||||||
if (move.length() == 5) {
|
|
||||||
prom = move.charAt(4);
|
|
||||||
if (Position.getY(toSq) == 7) {
|
|
||||||
white = true;
|
|
||||||
} else if (Position.getY(toSq) == 0) {
|
|
||||||
white = false;
|
|
||||||
} else {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int promoteTo;
|
|
||||||
switch (prom) {
|
|
||||||
case ' ':
|
|
||||||
promoteTo = Piece.EMPTY;
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
promoteTo = white ? Piece.WQUEEN : Piece.BQUEEN;
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
promoteTo = white ? Piece.WROOK : Piece.BROOK;
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
promoteTo = white ? Piece.WBISHOP : Piece.BBISHOP;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
promoteTo = white ? Piece.WKNIGHT : Piece.BKNIGHT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
m = new Move(fromSq, toSq, promoteTo);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a string, such as "e4" to a square number.
|
* Convert a string, such as "e4" to a square number.
|
||||||
* @return The square number, or -1 if not a legal square.
|
* @return The square number, or -1 if not a legal square.
|
||||||
@@ -668,18 +475,6 @@ public class TextIO {
|
|||||||
return Position.getSquare(x, y);
|
return Position.getSquare(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a square number to a string, such as "e4".
|
|
||||||
*/
|
|
||||||
public static String squareToString(int square) {
|
|
||||||
StringBuilder ret = new StringBuilder();
|
|
||||||
int x = Position.getX(square);
|
|
||||||
int y = Position.getY(square);
|
|
||||||
ret.append((char) (x + 'a'));
|
|
||||||
ret.append((char) (y + '1'));
|
|
||||||
return ret.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String pieceToChar(int p) {
|
private static String pieceToChar(int p) {
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case Piece.WQUEEN: case Piece.BQUEEN: return "Q";
|
case Piece.WQUEEN: case Piece.BQUEEN: return "Q";
|
||||||
@@ -702,18 +497,4 @@ public class TextIO {
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add an = sign to a promotion move, as required by the PGN standard. */
|
|
||||||
public static String pgnPromotion(String str) {
|
|
||||||
int idx = str.length() - 1;
|
|
||||||
while (idx > 0) {
|
|
||||||
char c = str.charAt(idx);
|
|
||||||
if ((c != '#') && (c != '+'))
|
|
||||||
break;
|
|
||||||
idx--;
|
|
||||||
}
|
|
||||||
if ((idx > 0) && (charToPiece(true, str.charAt(idx)) != -1))
|
|
||||||
idx--;
|
|
||||||
return str.substring(0, idx + 1) + '=' + str.substring(idx + 1, str.length());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,193 +0,0 @@
|
|||||||
/*
|
|
||||||
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 chess;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import chess.TimeControlData.TimeControlField;
|
|
||||||
|
|
||||||
/** Keep track of time control information for both players. */
|
|
||||||
public class TimeControl {
|
|
||||||
TimeControlData tcData;
|
|
||||||
|
|
||||||
private int whiteBaseTime; // Current remaining time, or remaining time when clock started
|
|
||||||
private int blackBaseTime; // Current remaining time, or remaining time when clock started
|
|
||||||
|
|
||||||
int currentMove;
|
|
||||||
boolean whiteToMove;
|
|
||||||
|
|
||||||
private int elapsed; // Accumulated elapsed time for this move.
|
|
||||||
private long timerT0; // Time when timer started. 0 if timer is stopped.
|
|
||||||
|
|
||||||
|
|
||||||
/** Constructor. Sets time control to "game in 5min". */
|
|
||||||
public TimeControl() {
|
|
||||||
tcData = new TimeControlData();
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void reset() {
|
|
||||||
currentMove = 1;
|
|
||||||
whiteToMove = true;
|
|
||||||
elapsed = 0;
|
|
||||||
timerT0 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set time controls for white and black players. */
|
|
||||||
public final void setTimeControl(TimeControlData tcData) {
|
|
||||||
this.tcData = tcData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setCurrentMove(int move, boolean whiteToMove, int whiteBaseTime, int blackBaseTime) {
|
|
||||||
currentMove = move;
|
|
||||||
this.whiteToMove = whiteToMove;
|
|
||||||
this.whiteBaseTime = whiteBaseTime;
|
|
||||||
this.blackBaseTime = blackBaseTime;
|
|
||||||
timerT0 = 0;
|
|
||||||
elapsed = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Move current move "delta" half-moves forward. */
|
|
||||||
public final void advanceMove(int delta) {
|
|
||||||
while (delta > 0) {
|
|
||||||
if (!whiteToMove)
|
|
||||||
currentMove++;
|
|
||||||
whiteToMove = !whiteToMove;
|
|
||||||
delta--;
|
|
||||||
}
|
|
||||||
while (delta < 0) {
|
|
||||||
whiteToMove = !whiteToMove;
|
|
||||||
if (!whiteToMove)
|
|
||||||
currentMove--;
|
|
||||||
delta++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean clockRunning() {
|
|
||||||
return timerT0 != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void startTimer(long now) {
|
|
||||||
if (!clockRunning()) {
|
|
||||||
timerT0 = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void stopTimer(long now) {
|
|
||||||
if (clockRunning()) {
|
|
||||||
int currElapsed = (int)(now - timerT0);
|
|
||||||
timerT0 = 0;
|
|
||||||
if (currElapsed > 0)
|
|
||||||
elapsed += currElapsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Compute new remaining time after a move is made. */
|
|
||||||
public final int moveMade(long now, boolean useIncrement) {
|
|
||||||
stopTimer(now);
|
|
||||||
|
|
||||||
ArrayList<TimeControlField> tc = tcData.getTC(whiteToMove);
|
|
||||||
Pair<Integer,Integer> tcInfo = getCurrentTC(whiteToMove);
|
|
||||||
int tcIdx = tcInfo.first;
|
|
||||||
int movesToTc = tcInfo.second;
|
|
||||||
|
|
||||||
int remaining = getRemainingTime(whiteToMove, now);
|
|
||||||
if (useIncrement) {
|
|
||||||
remaining += tc.get(tcIdx).increment;
|
|
||||||
if (movesToTc == 1) {
|
|
||||||
if (tcIdx+1 < tc.size())
|
|
||||||
tcIdx++;
|
|
||||||
remaining += tc.get(tcIdx).timeControl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elapsed = 0;
|
|
||||||
return remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get remaining time */
|
|
||||||
public final int getRemainingTime(boolean whiteToMove, long now) {
|
|
||||||
int remaining = whiteToMove ? whiteBaseTime : blackBaseTime;
|
|
||||||
if (whiteToMove == this.whiteToMove) {
|
|
||||||
remaining -= elapsed;
|
|
||||||
if (timerT0 != 0)
|
|
||||||
remaining -= now - timerT0;
|
|
||||||
}
|
|
||||||
return remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get initial thinking time in milliseconds. */
|
|
||||||
public final int getInitialTime(boolean whiteMove) {
|
|
||||||
ArrayList<TimeControlField> tc = tcData.getTC(whiteMove);
|
|
||||||
return tc.get(0).timeControl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get time increment in milliseconds after playing next move. */
|
|
||||||
public final int getIncrement(boolean whiteMove) {
|
|
||||||
ArrayList<TimeControlField> tc = tcData.getTC(whiteMove);
|
|
||||||
int tcIdx = getCurrentTC(whiteMove).first;
|
|
||||||
return tc.get(tcIdx).increment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return number of moves to the next time control, or 0 if "sudden death". */
|
|
||||||
public final int getMovesToTC(boolean whiteMove) {
|
|
||||||
return getCurrentTC(whiteMove).second;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return Array containing time control, moves per session and time increment. */
|
|
||||||
public int[] getTimeLimit(boolean whiteMove) {
|
|
||||||
ArrayList<TimeControlField> tc = tcData.getTC(whiteMove);
|
|
||||||
int tcIdx = getCurrentTC(whiteMove).first;
|
|
||||||
TimeControlField t = tc.get(tcIdx);
|
|
||||||
return new int[]{t.timeControl, t.movesPerSession, t.increment};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return the current active time control index and number of moves to next time control. */
|
|
||||||
private Pair<Integer,Integer> getCurrentTC(boolean whiteMove) {
|
|
||||||
ArrayList<TimeControlField> tc = tcData.getTC(whiteMove);
|
|
||||||
int tcIdx = 0;
|
|
||||||
final int lastTcIdx = tc.size() - 1;
|
|
||||||
int nextTC = 1;
|
|
||||||
int currMove = currentMove;
|
|
||||||
if (!whiteToMove && whiteMove)
|
|
||||||
currMove++;
|
|
||||||
while (true) {
|
|
||||||
if (tc.get(tcIdx).movesPerSession <= 0)
|
|
||||||
return new Pair<>(tcIdx, 0);
|
|
||||||
nextTC += tc.get(tcIdx).movesPerSession;
|
|
||||||
if (nextTC > currMove)
|
|
||||||
break;
|
|
||||||
if (tcIdx < lastTcIdx)
|
|
||||||
tcIdx++;
|
|
||||||
}
|
|
||||||
return new Pair<>(tcIdx, nextTC - currMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** De-serialize from input stream. */
|
|
||||||
public void readFromStream(DataInputStream dis, int version) throws IOException {
|
|
||||||
tcData.readFromStream(dis, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Serialize to output stream. */
|
|
||||||
public void writeToStream(DataOutputStream dos) throws IOException {
|
|
||||||
tcData.writeToStream(dos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
DroidFish - An Android chess program.
|
|
||||||
Copyright (C) 2013,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 chess;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public final class TimeControlData {
|
|
||||||
public static final class TimeControlField {
|
|
||||||
int timeControl; // Time in milliseconds
|
|
||||||
int movesPerSession;
|
|
||||||
int increment; // Increment in milliseconds
|
|
||||||
|
|
||||||
public TimeControlField(int time, int moves, int inc) {
|
|
||||||
timeControl = time;
|
|
||||||
movesPerSession = moves;
|
|
||||||
increment = inc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<TimeControlField> tcW, tcB;
|
|
||||||
|
|
||||||
/** Constructor. Set a default time control. */
|
|
||||||
public TimeControlData() {
|
|
||||||
tcW = new ArrayList<>();
|
|
||||||
tcW.add(new TimeControlField(5*60*1000, 60, 0));
|
|
||||||
tcB = new ArrayList<>();
|
|
||||||
tcB.add(new TimeControlField(5*60*1000, 60, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set a single time control for both white and black. */
|
|
||||||
public final void setTimeControl(int time, int moves, int inc) {
|
|
||||||
tcW = new ArrayList<>();
|
|
||||||
tcW.add(new TimeControlField(time, moves, inc));
|
|
||||||
tcB = new ArrayList<>();
|
|
||||||
tcB.add(new TimeControlField(time, moves, inc));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get time control data array for white or black player. */
|
|
||||||
public ArrayList<TimeControlField> getTC(boolean whiteMove) {
|
|
||||||
return whiteMove ? tcW : tcB;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return true if white and black time controls are equal. */
|
|
||||||
public boolean isSymmetric() {
|
|
||||||
return arrayEquals(tcW, tcB);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (!(o instanceof TimeControlData))
|
|
||||||
return false;
|
|
||||||
TimeControlData tc2 = (TimeControlData)o;
|
|
||||||
return arrayEquals(tcW, tc2.tcW) && arrayEquals(tcB, tc2.tcB);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean arrayEquals(ArrayList<TimeControlField> a1,
|
|
||||||
ArrayList<TimeControlField> a2) {
|
|
||||||
if (a1.size() != a2.size())
|
|
||||||
return false;
|
|
||||||
for (int i = 0; i < a1.size(); i++) {
|
|
||||||
TimeControlField f1 = a1.get(i);
|
|
||||||
TimeControlField f2 = a2.get(i);
|
|
||||||
if ((f1.timeControl != f2.timeControl) ||
|
|
||||||
(f1.movesPerSession != f2.movesPerSession) ||
|
|
||||||
(f1.increment != f2.increment))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** De-serialize from input stream. */
|
|
||||||
public void readFromStream(DataInputStream dis, int version) throws IOException {
|
|
||||||
for (int c = 0; c < 2; c++) {
|
|
||||||
ArrayList<TimeControlField> tc = new ArrayList<>();
|
|
||||||
if (c == 0)
|
|
||||||
tcW = tc;
|
|
||||||
else
|
|
||||||
tcB = tc;
|
|
||||||
int nw = dis.readInt();
|
|
||||||
for (int i = 0; i < nw; i++) {
|
|
||||||
int time = dis.readInt();
|
|
||||||
int moves = dis.readInt();
|
|
||||||
int inc = dis.readInt();
|
|
||||||
tc.add(new TimeControlField(time, moves, inc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Serialize to output stream. */
|
|
||||||
public void writeToStream(DataOutputStream dos) throws IOException {
|
|
||||||
for (int c = 0; c < 2; c++) {
|
|
||||||
ArrayList<TimeControlField> tc = (c == 0) ? tcW : tcB;
|
|
||||||
int nw = tc.size();
|
|
||||||
dos.writeInt(nw);
|
|
||||||
for (int i = 0; i < nw; i++) {
|
|
||||||
TimeControlField tcf = tc.get(i);
|
|
||||||
dos.writeInt(tcf.timeControl);
|
|
||||||
dos.writeInt(tcf.movesPerSession);
|
|
||||||
dos.writeInt(tcf.increment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user