From 4230847224b2cd39c3fa45b26f2a2d845d5d788d Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Sun, 11 Aug 2019 11:40:31 +0200 Subject: [PATCH] Remove unneeded code from buildSrc --- buildSrc/src/main/java/chess/BitBoard.java | 188 --- buildSrc/src/main/java/chess/Book.java | 157 --- buildSrc/src/main/java/chess/EcoBuilder.java | 6 +- buildSrc/src/main/java/chess/Game.java | 538 -------- buildSrc/src/main/java/chess/GameTree.java | 1099 +---------------- buildSrc/src/main/java/chess/Move.java | 45 - buildSrc/src/main/java/chess/PGNOptions.java | 65 - buildSrc/src/main/java/chess/PgnToken.java | 15 - buildSrc/src/main/java/chess/Piece.java | 6 - buildSrc/src/main/java/chess/Position.java | 120 -- buildSrc/src/main/java/chess/TextIO.java | 219 ---- buildSrc/src/main/java/chess/TimeControl.java | 193 --- .../src/main/java/chess/TimeControlData.java | 122 -- 13 files changed, 23 insertions(+), 2750 deletions(-) delete mode 100644 buildSrc/src/main/java/chess/Game.java delete mode 100644 buildSrc/src/main/java/chess/PGNOptions.java delete mode 100644 buildSrc/src/main/java/chess/TimeControl.java delete mode 100644 buildSrc/src/main/java/chess/TimeControlData.java diff --git a/buildSrc/src/main/java/chess/BitBoard.java b/buildSrc/src/main/java/chess/BitBoard.java index b71c418..c4a30a3 100644 --- a/buildSrc/src/main/java/chess/BitBoard.java +++ b/buildSrc/src/main/java/chess/BitBoard.java @@ -20,99 +20,13 @@ package chess; 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 maskBToHFiles = 0xFEFEFEFEFEFEFEFEL; public static final long maskAToFFiles = 0x3F3F3F3F3F3F3F3FL; 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; - 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[] rMasks; 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) { 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)]; - } } diff --git a/buildSrc/src/main/java/chess/Book.java b/buildSrc/src/main/java/chess/Book.java index 95e2bcf..d2444e5 100644 --- a/buildSrc/src/main/java/chess/Book.java +++ b/buildSrc/src/main/java/chess/Book.java @@ -34,153 +34,6 @@ import java.util.Random; /** Implements an opening book. */ public class Book { - public static class BookEntry { - Move move; - int count; - BookEntry(Move move) { - this.move = move; - count = 1; - } - } - private static Map> 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 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 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 bookMoves = bookMap.get(pos.zobristHash()); - if (bookMoves == null) { - return null; - } - - ArrayList 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 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. */ public static void main(String[] args) throws IOException { String inFile = args[0]; @@ -265,14 +118,4 @@ public class Book { 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; - } - } } diff --git a/buildSrc/src/main/java/chess/EcoBuilder.java b/buildSrc/src/main/java/chess/EcoBuilder.java index 290908d..af32e18 100644 --- a/buildSrc/src/main/java/chess/EcoBuilder.java +++ b/buildSrc/src/main/java/chess/EcoBuilder.java @@ -87,13 +87,11 @@ public class EcoBuilder { private void readGame(String pgn) throws Throwable { if (pgn.isEmpty()) return; - Game game = new Game(null, new TimeControlData()); - PGNOptions options = new PGNOptions(); - game.readPGN(pgn, options); + GameTree tree = new GameTree(); + tree.readPGN(pgn); // Determine name of opening HashMap headers = new HashMap<>(); - GameTree tree = game.tree; tree.getHeaders(headers); int ecoIdx = addData(headers, "ECO"); int opnIdx = addData(headers, "Opening"); diff --git a/buildSrc/src/main/java/chess/Game.java b/buildSrc/src/main/java/chess/Game.java deleted file mode 100644 index 73a0685..0000000 --- a/buildSrc/src/main/java/chess/Game.java +++ /dev/null @@ -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 . -*/ - -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 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 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 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> getUCIHistory() { - Pair, Integer> ml = tree.getMoveList(); - List moveList = ml.first; - Position pos = new Position(tree.startPos); - ArrayList 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, Integer> ml = tree.getMoveList(); - List 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; - } -} diff --git a/buildSrc/src/main/java/chess/GameTree.java b/buildSrc/src/main/java/chess/GameTree.java index 80f2eb3..66822ff 100644 --- a/buildSrc/src/main/java/chess/GameTree.java +++ b/buildSrc/src/main/java/chess/GameTree.java @@ -22,24 +22,16 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collections; -import java.util.GregorianCalendar; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Map.Entry; -import chess.Game.GameState; -import chess.TimeControlData.TimeControlField; - public class GameTree { // Data from the seven tag roster (STR) part of the PGN standard - String event, site, date, round, white, black; - // Result is the last tag pair in the STR, but it is computed on demand from the game tree. + String event, site, date, round, white, black, result; public Position startPos; - private String timeControl, whiteTimeControl, blackTimeControl; // Non-standard tags static private final class TagPair { @@ -52,226 +44,30 @@ public class GameTree { public Node currentNode; public Position currentPos; // Cached value. Computable from "currentNode". - private final PgnToken.PgnTokenReceiver gameStateListener; - - /** Creates an empty GameTree starting at the standard start position. - * @param gameStateListener Optional tree change listener. - */ - public GameTree(PgnToken.PgnTokenReceiver gameStateListener) { - this.gameStateListener = gameStateListener; + /** Creates an empty GameTree starting at the standard start position. */ + public GameTree() { try { setStartPos(TextIO.readFEN(TextIO.startPosFEN)); } catch (ChessParseError e) { } } - final void setPlayerNames(String white, String black) { - this.white = white; - this.black = black; - updateListener(); - } - /** Set start position. Drops the whole game tree. */ final void setStartPos(Position pos) { event = "?"; site = "?"; - { - Calendar now = GregorianCalendar.getInstance(); - int year = now.get(Calendar.YEAR); - int month = now.get(Calendar.MONTH) + 1; - int day = now.get(Calendar.DAY_OF_MONTH); - date = String.format(Locale.US, "%04d.%02d.%02d", year, month, day); - } + date = "????.??.??"; round = "?"; white = "?"; black = "?"; startPos = pos; - timeControl = "?"; - whiteTimeControl = "?"; - blackTimeControl = "?"; tagPairs = new ArrayList<>(); rootNode = new Node(); currentNode = rootNode; currentPos = new Position(startPos); - updateListener(); } - private void updateListener() { - if (gameStateListener != null) - gameStateListener.clear(); - } - - /** PgnTokenReceiver implementation that generates plain text PGN data. */ - private static class PgnText implements PgnToken.PgnTokenReceiver { - private StringBuilder sb = new StringBuilder(256); - private String header = ""; - private int prevType = PgnToken.EOF; - - final String getPgnString() { - StringBuilder ret = new StringBuilder(4096); - ret.append(header); - ret.append('\n'); - - String[] words = sb.toString().split(" "); - int currLineLength = 0; - final int arrLen = words.length; - for (int i = 0; i < arrLen; i++) { - String word = words[i].trim(); - int wordLen = word.length(); - if (wordLen > 0) { - if (currLineLength == 0) { - ret.append(word); - currLineLength = wordLen; - } else if (currLineLength + 1 + wordLen >= 80) { - ret.append('\n'); - ret.append(word); - currLineLength = wordLen; - } else { - ret.append(' '); - currLineLength++; - ret.append(word); - currLineLength += wordLen; - } - } - } - ret.append("\n\n"); - return ret.toString(); - } - - @Override - public void processToken(Node node, int type, String token) { - if ( (prevType == PgnToken.RIGHT_BRACKET) && - (type != PgnToken.LEFT_BRACKET)) { - header = sb.toString(); - sb = new StringBuilder(4096); - } - switch (type) { - case PgnToken.STRING: { - sb.append(" \""); - int len = token.length(); - for (int i = 0; i < len; i++) { - char c = token.charAt(i); - if ((c == '\\') || (c == '"')) { - sb.append('\\'); - } - sb.append(c); - } - sb.append("\""); - break; - } - case PgnToken.INTEGER: - if ( (prevType != PgnToken.LEFT_PAREN) && - (prevType != PgnToken.RIGHT_BRACKET)) - sb.append(' '); - sb.append(token); - break; - case PgnToken.PERIOD: - sb.append('.'); - break; - case PgnToken.ASTERISK: - sb.append(" *"); - break; - case PgnToken.LEFT_BRACKET: - sb.append('['); - break; - case PgnToken.RIGHT_BRACKET: - sb.append("]\n"); - break; - case PgnToken.LEFT_PAREN: - sb.append(" ("); - break; - case PgnToken.RIGHT_PAREN: - sb.append(')'); - break; - case PgnToken.NAG: - sb.append(" $"); - sb.append(token); - break; - case PgnToken.SYMBOL: - if ((prevType != PgnToken.RIGHT_BRACKET) && (prevType != PgnToken.LEFT_BRACKET)) - sb.append(' '); - sb.append(token); - break; - case PgnToken.COMMENT: - if ( (prevType != PgnToken.LEFT_PAREN) && - (prevType != PgnToken.RIGHT_BRACKET)) - sb.append(' '); - sb.append('{'); - sb.append(token); - sb.append('}'); - break; - case PgnToken.EOF: - break; - } - prevType = type; - } - - @Override - public boolean isUpToDate() { - return true; - } - @Override - public void clear() { - } - @Override - public void setCurrent(Node node) { - } - } - - /** Export game tree in PGN format. */ - public final String toPGN(PGNOptions options) { - PgnText pgnText = new PgnText(); - options.exp.pgnPromotions = true; - options.exp.pieceType = PGNOptions.PT_ENGLISH; - pgnTreeWalker(options, pgnText); - return pgnText.getPgnString(); - } - - /** Walks the game tree in PGN export order. */ - public final void pgnTreeWalker(PGNOptions options, PgnToken.PgnTokenReceiver out) { - String pgnResultString = getPGNResultStringMainLine(); - - // Write seven tag roster - addTagPair(out, "Event", event); - addTagPair(out, "Site", site); - addTagPair(out, "Date", date); - addTagPair(out, "Round", round); - addTagPair(out, "White", white); - addTagPair(out, "Black", black); - addTagPair(out, "Result", pgnResultString); - - // Write special tag pairs - String fen = TextIO.toFEN(startPos); - if (!fen.equals(TextIO.startPosFEN)) { - addTagPair(out, "FEN", fen); - addTagPair(out, "SetUp", "1"); - } - if (!timeControl.equals("?")) - addTagPair(out, "TimeControl", timeControl); - if (!whiteTimeControl.equals("?")) - addTagPair(out, "WhiteTimeControl", whiteTimeControl); - if (!blackTimeControl.equals("?")) - addTagPair(out, "BlackTimeControl", blackTimeControl); - - // Write other non-standard tag pairs - for (int i = 0; i < tagPairs.size(); i++) - addTagPair(out, tagPairs.get(i).tagName, tagPairs.get(i).tagValue); - - // Write moveText section - MoveNumber mn = new MoveNumber(startPos.fullMoveCounter, startPos.whiteMove); - Node.addPgnData(out, rootNode, mn.prev(), options); - out.processToken(null, PgnToken.SYMBOL, pgnResultString); - out.processToken(null, PgnToken.EOF, null); - } - - private void addTagPair(PgnToken.PgnTokenReceiver out, String tagName, String tagValue) { - out.processToken(null, PgnToken.LEFT_BRACKET, null); - out.processToken(null, PgnToken.SYMBOL, tagName); - out.processToken(null, PgnToken.STRING, tagValue); - out.processToken(null, PgnToken.RIGHT_BRACKET, null); - } - - final static class PgnScanner { + final static private class PgnScanner { String data; int idx; List savedTokens; @@ -423,7 +219,7 @@ public class GameTree { } /** Import PGN data. */ - public final boolean readPGN(String pgn, PGNOptions options) throws ChessParseError { + public final boolean readPGN(String pgn) throws ChessParseError { PgnScanner scanner = new PgnScanner(pgn); PgnToken tok = scanner.nextToken(); @@ -463,7 +259,7 @@ public class GameTree { // Parse move section Node gameRoot = new Node(); - Node.parsePgn(scanner, gameRoot, options); + Node.parsePgn(scanner, gameRoot); if (tagPairs.size() == 0) { gameRoot.verifyChildren(TextIO.readFEN(TextIO.startPosFEN)); @@ -481,7 +277,7 @@ public class GameTree { } setStartPos(TextIO.readFEN(fen)); - String result = ""; + result = ""; for (int i = 0; i < nTags; i++) { String name = tagPairs.get(i).tagName; String val = tagPairs.get(i).tagValue; @@ -501,12 +297,6 @@ public class GameTree { black = val; } else if (name.equals("Result")) { result = val; - } else if (name.equals("TimeControl")) { - timeControl = val; - } else if (name.equals("WhiteTimeControl")) { - whiteTimeControl = val; - } else if (name.equals("BlackTimeControl")) { - blackTimeControl = val; } else { this.tagPairs.add(tagPairs.get(i)); } @@ -515,107 +305,9 @@ public class GameTree { rootNode = gameRoot; currentNode = rootNode; - // If result indicated draw by agreement or a resigned game, - // add that info to the game tree. - { - // Go to end of mainline - while (variations().size() > 0) - goForward(0); - GameState state = getGameState(); - if (state == GameState.ALIVE) - addResult(result); - // Go back to the root - while (currentNode != rootNode) - goBack(); - } - - updateListener(); return true; } - /** Add game result to the tree. currentNode must be at the end of the main line. */ - private void addResult(String result) { - if (result.equals("1-0")) { - if (currentPos.whiteMove) { - currentNode.playerAction = "resign"; - } else { - addMove("--", "resign", 0, "", ""); - } - } else if (result.equals("0-1")) { - if (!currentPos.whiteMove) { - currentNode.playerAction = "resign"; - } else { - addMove("--", "resign", 0, "", ""); - } - } else if (result.equals("1/2-1/2") || result.equals("1/2")) { - currentNode.playerAction = "draw offer"; - addMove("--", "draw accept", 0, "", ""); - } - } - - /** Serialize to output stream. */ - public final void writeToStream(DataOutputStream dos) throws IOException { - dos.writeUTF(event); - dos.writeUTF(site); - dos.writeUTF(date); - dos.writeUTF(round); - dos.writeUTF(white); - dos.writeUTF(black); - dos.writeUTF(TextIO.toFEN(startPos)); - dos.writeUTF(timeControl); - dos.writeUTF(whiteTimeControl); - dos.writeUTF(blackTimeControl); - int nTags = tagPairs.size(); - dos.writeInt(nTags); - for (int i = 0; i < nTags; i++) { - dos.writeUTF(tagPairs.get(i).tagName); - dos.writeUTF(tagPairs.get(i).tagValue); - } - Node.writeToStream(dos, rootNode); - ArrayList pathFromRoot = currentNode.getPathFromRoot(); - int pathLen = pathFromRoot.size(); - dos.writeInt(pathLen); - for (int i = 0; i < pathLen; i++) - dos.writeInt(pathFromRoot.get(i)); - } - - /** De-serialize from input stream. */ - public final void readFromStream(DataInputStream dis, int version) throws IOException, ChessParseError { - event = dis.readUTF(); - site = dis.readUTF(); - date = dis.readUTF(); - round = dis.readUTF(); - white = dis.readUTF(); - black = dis.readUTF(); - startPos = TextIO.readFEN(dis.readUTF()); - currentPos = new Position(startPos); - timeControl = dis.readUTF(); - if (version >= 2) { - whiteTimeControl = dis.readUTF(); - blackTimeControl = dis.readUTF(); - } else { - whiteTimeControl = "?"; - blackTimeControl = "?"; - } - int nTags = dis.readInt(); - tagPairs.clear(); - for (int i = 0; i < nTags; i++) { - TagPair tp = new TagPair(); - tp.tagName = dis.readUTF(); - tp.tagValue = dis.readUTF(); - tagPairs.add(tp); - } - rootNode = new Node(); - Node.readFromStream(dis, rootNode); - currentNode = rootNode; - int pathLen = dis.readInt(); - for (int i = 0; i < pathLen; i++) - goForward(dis.readInt()); - - updateListener(); - } - - /** Go backward in game tree. */ public final void goBack() { if (currentNode.parent != null) { @@ -628,18 +320,13 @@ public class GameTree { * @param variation Which variation to follow. -1 to follow default variation. */ public final void goForward(int variation) { - goForward(variation, true); - } - public final void goForward(int variation, boolean updateDefault) { - if (currentNode.verifyChildren(currentPos)) - updateListener(); + currentNode.verifyChildren(currentPos); if (variation < 0) variation = currentNode.defaultChild; int numChildren = currentNode.children.size(); if (variation >= numChildren) variation = 0; - if (updateDefault) - currentNode.defaultChild = variation; + currentNode.defaultChild = variation; if (numChildren > 0) { currentNode = currentNode.children.get(variation); currentPos.makeMove(currentNode.move, currentNode.ui); @@ -647,295 +334,27 @@ public class GameTree { } } - /** Go to given node in game tree. - * @return True if current node changed, false otherwise. */ - public final boolean goNode(Node node) { - if (node == currentNode) - return false; - ArrayList path = node.getPathFromRoot(); - while (currentNode != rootNode) - goBack(); - for (Integer c : path) - goForward(c); - return true; - } - /** List of possible continuation moves. */ public final ArrayList variations() { - if (currentNode.verifyChildren(currentPos)) - updateListener(); + currentNode.verifyChildren(currentPos); ArrayList ret = new ArrayList<>(); for (Node child : currentNode.children) ret.add(child.move); return ret; } - /** Add a move last in the list of variations. - * @return Move number in variations list. -1 if moveStr is not a valid move - */ - public final int addMove(String moveStr, String playerAction, int nag, String preComment, String postComment) { - if (currentNode.verifyChildren(currentPos)) - updateListener(); - int idx = currentNode.children.size(); - Node node = new Node(currentNode, moveStr, playerAction, Integer.MIN_VALUE, nag, preComment, postComment); - Move move = TextIO.UCIstringToMove(moveStr); - ArrayList moves = null; - if (move == null) { - moves = MoveGen.instance.legalMoves(currentPos); - move = TextIO.stringToMove(currentPos, moveStr, moves); - } - if (move == null) - return -1; - if (moves == null) - moves = MoveGen.instance.legalMoves(currentPos); - node.moveStr = TextIO.moveToString(currentPos, move, false, moves); - node.move = move; - node.ui = new UndoInfo(); - currentNode.children.add(node); - updateListener(); - return idx; - } - - /** Move a variation in the ordered list of variations. */ - public final void reorderVariation(int varNo, int newPos) { - if (currentNode.verifyChildren(currentPos)) - updateListener(); - int nChild = currentNode.children.size(); - if ((varNo < 0) || (varNo >= nChild) || (newPos < 0) || (newPos >= nChild)) - return; - Node var = currentNode.children.get(varNo); - currentNode.children.remove(varNo); - currentNode.children.add(newPos, var); - - int newDef = currentNode.defaultChild; - if (varNo == newDef) { - newDef = newPos; - } else { - if (varNo < newDef) newDef--; - if (newPos <= newDef) newDef++; - } - currentNode.defaultChild = newDef; - updateListener(); - } - - /** Delete a variation. */ - public final void deleteVariation(int varNo) { - if (currentNode.verifyChildren(currentPos)) - updateListener(); - int nChild = currentNode.children.size(); - if ((varNo < 0) || (varNo >= nChild)) - return; - currentNode.children.remove(varNo); - if (varNo == currentNode.defaultChild) { - currentNode.defaultChild = 0; - } else if (varNo < currentNode.defaultChild) { - currentNode.defaultChild--; - } - updateListener(); - } - - /** Get linear game history, using default variations at branch points. */ - public final Pair, Integer> getMoveList() { - List ret = new ArrayList<>(); - Node node = currentNode; - while (node != rootNode) { - ret.add(node); - node = node.parent; - } - Collections.reverse(ret); - int numMovesPlayed = ret.size(); - node = currentNode; - Position pos = new Position(currentPos); - UndoInfo ui = new UndoInfo(); - boolean changed = false; - while (true) { - if (node.verifyChildren(pos)) - changed = true; - if (node.defaultChild >= node.children.size()) - break; - Node child = node.children.get(node.defaultChild); - ret.add(child); - pos.makeMove(child.move, ui); - node = child; - } - if (changed) - updateListener(); - return new Pair<>(ret, numMovesPlayed); - } - - final void setRemainingTime(int remaining) { - currentNode.remainingTime = remaining; - } - - final int getRemainingTime(boolean whiteMove, int initialTime) { - final int undef = Integer.MIN_VALUE; - int remainingTime = undef; - Node node = currentNode; - boolean wtm = currentPos.whiteMove; - while (true) { - if (wtm != whiteMove) { // If wtm in current mode, black made last move - remainingTime = node.remainingTime; - if (remainingTime != undef) - break; - } - Node parent = node.parent; - if (parent == null) - break; - wtm = !wtm; - node = parent; - } - if (remainingTime == undef) { - remainingTime = initialTime; - } - return remainingTime; - } - - final GameState getGameState() { - Position pos = currentPos; - String action = currentNode.playerAction; - if (action.equals("resign")) { - // Player made null move to resign, causing whiteMove to toggle - return pos.whiteMove ? GameState.RESIGN_BLACK : GameState.RESIGN_WHITE; - } - ArrayList moves = new MoveGen().legalMoves(pos); - if (moves.size() == 0) { - if (MoveGen.inCheck(pos)) { - return pos.whiteMove ? GameState.BLACK_MATE : GameState.WHITE_MATE; - } else { - return pos.whiteMove ? GameState.WHITE_STALEMATE : GameState.BLACK_STALEMATE; - } - } - if (insufficientMaterial(pos)) { - return GameState.DRAW_NO_MATE; - } - - if (action.startsWith("draw accept")) { - return GameState.DRAW_AGREE; - } - if (action.startsWith("draw rep")) { - return GameState.DRAW_REP; - } - if (action.startsWith("draw 50")) { - return GameState.DRAW_50; - } - return GameState.ALIVE; - } - - /** Get PGN result string corresponding to the current position. */ - public final String getPGNResultString() { - String gameResult = "*"; - switch (getGameState()) { - case ALIVE: - break; - case WHITE_MATE: - case RESIGN_BLACK: - gameResult = "1-0"; - break; - case BLACK_MATE: - case RESIGN_WHITE: - gameResult = "0-1"; - break; - case WHITE_STALEMATE: - case BLACK_STALEMATE: - case DRAW_REP: - case DRAW_50: - case DRAW_NO_MATE: - case DRAW_AGREE: - gameResult = "1/2-1/2"; - break; - } - return gameResult; - } - - /** Evaluate PGN result string at the end of the main line. */ - public final String getPGNResultStringMainLine() { - List currPath = new ArrayList<>(); - while (currentNode != rootNode) { - Node child = currentNode; - goBack(); - int childNum = currentNode.children.indexOf(child); - currPath.add(childNum); - } - while (variations().size() > 0) - goForward(0, false); - String res = getPGNResultString(); - while (currentNode != rootNode) - goBack(); - for (int i = currPath.size() - 1; i >= 0; i--) - goForward(currPath.get(i), false); - return res; - } - - private static boolean insufficientMaterial(Position pos) { - if (pos.nPieces(Piece.WQUEEN) > 0) return false; - if (pos.nPieces(Piece.WROOK) > 0) return false; - if (pos.nPieces(Piece.WPAWN) > 0) return false; - if (pos.nPieces(Piece.BQUEEN) > 0) return false; - if (pos.nPieces(Piece.BROOK) > 0) return false; - if (pos.nPieces(Piece.BPAWN) > 0) return false; - int wb = pos.nPieces(Piece.WBISHOP); - int wn = pos.nPieces(Piece.WKNIGHT); - int bb = pos.nPieces(Piece.BBISHOP); - int bn = pos.nPieces(Piece.BKNIGHT); - if (wb + wn + bb + bn <= 1) { - return true; // King + bishop/knight vs king is draw - } - if (wn + bn == 0) { - // Only bishops. If they are all on the same color, the position is a draw. - boolean bSquare = false; - boolean wSquare = false; - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - int p = pos.getPiece(Position.getSquare(x, y)); - if ((p == Piece.BBISHOP) || (p == Piece.WBISHOP)) { - if (Position.darkSquare(x, y)) { - bSquare = true; - } else { - wSquare = true; - } - } - } - } - if (!bSquare || !wSquare) { - return true; - } - } - return false; - } - - - /** Keep track of current move and side to move. Used for move number printing. */ - private static final class MoveNumber { - final int moveNo; - final boolean wtm; // White to move - MoveNumber(int moveNo, boolean wtm) { - this.moveNo = moveNo; - this.wtm = wtm; - } - public final MoveNumber next() { - if (wtm) return new MoveNumber(moveNo, false); - else return new MoveNumber(moveNo + 1, true); - } - public final MoveNumber prev() { - if (wtm) return new MoveNumber(moveNo - 1, false); - else return new MoveNumber(moveNo, true); - } - } - /** * A node object represents a position in the game tree. * The position is defined by the move that leads to the position from the parent position. * The root node is special in that it doesn't have a move. */ - public static class Node { + private static class Node { String moveStr; // String representation of move leading to this node. Empty string in root node. public Move move; // Computed on demand for better PGN parsing performance. // Subtrees of invalid moves will be dropped when detected. // Always valid for current node. private UndoInfo ui; // Computed when move is computed - String playerAction; // Player action. Draw claim/offer/accept or resign. - int remainingTime; // Remaining time in ms for side that played moveStr, or INT_MIN if unknown. int nag; // Numeric annotation glyph String preComment; // Comment before move String postComment; // Comment after move @@ -948,8 +367,6 @@ public class GameTree { this.moveStr = ""; this.move = null; this.ui = null; - this.playerAction = ""; - this.remainingTime = Integer.MIN_VALUE; this.parent = null; this.children = new ArrayList<>(); this.defaultChild = 0; @@ -958,21 +375,6 @@ public class GameTree { this.postComment = ""; } - public Node(Node parent, String moveStr, String playerAction, int remainingTime, int nag, - String preComment, String postComment) { - this.moveStr = moveStr; - this.move = null; - this.ui = null; - this.playerAction = playerAction; - this.remainingTime = remainingTime; - this.parent = parent; - this.children = new ArrayList<>(); - this.defaultChild = 0; - this.nag = nag; - this.preComment = preComment; - this.postComment = postComment; - } - public Node getParent() { return parent; } @@ -1027,171 +429,13 @@ public class GameTree { throw new RuntimeException(); } - static void writeToStream(DataOutputStream dos, Node node) throws IOException { - while (true) { - dos.writeUTF(node.moveStr); - if (node.move != null) { - dos.writeByte(node.move.from); - dos.writeByte(node.move.to); - dos.writeByte(node.move.promoteTo); - } else { - dos.writeByte(-1); - } - dos.writeUTF(node.playerAction); - dos.writeInt(node.remainingTime); - dos.writeInt(node.nag); - dos.writeUTF(node.preComment); - dos.writeUTF(node.postComment); - dos.writeInt(node.defaultChild); - int nChildren = node.children.size(); - dos.writeInt(nChildren); - if (nChildren == 0) - break; - for (int i = 1; i < nChildren; i++) { - writeToStream(dos, node.children.get(i)); - } - node = node.children.get(0); - } - } - - static void readFromStream(DataInputStream dis, Node node) throws IOException { - while (true) { - node.moveStr = dis.readUTF(); - int from = dis.readByte(); - if (from >= 0) { - int to = dis.readByte(); - int prom = dis.readByte(); - node.move = new Move(from, to, prom); - node.ui = new UndoInfo(); - } - node.playerAction = dis.readUTF(); - node.remainingTime = dis.readInt(); - node.nag = dis.readInt(); - node.preComment = dis.readUTF(); - node.postComment = dis.readUTF(); - node.defaultChild = dis.readInt(); - int nChildren = dis.readInt(); - if (nChildren == 0) - break; - for (int i = 1; i < nChildren; i++) { - Node child = new Node(); - child.parent = node; - readFromStream(dis, child); - node.children.add(child); - } - Node child = new Node(); - child.parent = node; - node.children.add(0, child); - node = child; - } - } - - /** Export whole tree rooted at "node" in PGN format. */ - public static void addPgnData(PgnToken.PgnTokenReceiver out, Node node, - MoveNumber moveNum, PGNOptions options) { - boolean needMoveNr = node.addPgnDataOneNode(out, moveNum, true, options); - while (true) { - int nChild = node.children.size(); - if (nChild == 0) - break; - MoveNumber nextMN = moveNum.next(); - needMoveNr = node.children.get(0).addPgnDataOneNode(out, nextMN, needMoveNr, options); - if (options.exp.variations) { - for (int i = 1; i < nChild; i++) { - out.processToken(node, PgnToken.LEFT_PAREN, null); - addPgnData(out, node.children.get(i), nextMN, options); - out.processToken(node, PgnToken.RIGHT_PAREN, null); - needMoveNr = true; - } - } - node = node.children.get(0); - moveNum = moveNum.next(); - } - } - - /** Export this node in PGN (or display text) format. */ - private boolean addPgnDataOneNode(PgnToken.PgnTokenReceiver out, MoveNumber mn, - boolean needMoveNr, PGNOptions options) { - if ((preComment.length() > 0) && options.exp.comments) { - out.processToken(this, PgnToken.COMMENT, preComment); - needMoveNr = true; - } - if (moveStr.length() > 0) { - boolean nullSkip = moveStr.equals("--") && (playerAction.length() > 0) && !options.exp.playerAction; - if (!nullSkip) { - if (mn.wtm) { - out.processToken(this, PgnToken.INTEGER, Integer.valueOf(mn.moveNo).toString()); - out.processToken(this, PgnToken.PERIOD, null); - } else { - if (needMoveNr) { - out.processToken(this, PgnToken.INTEGER, Integer.valueOf(mn.moveNo).toString()); - for (int i = 0; i < 3; i++) - out.processToken(this, PgnToken.PERIOD, null); - } - } - String str = moveStr; - if (options.exp.pgnPromotions && (move != null) && (move.promoteTo != Piece.EMPTY)) - str = TextIO.pgnPromotion(str); - out.processToken(this, PgnToken.SYMBOL, str); - needMoveNr = false; - } - } - if ((nag > 0) && options.exp.nag) { - out.processToken(this, PgnToken.NAG, Integer.valueOf(nag).toString()); - if (options.exp.moveNrAfterNag) - needMoveNr = true; - } - if ((postComment.length() > 0) && options.exp.comments) { - out.processToken(this, PgnToken.COMMENT, postComment); - needMoveNr = true; - } - if ((playerAction.length() > 0) && options.exp.playerAction) { - addExtendedInfo(out, "playeraction", playerAction); - needMoveNr = true; - } - if ((remainingTime != Integer.MIN_VALUE) && options.exp.clockInfo) { - addExtendedInfo(out, "clk", getTimeStr(remainingTime)); - needMoveNr = true; - } - return needMoveNr; - } - - private void addExtendedInfo(PgnToken.PgnTokenReceiver out, - String extCmd, String extData) { - out.processToken(this, PgnToken.COMMENT, "[%" + extCmd + " " + extData + "]"); - } - - private static String getTimeStr(int remainingTime) { - int secs = (int)Math.floor((remainingTime + 999) / 1000.0); - boolean neg = false; - if (secs < 0) { - neg = true; - secs = -secs; - } - int mins = secs / 60; - secs -= mins * 60; - int hours = mins / 60; - mins -= hours * 60; - StringBuilder ret = new StringBuilder(); - if (neg) ret.append('-'); - if (hours < 10) ret.append('0'); - ret.append(hours); - ret.append(':'); - if (mins < 10) ret.append('0'); - ret.append(mins); - ret.append(':'); - if (secs < 10) ret.append('0'); - ret.append(secs); - return ret.toString(); - } - private Node addChild(Node child) { child.parent = this; children.add(child); return child; } - public static void parsePgn(PgnScanner scanner, Node node, PGNOptions options) { + public static void parsePgn(PgnScanner scanner, Node node) { Node nodeToAdd = new Node(); boolean moveAdded = false; while (true) { @@ -1206,8 +450,8 @@ public class GameTree { nodeToAdd = new Node(); moveAdded = false; } - if ((node.parent != null) && options.imp.variations) { - parsePgn(scanner, node.parent, options); + if (node.parent != null) { + parsePgn(scanner, node.parent); } else { int nestLevel = 1; while (nestLevel > 0) { @@ -1220,7 +464,7 @@ public class GameTree { } break; case PgnToken.NAG: - if (moveAdded && options.imp.nag) { // NAG must be after move + if (moveAdded) { // NAG must be after move try { nodeToAdd.nag = Integer.parseInt(tok.token); } catch (NumberFormatException e) { @@ -1268,31 +512,10 @@ public class GameTree { } break; case PgnToken.COMMENT: - try { - while (true) { - Pair ret = extractExtInfo(tok.token, "clk"); - tok.token = ret.first; - String cmdPars = ret.second; - if (cmdPars == null) - break; - nodeToAdd.remainingTime = parseTimeString(cmdPars); - } - while (true) { - Pair ret = extractExtInfo(tok.token, "playeraction"); - tok.token = ret.first; - String cmdPars = ret.second; - if (cmdPars == null) - break; - nodeToAdd.playerAction = cmdPars; - } - } catch (IndexOutOfBoundsException e) { - } - if (options.imp.comments) { - if (moveAdded) - nodeToAdd.postComment += tok.token; - else - nodeToAdd.preComment += tok.token; - } + if (moveAdded) + nodeToAdd.postComment += tok.token; + else + nodeToAdd.preComment += tok.token; break; case PgnToken.ASTERISK: case PgnToken.LEFT_BRACKET: @@ -1305,177 +528,6 @@ public class GameTree { } } } - - private static Pair extractExtInfo(String comment, String cmd) { - comment = comment.replaceAll("\n|\r|\t", " "); - String remaining = comment; - String param = null; - String match = "[%" + cmd + " "; - int start = comment.indexOf(match); - if (start >= 0) { - int end = comment.indexOf("]", start); - if (end >= 0) { - remaining = comment.substring(0, start) + comment.substring(end + 1); - param = comment.substring(start + match.length(), end); - } - } - return new Pair<>(remaining, param); - } - - /** Convert hh:mm:ss to milliseconds */ - private static int parseTimeString(String str) { - str = str.trim(); - int ret = 0; - boolean neg = false; - int i = 0; - if (str.charAt(0) == '-') { - neg = true; - i++; - } - int num = 0; - final int len = str.length(); - for ( ; i < len; i++) { - char c = str.charAt(i); - if ((c >= '0') && (c <= '9')) { - num = num * 10 + c - '0'; - } else if (c == ':') { - ret += num; - num = 0; - ret *= 60; - } - } - ret += num; - ret *= 1000; - if (neg) - ret = -ret; - return ret; - } - - public static String nagStr(int nag) { - switch (nag) { - case 1: return "!"; - case 2: return "?"; - case 3: return "!!"; - case 4: return "??"; - case 5: return "!?"; - case 6: return "?!"; - case 11: return " ="; - case 13: return " ∞"; - case 14: return " +/="; - case 15: return " =/+"; - case 16: return " +/-"; - case 17: return " -/+"; - case 18: return " +-"; - case 19: return " -+"; - default: return ""; - } - } - - public static int strToNag(String str) { - if (str.equals("!")) return 1; - else if (str.equals("?")) return 2; - else if (str.equals("!!")) return 3; - else if (str.equals("??")) return 4; - else if (str.equals("!?")) return 5; - else if (str.equals("?!")) return 6; - else if (str.equals("=")) return 11; - else if (str.equals("∞")) return 13; - else if (str.equals("+/=")) return 14; - else if (str.equals("=/+")) return 15; - else if (str.equals("+/-")) return 16; - else if (str.equals("-/+")) return 17; - else if (str.equals("+-")) return 18; - else if (str.equals("-+")) return 19; - else { - try { - str = str.replace("$", ""); - int nag = Integer.parseInt(str); - return nag; - } catch (NumberFormatException nfe) { - } - return 0; - } - } - } - - /** Set PGN header tags and values. Setting a non-required - * tag to null causes it to be removed. - * @return True if game result changes, false otherwise. */ - boolean setHeaders(Map headers) { - boolean resultChanged = false; - for (Entry entry : headers.entrySet()) { - String tag = entry.getKey(); - String val = entry.getValue(); - if (tag.equals("Event")) event = val; - else if (tag.equals("Site")) site = val; - else if (tag.equals("Date")) date = val; - else if (tag.equals("Round")) round = val; - else if (tag.equals("White")) white = val; - else if (tag.equals("Black")) black = val; - else if (tag.equals("Result")) { - List currPath = new ArrayList<>(); - while (currentNode != rootNode) { - Node child = currentNode; - goBack(); - int childNum = currentNode.children.indexOf(child); - currPath.add(childNum); - } - while (variations().size() > 0) - goForward(0, false); - if (!val.equals(getPGNResultString())) { - resultChanged = true; - GameState state = getGameState(); - switch (state) { - case ALIVE: - case DRAW_50: - case DRAW_AGREE: - case DRAW_REP: - case RESIGN_BLACK: - case RESIGN_WHITE: - currentNode.playerAction = ""; - if ("--".equals(currentNode.moveStr)) { - Node child = currentNode; - goBack(); - int childNum = currentNode.children.indexOf(child); - deleteVariation(childNum); - } - addResult(val); - break; - default: - break; - } - } - while (currentNode != rootNode) - goBack(); - for (int i = currPath.size() - 1; i >= 0; i--) - goForward(currPath.get(i), false); - } else { - if (val != null) { - boolean found = false; - for (TagPair t : tagPairs) { - if (t.tagName.equals(tag)) { - t.tagValue = val; - found = true; - break; - } - } - if (!found) { - TagPair tp = new TagPair(); - tp.tagName = tag; - tp.tagValue = val; - tagPairs.add(tp); - } - } else { - for (int i = 0; i < tagPairs.size(); i++) { - if (tagPairs.get(i).tagName.equals(tag)) { - tagPairs.remove(i); - break; - } - } - } - } - } - return resultChanged; } /** Get PGN header tags and values. */ @@ -1486,119 +538,10 @@ public class GameTree { headers.put("Round", round); headers.put("White", white); headers.put("Black", black); - headers.put("Result", getPGNResultStringMainLine()); - if (!timeControl.equals("?")) - headers.put("TimeControl", timeControl); - if (!whiteTimeControl.equals("?")) - headers.put("WhiteTimeControl", whiteTimeControl); - if (!blackTimeControl.equals("?")) - headers.put("BlackTimeControl", blackTimeControl); + headers.put("Result", result); for (int i = 0; i < tagPairs.size(); i++) { TagPair tp = tagPairs.get(i); headers.put(tp.tagName, tp.tagValue); } } - - private ArrayList stringToTCFields(String tcStr) { - String[] fields = tcStr.split(":"); - int nf = fields.length; - ArrayList ret = new ArrayList<>(nf); - for (int i = 0; i < nf; i++) { - String f = fields[i].trim(); - if (f.equals("?") || f.equals("-") || f.contains("*")) { - // Not supported - } else { - try { - int moves = 0; - int time = 0; - int inc = 0; - int idx = f.indexOf('/'); - if (idx > 0) - moves = Integer.parseInt(f.substring(0, idx).trim()); - if (idx >= 0) - f = f.substring(idx+1); - idx = f.indexOf('+'); - if (idx >= 0) { - if (idx > 0) - time = (int)(Double.parseDouble(f.substring(0, idx).trim())*1e3); - if (idx >= 0) - f = f.substring(idx+1); - inc = (int)(Double.parseDouble(f.trim())*1e3); - } else { - time = (int)(Double.parseDouble(f.trim())*1e3); - } - ret.add(new TimeControlField(time, moves, inc)); - } catch (NumberFormatException ex) { - // Invalid syntax, ignore - } - } - } - return ret; - } - - private String tcFieldsToString(ArrayList tcFields) { - StringBuilder sb = new StringBuilder(); - int nf = tcFields.size(); - for (int i = 0; i < nf; i++) { - if (i > 0) - sb.append(':'); - TimeControlField t = tcFields.get(i); - if (t.movesPerSession > 0) { - sb.append(t.movesPerSession); - sb.append('/'); - } - sb.append(t.timeControl / 1000); - int ms = t.timeControl % 1000; - if (ms > 0) { - sb.append('.'); - sb.append(String.format(Locale.US, "%03d", ms)); - } - if (t.increment > 0) { - sb.append('+'); - sb.append(t.increment / 1000); - ms = t.increment % 1000; - if (ms > 0) { - sb.append('.'); - sb.append(String.format(Locale.US, "%03d", ms)); - } - } - } - return sb.toString(); - } - - /** Get time control data, or null if not present. */ - public TimeControlData getTimeControlData() { - if (!whiteTimeControl.equals("?") && !blackTimeControl.equals("?")) { - ArrayList tcW = stringToTCFields(whiteTimeControl); - ArrayList tcB = stringToTCFields(blackTimeControl); - if (!tcW.isEmpty() && !tcB.isEmpty()) { - TimeControlData tcData = new TimeControlData(); - tcData.tcW = tcW; - tcData.tcB = tcB; - return tcData; - } - } - if (!timeControl.equals("?")) { - ArrayList tc = stringToTCFields(timeControl); - if (!tc.isEmpty()) { - TimeControlData tcData = new TimeControlData(); - tcData.tcW = tc; - tcData.tcB = tc; - return tcData; - } - } - return null; - } - - public void setTimeControlData(TimeControlData tcData) { - if (tcData.isSymmetric()) { - timeControl = tcFieldsToString(tcData.tcW); - whiteTimeControl = "?"; - blackTimeControl = "?"; - } else { - whiteTimeControl = tcFieldsToString(tcData.tcW); - blackTimeControl = tcFieldsToString(tcData.tcB); - timeControl = "?"; - } - } } diff --git a/buildSrc/src/main/java/chess/Move.java b/buildSrc/src/main/java/chess/Move.java index 1def1eb..055770e 100644 --- a/buildSrc/src/main/java/chess/Move.java +++ b/buildSrc/src/main/java/chess/Move.java @@ -30,58 +30,13 @@ public class Move { /** Promotion piece. */ public int promoteTo; - public int score; - /** Create a move object. */ public Move(int from, int to, int promoteTo) { this.from = from; this.to = to; 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 { - 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 public boolean equals(Object o) { if ((o == null) || (o.getClass() != this.getClass())) diff --git a/buildSrc/src/main/java/chess/PGNOptions.java b/buildSrc/src/main/java/chess/PGNOptions.java deleted file mode 100644 index 99b7650..0000000 --- a/buildSrc/src/main/java/chess/PGNOptions.java +++ /dev/null @@ -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 . -*/ - -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; - } -} diff --git a/buildSrc/src/main/java/chess/PgnToken.java b/buildSrc/src/main/java/chess/PgnToken.java index dc4054f..5a46cd7 100644 --- a/buildSrc/src/main/java/chess/PgnToken.java +++ b/buildSrc/src/main/java/chess/PgnToken.java @@ -45,19 +45,4 @@ public class PgnToken { this.type = type; 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); - } } diff --git a/buildSrc/src/main/java/chess/Piece.java b/buildSrc/src/main/java/chess/Piece.java index 2cac45a..7b3511c 100644 --- a/buildSrc/src/main/java/chess/Piece.java +++ b/buildSrc/src/main/java/chess/Piece.java @@ -45,10 +45,4 @@ public class Piece { public static boolean isWhite(int pType) { 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; - } } diff --git a/buildSrc/src/main/java/chess/Position.java b/buildSrc/src/main/java/chess/Position.java index ef3aeda..0889cb7 100644 --- a/buildSrc/src/main/java/chess/Position.java +++ b/buildSrc/src/main/java/chess/Position.java @@ -33,7 +33,6 @@ public class Position { // Bitboards public long[] pieceTypeBB; - public long whiteBB, blackBB; public boolean whiteMove; @@ -66,7 +65,6 @@ public class Position { for (int i = 0; i < Piece.nPieceTypes; i++) { pieceTypeBB[i] = 0L; } - whiteBB = blackBB = 0L; whiteMove = true; castleMask = 0; epSquare = -1; @@ -84,8 +82,6 @@ public class Position { for (int i = 0; i < Piece.nPieceTypes; i++) { pieceTypeBB[i] = other.pieceTypeBB[i]; } - whiteBB = other.whiteBB; - blackBB = other.blackBB; whiteMove = other.whiteMove; castleMask = other.castleMask; epSquare = other.epSquare; @@ -126,22 +122,7 @@ public class Position { public final long zobristHash() { 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. * @return True if positions are equal, false otherwise. @@ -204,13 +185,9 @@ public class Position { pieceTypeBB[piece] &= ~sqMaskF; pieceTypeBB[piece] |= sqMaskT; if (Piece.isWhite(piece)) { - whiteBB &= ~sqMaskF; - whiteBB |= sqMaskT; if (piece == Piece.WKING) wKingSq = to; } else { - blackBB &= ~sqMaskF; - blackBB |= sqMaskT; if (piece == Piece.BKING) bKingSq = to; } @@ -232,12 +209,10 @@ public class Position { if (removedPiece != Piece.EMPTY) { if (Piece.isWhite(removedPiece)) { - whiteBB &= ~sqMask; if (removedPiece == Piece.WPAWN) { pHashKey ^= psHashKeys[Piece.WPAWN][square]; } } else { - blackBB &= ~sqMask; if (removedPiece == Piece.BPAWN) { pHashKey ^= psHashKeys[Piece.BPAWN][square]; } @@ -246,14 +221,12 @@ public class Position { if (piece != Piece.EMPTY) { if (Piece.isWhite(piece)) { - whiteBB |= sqMask; if (piece == Piece.WPAWN) { pHashKey ^= psHashKeys[Piece.WPAWN][square]; } if (piece == Piece.WKING) wKingSq = square; } else { - blackBB |= sqMask; if (piece == Piece.BPAWN) { 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. */ public final int getCastleMask() { return castleMask; @@ -334,15 +263,6 @@ public class Position { 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. */ public final void makeMove(Move move, UndoInfo ui) { 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) { if (square == Position.getSquare(0, 0)) { setCastleMask(castleMask & ~(1 << Position.A1_CASTLE)); diff --git a/buildSrc/src/main/java/chess/TextIO.java b/buildSrc/src/main/java/chess/TextIO.java index f4677ac..0fcd74c 100644 --- a/buildSrc/src/main/java/chess/TextIO.java +++ b/buildSrc/src/main/java/chess/TextIO.java @@ -105,7 +105,6 @@ public class TextIO { } } pos.setCastleMask(castleMask); - removeBogusCastleFlags(pos); if (words.length > 3) { // En passant target square @@ -182,21 +181,6 @@ public class TextIO { 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. */ public static void fixupEPSquare(Position pos) { int epSquare = pos.getEpSquare(); @@ -226,91 +210,6 @@ public class TextIO { 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. * @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 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 { int piece; // -1 for unspecified int fromX, fromY, toX, toY; // -1 for unspecified @@ -580,82 +463,6 @@ public class TextIO { 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. * @return The square number, or -1 if not a legal square. @@ -668,18 +475,6 @@ public class TextIO { 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) { switch (p) { case Piece.WQUEEN: case Piece.BQUEEN: return "Q"; @@ -702,18 +497,4 @@ public class TextIO { } 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()); - } } diff --git a/buildSrc/src/main/java/chess/TimeControl.java b/buildSrc/src/main/java/chess/TimeControl.java deleted file mode 100644 index 4a89c16..0000000 --- a/buildSrc/src/main/java/chess/TimeControl.java +++ /dev/null @@ -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 . -*/ - -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 tc = tcData.getTC(whiteToMove); - Pair 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 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 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 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 getCurrentTC(boolean whiteMove) { - ArrayList 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); - } -} diff --git a/buildSrc/src/main/java/chess/TimeControlData.java b/buildSrc/src/main/java/chess/TimeControlData.java deleted file mode 100644 index 9092cbe..0000000 --- a/buildSrc/src/main/java/chess/TimeControlData.java +++ /dev/null @@ -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 . -*/ - -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 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 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 a1, - ArrayList 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 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 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); - } - } - } -}