DroidFish: Save time control information when exiting program.

This commit is contained in:
Peter Osterlund
2013-04-14 20:53:02 +00:00
parent a14a8250a1
commit d5930c0f30
8 changed files with 245 additions and 113 deletions

View File

@@ -136,8 +136,7 @@ public class DroidFish extends Activity implements GUIInterface {
// FIXME!!! Add support for "Chess Leipzig" font // FIXME!!! Add support for "Chess Leipzig" font
// FIXME!!! Computer clock should stop if phone turned off (computer stops thinking if unplugged) // 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!!! Add support for "no time control" and "hour-glass time control" as defined by the PGN standard
// FIXME!!! How to handle hour-glass time control?
// FIXME!!! Online play on FICS // FIXME!!! Online play on FICS
// FIXME!!! Add chess960 support // FIXME!!! Add chess960 support
@@ -167,7 +166,9 @@ public class DroidFish extends Activity implements GUIInterface {
private int maxNumArrows; private int maxNumArrows;
private GameMode gameMode; private GameMode gameMode;
private boolean mPonderMode; private boolean mPonderMode;
private TimeControlData tcData = new TimeControlData(); private int timeControl;
private int movesPerSession;
private int timeIncrement;
private int mEngineThreads; private int mEngineThreads;
private String playerName; private String playerName;
private boolean boardFlipped; private boolean boardFlipped;
@@ -407,6 +408,8 @@ public class DroidFish extends Activity implements GUIInterface {
ctrl = new DroidChessController(this, gameTextListener, pgnOptions); ctrl = new DroidChessController(this, gameTextListener, pgnOptions);
egtbForceReload = true; egtbForceReload = true;
readPrefs(); readPrefs();
TimeControlData tcData = new TimeControlData();
tcData.setTimeControl(timeControl, movesPerSession, timeIncrement);
ctrl.newGame(gameMode, tcData); ctrl.newGame(gameMode, tcData);
{ {
byte[] data = null; byte[] data = null;
@@ -522,6 +525,8 @@ public class DroidFish extends Activity implements GUIInterface {
} }
private final byte[] strToByteArr(String str) { private final byte[] strToByteArr(String str) {
if (str == null)
return null;
int nBytes = str.length() / 2; int nBytes = str.length() / 2;
byte[] ret = new byte[nBytes]; byte[] ret = new byte[nBytes];
for (int i = 0; i < nBytes; i++) { for (int i = 0; i < nBytes; i++) {
@@ -533,6 +538,8 @@ public class DroidFish extends Activity implements GUIInterface {
} }
private final String byteArrToString(byte[] data) { private final String byteArrToString(byte[] data) {
if (data == null)
return null;
StringBuilder ret = new StringBuilder(32768); StringBuilder ret = new StringBuilder(32768);
int nBytes = data.length; int nBytes = data.length;
for (int i = 0; i < nBytes; i++) { for (int i = 0; i < nBytes; i++) {
@@ -818,7 +825,7 @@ public class DroidFish extends Activity implements GUIInterface {
if (ctrl != null) { if (ctrl != null) {
byte[] data = ctrl.toByteArray(); byte[] data = ctrl.toByteArray();
outState.putByteArray("gameState", data); 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(); Editor editor = settings.edit();
String dataStr = byteArrToString(data); String dataStr = byteArrToString(data);
editor.putString("gameState", dataStr); editor.putString("gameState", dataStr);
editor.putInt("gameStateVersion", 2); editor.putInt("gameStateVersion", 3);
editor.commit(); editor.commit();
} }
lastVisibleMillis = System.currentTimeMillis(); lastVisibleMillis = System.currentTimeMillis();
@@ -896,11 +903,9 @@ public class DroidFish extends Activity implements GUIInterface {
if (!mPonderMode) if (!mPonderMode)
ctrl.stopPonder(); ctrl.stopPonder();
int timeControl = getIntSetting("timeControl", 120000); timeControl = getIntSetting("timeControl", 120000);
int movesPerSession = getIntSetting("movesPerSession", 60); movesPerSession = getIntSetting("movesPerSession", 60);
int timeIncrement = getIntSetting("timeIncrement", 0); timeIncrement = getIntSetting("timeIncrement", 0);
tcData.setTimeControl(timeControl, movesPerSession, timeIncrement);
updateTimeControlTitle();
boardGestures = settings.getBoolean("boardGestures", true); boardGestures = settings.getBoolean("boardGestures", true);
scrollSensitivity = Float.parseFloat(settings.getString("scrollSensitivity", "2")); scrollSensitivity = Float.parseFloat(settings.getString("scrollSensitivity", "2"));
@@ -1651,22 +1656,6 @@ public class DroidFish extends Activity implements GUIInterface {
cb.setMoveHints(hints); 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 PROMOTE_DIALOG = 0;
static private final int BOARD_MENU_DIALOG = 1; static private final int BOARD_MENU_DIALOG = 1;
static private final int ABOUT_DIALOG = 2; static private final int ABOUT_DIALOG = 2;
@@ -1756,6 +1745,24 @@ public class DroidFish extends Activity implements GUIInterface {
return builder.create(); 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() { private final Dialog promoteDialog() {
final CharSequence[] items = { final CharSequence[] items = {
getString(R.string.queen), getString(R.string.rook), getString(R.string.queen), getString(R.string.rook),

View File

@@ -18,6 +18,11 @@
package org.petero.droidfish.gamelogic; 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -199,13 +204,41 @@ public class DroidChessController {
/** De-serialize from byte array. */ /** De-serialize from byte array. */
public final synchronized void fromByteArray(byte[] data, int version) { public final synchronized void fromByteArray(byte[] data, int version) {
game.fromByteArray(data, version); ByteArrayInputStream bais = null;
game.tree.translateMoves(); 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. */ /** Serialize to byte array. */
public final synchronized byte[] toByteArray() { 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. */ /** Return FEN string corresponding to a current position. */

View File

@@ -18,6 +18,9 @@
package org.petero.droidfish.gamelogic; package org.petero.droidfish.gamelogic;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -47,15 +50,18 @@ public class Game {
tree.setTimeControlData(tcData); tree.setTimeControlData(tcData);
} }
/** De-serialize from byte array. */ /** De-serialize from input stream. */
final void fromByteArray(byte[] data, int version) { final void readFromStream(DataInputStream dis, int version) throws IOException, ChessParseError {
tree.fromByteArray(data, version); tree.readFromStream(dis, version);
if (version >= 3)
timeController.readFromStream(dis, version);
updateTimeControl(true); updateTimeControl(true);
} }
/** Serialize to byte array. */ /** Serialize to output stream. */
final synchronized byte[] toByteArray() { final synchronized void writeToStream(DataOutputStream dos) throws IOException {
return tree.toByteArray(); tree.writeToStream(dos);
timeController.writeToStream(dos);
} }
public final void setGamePaused(boolean gamePaused) { public final void setGamePaused(boolean gamePaused) {

View File

@@ -18,8 +18,6 @@
package org.petero.droidfish.gamelogic; package org.petero.droidfish.gamelogic;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
@@ -613,83 +611,65 @@ public class GameTree {
return true; return true;
} }
/** Serialize to byte array. */ /** Serialize to output stream. */
public final byte[] toByteArray() { public final void writeToStream(DataOutputStream dos) throws IOException {
try { dos.writeUTF(event);
ByteArrayOutputStream baos = new ByteArrayOutputStream(32768); dos.writeUTF(site);
DataOutputStream dos = new DataOutputStream(baos); dos.writeUTF(date);
dos.writeUTF(event); dos.writeUTF(round);
dos.writeUTF(site); dos.writeUTF(white);
dos.writeUTF(date); dos.writeUTF(black);
dos.writeUTF(round); dos.writeUTF(TextIO.toFEN(startPos));
dos.writeUTF(white); dos.writeUTF(timeControl);
dos.writeUTF(black); dos.writeUTF(whiteTimeControl);
dos.writeUTF(TextIO.toFEN(startPos)); dos.writeUTF(blackTimeControl);
dos.writeUTF(timeControl); int nTags = tagPairs.size();
dos.writeUTF(whiteTimeControl); dos.writeInt(nTags);
dos.writeUTF(blackTimeControl); for (int i = 0; i < nTags; i++) {
int nTags = tagPairs.size(); dos.writeUTF(tagPairs.get(i).tagName);
dos.writeInt(nTags); dos.writeUTF(tagPairs.get(i).tagValue);
for (int i = 0; i < nTags; i++) {
dos.writeUTF(tagPairs.get(i).tagName);
dos.writeUTF(tagPairs.get(i).tagValue);
}
Node.writeToStream(dos, rootNode);
ArrayList<Integer> 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;
} }
Node.writeToStream(dos, rootNode);
ArrayList<Integer> 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. */ /** De-serialize from input stream. */
public final void fromByteArray(byte[] data, int version) { public final void readFromStream(DataInputStream dis, int version) throws IOException, ChessParseError {
try { event = dis.readUTF();
ByteArrayInputStream bais = new ByteArrayInputStream(data); site = dis.readUTF();
DataInputStream dis = new DataInputStream(bais); date = dis.readUTF();
event = dis.readUTF(); round = dis.readUTF();
site = dis.readUTF(); white = dis.readUTF();
date = dis.readUTF(); black = dis.readUTF();
round = dis.readUTF(); startPos = TextIO.readFEN(dis.readUTF());
white = dis.readUTF(); currentPos = new Position(startPos);
black = dis.readUTF(); timeControl = dis.readUTF();
startPos = TextIO.readFEN(dis.readUTF()); if (version >= 2) {
currentPos = new Position(startPos); whiteTimeControl = dis.readUTF();
timeControl = dis.readUTF(); blackTimeControl = dis.readUTF();
if (version >= 2) { } else {
whiteTimeControl = dis.readUTF(); whiteTimeControl = "?";
blackTimeControl = dis.readUTF(); blackTimeControl = "?";
} 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) {
} }
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(); updateListener();
} }

View File

@@ -18,6 +18,9 @@
package org.petero.droidfish.gamelogic; package org.petero.droidfish.gamelogic;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import org.petero.droidfish.gamelogic.TimeControlData.TimeControlField; import org.petero.droidfish.gamelogic.TimeControlData.TimeControlField;
@@ -161,4 +164,14 @@ public class TimeControl {
} }
return new Pair<Integer,Integer>(tcIdx, nextTC - currMove); return new Pair<Integer,Integer>(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);
}
} }

View File

@@ -1,5 +1,8 @@
package org.petero.droidfish.gamelogic; package org.petero.droidfish.gamelogic;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
public final class TimeControlData { public final class TimeControlData {
@@ -65,4 +68,37 @@ public final class TimeControlData {
} }
return true; return true;
} }
/** De-serialize from input stream. */
public void readFromStream(DataInputStream dis, int version) throws IOException {
for (int c = 0; c < 2; c++) {
ArrayList<TimeControlField> tc = new ArrayList<TimeControlField>();
if (c == 0)
tcW = tc;
else
tcB = tc;
int nw = dis.readInt();
for (int i = 0; i < nw; i++) {
int time = dis.readInt();
int moves = dis.readInt();
int inc = dis.readInt();
tc.add(new TimeControlField(time, moves, inc));
}
}
}
/** Serialize to output stream. */
public void writeToStream(DataOutputStream dos) throws IOException {
for (int c = 0; c < 2; c++) {
ArrayList<TimeControlField> tc = (c == 0) ? tcW : tcB;
int nw = tc.size();
dos.writeInt(nw);
for (int i = 0; i < nw; i++) {
TimeControlField tcf = tc.get(i);
dos.writeInt(tcf.timeControl);
dos.writeInt(tcf.movesPerSession);
dos.writeInt(tcf.increment);
}
}
}
} }

View File

@@ -19,6 +19,11 @@
package org.petero.droidfish.gamelogic; 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -39,7 +44,7 @@ public class GameTreeTest extends TestCase {
return gt.addMove(moveStr, "", 0, "", ""); return gt.addMove(moveStr, "", 0, "", "");
} }
public final void testGameTree() throws ChessParseError { public final void testGameTree() throws ChessParseError, IOException {
GameTree gt = new GameTree(null); GameTree gt = new GameTree(null);
Position expectedPos = TextIO.readFEN(TextIO.startPosFEN); Position expectedPos = TextIO.readFEN(TextIO.startPosFEN);
assertEquals(expectedPos, gt.currentPos); assertEquals(expectedPos, gt.currentPos);
@@ -97,9 +102,24 @@ public class GameTreeTest extends TestCase {
expectedPos.makeMove(move, ui); expectedPos.makeMove(move, ui);
assertEquals(expectedPos, gt.currentPos); 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 = 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); assertEquals(expectedPos, gt.currentPos);
gt.goBack(); gt.goBack();

View File

@@ -18,6 +18,11 @@
package org.petero.droidfish.gamelogic; 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.ArrayList;
import org.petero.droidfish.gamelogic.TimeControlData.TimeControlField; 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 - 2000 + (timeCont + inc)*0, tc.getRemainingTime(true, t0 + 4711));
assertEquals(timeCont, tc.getRemainingTime(false, 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<TimeControlField>();
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<TimeControlField>();
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);
}
} }