/* DroidFish - An Android chess program. Copyright (C) 2011 Peter Ă–sterlund, peterosterlund2@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.petero.droidfish.gamelogic; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import org.petero.droidfish.gamelogic.TimeControlData.TimeControlField; /** Keep track of time control information for both players. */ public class TimeControl { TimeControlData tcData; private int whiteBaseTime; // Current remaining time, or remaining time when clock started private int blackBaseTime; // Current remaining time, or remaining time when clock started int currentMove; boolean whiteToMove; private int elapsed; // Accumulated elapsed time for this move. private long timerT0; // Time when timer started. 0 if timer is stopped. /** Constructor. Sets time control to "game in 5min". */ public TimeControl() { tcData = new TimeControlData(); reset(); } public final void reset() { currentMove = 1; whiteToMove = true; elapsed = 0; timerT0 = 0; } /** Set time controls for white and black players. */ public final void setTimeControl(TimeControlData tcData) { this.tcData = tcData; } public final void setCurrentMove(int move, boolean whiteToMove, int whiteBaseTime, int blackBaseTime) { currentMove = move; this.whiteToMove = whiteToMove; this.whiteBaseTime = whiteBaseTime; this.blackBaseTime = blackBaseTime; timerT0 = 0; elapsed = 0; } /** Move current move "delta" half-moves forward. */ public final void advanceMove(int delta) { while (delta > 0) { if (!whiteToMove) currentMove++; whiteToMove = !whiteToMove; delta--; } while (delta < 0) { whiteToMove = !whiteToMove; if (!whiteToMove) currentMove--; delta++; } } public final boolean clockRunning() { return timerT0 != 0; } public final void startTimer(long now) { if (!clockRunning()) { timerT0 = now; } } public final void stopTimer(long now) { if (clockRunning()) { int currElapsed = (int)(now - timerT0); timerT0 = 0; if (currElapsed > 0) elapsed += currElapsed; } } /** Compute new remaining time after a move is made. */ public final int moveMade(long now, boolean useIncrement) { stopTimer(now); ArrayList<TimeControlField> tc = tcData.getTC(whiteToMove); Pair<Integer,Integer> tcInfo = getCurrentTC(whiteToMove); int tcIdx = tcInfo.first; int movesToTc = tcInfo.second; int remaining = getRemainingTime(whiteToMove, now); if (useIncrement) { remaining += tc.get(tcIdx).increment; if (movesToTc == 1) { if (tcIdx+1 < tc.size()) tcIdx++; remaining += tc.get(tcIdx).timeControl; } } elapsed = 0; return remaining; } /** Get remaining time */ public final int getRemainingTime(boolean whiteToMove, long now) { int remaining = whiteToMove ? whiteBaseTime : blackBaseTime; if (whiteToMove == this.whiteToMove) { remaining -= elapsed; if (timerT0 != 0) remaining -= now - timerT0; } return remaining; } /** Get initial thinking time in milliseconds. */ public final int getInitialTime(boolean whiteMove) { ArrayList<TimeControlField> tc = tcData.getTC(whiteMove); return tc.get(0).timeControl; } /** Get time increment in milliseconds after playing next move. */ public final int getIncrement(boolean whiteMove) { ArrayList<TimeControlField> tc = tcData.getTC(whiteMove); int tcIdx = getCurrentTC(whiteMove).first; return tc.get(tcIdx).increment; } /** Return number of moves to the next time control, or 0 if "sudden death". */ public final int getMovesToTC(boolean whiteMove) { return getCurrentTC(whiteMove).second; } /** @return Array containing time control, moves per session and time increment. */ public int[] getTimeLimit(boolean whiteMove) { ArrayList<TimeControlField> tc = tcData.getTC(whiteMove); int tcIdx = getCurrentTC(whiteMove).first; TimeControlField t = tc.get(tcIdx); return new int[]{t.timeControl, t.movesPerSession, t.increment}; } /** Return the current active time control index and number of moves to next time control. */ private Pair<Integer,Integer> getCurrentTC(boolean whiteMove) { ArrayList<TimeControlField> tc = tcData.getTC(whiteMove); int tcIdx = 0; final int lastTcIdx = tc.size() - 1; int nextTC = 1; int currMove = currentMove; if (!whiteToMove && whiteMove) currMove++; while (true) { if (tc.get(tcIdx).movesPerSession <= 0) return new Pair<Integer,Integer>(tcIdx, 0); nextTC += tc.get(tcIdx).movesPerSession; if (nextTC > currMove) break; if (tcIdx < lastTcIdx) tcIdx++; } 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); } }