diff --git a/DroidFish/src/org/petero/droidfish/DroidFish.java b/DroidFish/src/org/petero/droidfish/DroidFish.java index b26f543..e846b47 100644 --- a/DroidFish/src/org/petero/droidfish/DroidFish.java +++ b/DroidFish/src/org/petero/droidfish/DroidFish.java @@ -136,8 +136,7 @@ public class DroidFish extends Activity implements GUIInterface { // FIXME!!! Add support for "Chess Leipzig" font // FIXME!!! Computer clock should stop if phone turned off (computer stops thinking if unplugged) - // FIXME!!! Add support for all time controls defined by the PGN standard - // FIXME!!! How to handle hour-glass time control? + // FIXME!!! Add support for "no time control" and "hour-glass time control" as defined by the PGN standard // FIXME!!! Online play on FICS // FIXME!!! Add chess960 support @@ -167,7 +166,9 @@ public class DroidFish extends Activity implements GUIInterface { private int maxNumArrows; private GameMode gameMode; private boolean mPonderMode; - private TimeControlData tcData = new TimeControlData(); + private int timeControl; + private int movesPerSession; + private int timeIncrement; private int mEngineThreads; private String playerName; private boolean boardFlipped; @@ -407,6 +408,8 @@ public class DroidFish extends Activity implements GUIInterface { ctrl = new DroidChessController(this, gameTextListener, pgnOptions); egtbForceReload = true; readPrefs(); + TimeControlData tcData = new TimeControlData(); + tcData.setTimeControl(timeControl, movesPerSession, timeIncrement); ctrl.newGame(gameMode, tcData); { byte[] data = null; @@ -522,6 +525,8 @@ public class DroidFish extends Activity implements GUIInterface { } private final byte[] strToByteArr(String str) { + if (str == null) + return null; int nBytes = str.length() / 2; byte[] ret = new byte[nBytes]; for (int i = 0; i < nBytes; i++) { @@ -533,6 +538,8 @@ public class DroidFish extends Activity implements GUIInterface { } private final String byteArrToString(byte[] data) { + if (data == null) + return null; StringBuilder ret = new StringBuilder(32768); int nBytes = data.length; for (int i = 0; i < nBytes; i++) { @@ -818,7 +825,7 @@ public class DroidFish extends Activity implements GUIInterface { if (ctrl != null) { byte[] data = ctrl.toByteArray(); outState.putByteArray("gameState", data); - outState.putInt("gameStateVersion", 2); + outState.putInt("gameStateVersion", 3); } } @@ -841,7 +848,7 @@ public class DroidFish extends Activity implements GUIInterface { Editor editor = settings.edit(); String dataStr = byteArrToString(data); editor.putString("gameState", dataStr); - editor.putInt("gameStateVersion", 2); + editor.putInt("gameStateVersion", 3); editor.commit(); } lastVisibleMillis = System.currentTimeMillis(); @@ -896,11 +903,9 @@ public class DroidFish extends Activity implements GUIInterface { if (!mPonderMode) ctrl.stopPonder(); - int timeControl = getIntSetting("timeControl", 120000); - int movesPerSession = getIntSetting("movesPerSession", 60); - int timeIncrement = getIntSetting("timeIncrement", 0); - tcData.setTimeControl(timeControl, movesPerSession, timeIncrement); - updateTimeControlTitle(); + timeControl = getIntSetting("timeControl", 120000); + movesPerSession = getIntSetting("movesPerSession", 60); + timeIncrement = getIntSetting("timeIncrement", 0); boardGestures = settings.getBoolean("boardGestures", true); scrollSensitivity = Float.parseFloat(settings.getString("scrollSensitivity", "2")); @@ -1651,22 +1656,6 @@ public class DroidFish extends Activity implements GUIInterface { cb.setMoveHints(hints); } - private final void startNewGame(int type) { - if (type != 2) { - int gameModeType = (type == 0) ? GameMode.PLAYER_WHITE : GameMode.PLAYER_BLACK; - Editor editor = settings.edit(); - String gameModeStr = String.format(Locale.US, "%d", gameModeType); - editor.putString("gameMode", gameModeStr); - editor.commit(); - gameMode = new GameMode(gameModeType); - } -// savePGNToFile(".autosave.pgn", true); - ctrl.newGame(gameMode, tcData); - ctrl.startGame(); - setBoardFlip(true); - updateEngineTitle(); - } - static private final int PROMOTE_DIALOG = 0; static private final int BOARD_MENU_DIALOG = 1; static private final int ABOUT_DIALOG = 2; @@ -1756,6 +1745,24 @@ public class DroidFish extends Activity implements GUIInterface { return builder.create(); } + private final void startNewGame(int type) { + if (type != 2) { + int gameModeType = (type == 0) ? GameMode.PLAYER_WHITE : GameMode.PLAYER_BLACK; + Editor editor = settings.edit(); + String gameModeStr = String.format(Locale.US, "%d", gameModeType); + editor.putString("gameMode", gameModeStr); + editor.commit(); + gameMode = new GameMode(gameModeType); + } +// savePGNToFile(".autosave.pgn", true); + TimeControlData tcData = new TimeControlData(); + tcData.setTimeControl(timeControl, movesPerSession, timeIncrement); + ctrl.newGame(gameMode, tcData); + ctrl.startGame(); + setBoardFlip(true); + updateEngineTitle(); + } + private final Dialog promoteDialog() { final CharSequence[] items = { getString(R.string.queen), getString(R.string.rook), diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java b/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java index 69948e8..d036a90 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/DroidChessController.java @@ -18,6 +18,11 @@ package org.petero.droidfish.gamelogic; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -199,13 +204,41 @@ public class DroidChessController { /** De-serialize from byte array. */ public final synchronized void fromByteArray(byte[] data, int version) { - game.fromByteArray(data, version); - game.tree.translateMoves(); + ByteArrayInputStream bais = null; + DataInputStream dis = null; + try { + bais = new ByteArrayInputStream(data); + dis = new DataInputStream(bais); + game.readFromStream(dis, version); + game.tree.translateMoves(); + } catch (IOException e) { + } catch (ChessParseError e) { + } finally { + if (dis != null) + try { dis.close(); } catch (IOException ex) {} + if (bais != null) + try { bais.close(); } catch (IOException ex) {} + } } /** Serialize to byte array. */ public final synchronized byte[] toByteArray() { - return game.toByteArray(); + ByteArrayOutputStream baos = null; + DataOutputStream dos = null; + try { + baos = new ByteArrayOutputStream(32768); + dos = new DataOutputStream(baos); + game.writeToStream(dos); + dos.flush(); + return baos.toByteArray(); + } catch (IOException e) { + return null; + } finally { + if (dos != null) + try { dos.close(); } catch (IOException ex) {} + if (baos != null) + try { baos.close(); } catch (IOException ex) {} + } } /** Return FEN string corresponding to a current position. */ diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/Game.java b/DroidFish/src/org/petero/droidfish/gamelogic/Game.java index cc0ce87..67b6594 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/Game.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/Game.java @@ -18,6 +18,9 @@ package org.petero.droidfish.gamelogic; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -47,15 +50,18 @@ public class Game { tree.setTimeControlData(tcData); } - /** De-serialize from byte array. */ - final void fromByteArray(byte[] data, int version) { - tree.fromByteArray(data, version); + /** 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 byte array. */ - final synchronized byte[] toByteArray() { - return tree.toByteArray(); + /** Serialize to output stream. */ + final synchronized void writeToStream(DataOutputStream dos) throws IOException { + tree.writeToStream(dos); + timeController.writeToStream(dos); } public final void setGamePaused(boolean gamePaused) { diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/GameTree.java b/DroidFish/src/org/petero/droidfish/gamelogic/GameTree.java index 19d9186..8d22ef6 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/GameTree.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/GameTree.java @@ -18,8 +18,6 @@ package org.petero.droidfish.gamelogic; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -613,83 +611,65 @@ public class GameTree { return true; } - /** Serialize to byte array. */ - public final byte[] toByteArray() { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(32768); - DataOutputStream dos = new DataOutputStream(baos); - 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)); - dos.flush(); - dos.close(); - byte[] ret = baos.toByteArray(); - baos.close(); - return ret; - } catch (IOException e) { - return null; + /** 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 byte array. */ - public final void fromByteArray(byte[] data, int version) { - try { - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - 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()); - dis.close(); - bais.close(); - } catch (IOException e) { - } catch (ChessParseError e) { + /** 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(); } diff --git a/DroidFish/src/org/petero/droidfish/gamelogic/TimeControl.java b/DroidFish/src/org/petero/droidfish/gamelogic/TimeControl.java index 646863e..6df051a 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/TimeControl.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/TimeControl.java @@ -18,6 +18,9 @@ package org.petero.droidfish.gamelogic; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.ArrayList; import org.petero.droidfish.gamelogic.TimeControlData.TimeControlField; @@ -161,4 +164,14 @@ public class TimeControl { } 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/DroidFish/src/org/petero/droidfish/gamelogic/TimeControlData.java b/DroidFish/src/org/petero/droidfish/gamelogic/TimeControlData.java index f67eb7a..1b0288f 100644 --- a/DroidFish/src/org/petero/droidfish/gamelogic/TimeControlData.java +++ b/DroidFish/src/org/petero/droidfish/gamelogic/TimeControlData.java @@ -1,5 +1,8 @@ package org.petero.droidfish.gamelogic; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.ArrayList; public final class TimeControlData { @@ -65,4 +68,37 @@ public final class TimeControlData { } 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); + } + } + } } diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java index f3bff74..b5ef803 100644 --- a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java @@ -19,6 +19,11 @@ package org.petero.droidfish.gamelogic; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -39,7 +44,7 @@ public class GameTreeTest extends TestCase { return gt.addMove(moveStr, "", 0, "", ""); } - public final void testGameTree() throws ChessParseError { + public final void testGameTree() throws ChessParseError, IOException { GameTree gt = new GameTree(null); Position expectedPos = TextIO.readFEN(TextIO.startPosFEN); assertEquals(expectedPos, gt.currentPos); @@ -97,9 +102,24 @@ public class GameTreeTest extends TestCase { expectedPos.makeMove(move, ui); assertEquals(expectedPos, gt.currentPos); - byte[] serialState = gt.toByteArray(); + byte[] serialState = null; + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(32768); + DataOutputStream dos = new DataOutputStream(baos); + gt.writeToStream(dos); + dos.flush(); + serialState = baos.toByteArray(); + dos.close(); + baos.close(); + } gt = new GameTree(null); - gt.fromByteArray(serialState, 2); + { + ByteArrayInputStream bais = new ByteArrayInputStream(serialState); + DataInputStream dis = new DataInputStream(bais); + gt.readFromStream(dis, 3); + dis.close(); + bais.close(); + } assertEquals(expectedPos, gt.currentPos); gt.goBack(); diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java index 45346dd..772e4cb 100644 --- a/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java @@ -18,6 +18,11 @@ package org.petero.droidfish.gamelogic; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.ArrayList; import org.petero.droidfish.gamelogic.TimeControlData.TimeControlField; @@ -227,4 +232,36 @@ public class TimeControlTest extends TestCase { assertEquals(timeCont - 2000 + (timeCont + inc)*0, tc.getRemainingTime(true, t0 + 4711)); assertEquals(timeCont, tc.getRemainingTime(false, t0 + 4711)); } + + public void testSerialize() throws IOException { + TimeControl tc = new TimeControl(); + TimeControlData tcData = new TimeControlData(); + tcData.tcW = new ArrayList(); + tcData.tcW.add(tcf(120*60*1000, 40, 0)); + tcData.tcW.add(tcf(60*60*1000, 20, 0)); + tcData.tcW.add(tcf(30*60*1000, 0, 15*1000)); + tcData.tcB = new ArrayList(); + tcData.tcB.add(tcf(5*60*1000, 60, 1000)); + tc.setTimeControl(tcData); + + byte[] serialState = null; + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(32768); + DataOutputStream dos = new DataOutputStream(baos); + tc.writeToStream(dos); + dos.flush(); + serialState = baos.toByteArray(); + dos.close(); + baos.close(); + } + TimeControl tc2 = new TimeControl(); + { + ByteArrayInputStream bais = new ByteArrayInputStream(serialState); + DataInputStream dis = new DataInputStream(bais); + tc2.readFromStream(dis, 3); + dis.close(); + bais.close(); + } + assertEquals(tcData, tc2.tcData); + } }