fix move time recording in correspondence

pull/2701/head
Niklas Fiekas 2017-02-23 10:31:42 +01:00
parent 02f94be28c
commit ae45082d2b
5 changed files with 70 additions and 59 deletions

View File

@ -77,7 +77,8 @@ object BSONHandlers {
val bPlayer = player(blackPlayer, Black, blackId, blackUid)
val createdAtValue = r date createdAt
val realVariant = Variant(r intD variant) | chess.variant.Standard
val game = Game(
val gameClock = r.getO[Color => Clock](clock)(clockBSONReader(createdAtValue, wPlayer.berserk, bPlayer.berserk)) map (_(Color(0 == nbTurns % 2)))
Game(
id = r str id,
whitePlayer = wPlayer,
blackPlayer = bPlayer,
@ -86,7 +87,7 @@ object BSONHandlers {
status = r.get[Status](status),
turns = nbTurns,
startedAtTurn = r intD startedAtTurn,
clock = r.getO[Color => Clock](clock)(clockBSONReader(createdAtValue, wPlayer.berserk, bPlayer.berserk)) map (_(Color(0 == nbTurns % 2))),
clock = gameClock,
positionHashes = r.bytesD(positionHashes).value,
checkCount = {
val counts = r.intsD(checkCount)
@ -95,7 +96,15 @@ object BSONHandlers {
castleLastMoveTime = r.get[CastleLastMoveTime](castleLastMoveTime)(CastleLastMoveTime.castleLastMoveTimeBSONHandler),
unmovedRooks = r.getO[UnmovedRooks](unmovedRooks) | UnmovedRooks.default,
daysPerTurn = r intO daysPerTurn,
legacyMoveTimes = r bytesO legacyMoveTimes,
binaryMoveTimes = r bytesO moveTimes,
clockHistory = for {
clk <- gameClock
start = clk.limit.seconds
ew = (clk.remainingTime(White) * 1000).toLong.millis
eb = (clk.remainingTime(Black) * 1000).toLong.millis
bw <- r bytesO whiteClockHistory
bb <- r bytesO blackClockHistory
} yield BinaryFormat.clockHistory.read(start, ew, eb, bw, bb, r intD startedAtTurn, nbTurns),
mode = Mode(r boolD rated),
variant = realVariant,
crazyData = (realVariant == Crazyhouse) option r.get[Crazyhouse.Data](crazyData),
@ -112,13 +121,6 @@ object BSONHandlers {
analysed = r boolD analysed
)
)
game.copy(clockHistory = for {
start <- game.initialClockTime
ew <- game.remainingClockTime(White)
eb <- game.remainingClockTime(Black)
bw <- r bytesO whiteClockHistory
bb <- r bytesO blackClockHistory
} yield BinaryFormat.clockHistory.read(start, ew, eb, bw, bb, game.startedAtTurn, game.turns))
}
def writes(w: BSON.Writer, o: Game) = BSONDocument(
@ -138,9 +140,9 @@ object BSONHandlers {
castleLastMoveTime -> CastleLastMoveTime.castleLastMoveTimeBSONHandler.write(o.castleLastMoveTime),
unmovedRooks -> o.unmovedRooks,
daysPerTurn -> o.daysPerTurn,
legacyMoveTimes -> o.legacyMoveTimes,
whiteClockHistory -> o.clockHistory.map(h => BinaryFormat.clockHistory.writeSide(~o.initialClockTime, ~o.remainingClockTime(White), h.white)),
blackClockHistory -> o.clockHistory.map(h => BinaryFormat.clockHistory.writeSide(~o.initialClockTime, ~o.remainingClockTime(Black), h.black)),
moveTimes -> o.binaryMoveTimes,
whiteClockHistory -> clockHistory(White, o.clockHistory, o.clock),
blackClockHistory -> clockHistory(Black, o.clockHistory, o.clock),
rated -> w.boolO(o.mode.rated),
variant -> o.variant.exotic.option(o.variant.id).map(w.int),
crazyData -> o.crazyData,
@ -157,6 +159,12 @@ object BSONHandlers {
)
}
private def clockHistory(color: Color, clockHistory: Option[ClockHistory], clock: Option[Clock]): Option[ByteArray] = for {
history <- clockHistory
clock <- clock
remaining = (clock.remainingTime(color) * 1000).toLong.millis
} yield BinaryFormat.clockHistory.writeSide(clock.limit.seconds, remaining, history(color))
private[game] def clockBSONReader(since: DateTime, whiteBerserk: Boolean, blackBerserk: Boolean) = new BSONReader[BSONBinary, Color => Clock] {
def read(bin: BSONBinary) = BinaryFormat.clock(since).read(
ByteArrayBSONHandler read bin, whiteBerserk, blackBerserk

View File

@ -29,7 +29,7 @@ object BinaryFormat {
object clockHistory {
def writeSide(start: FiniteDuration, end: FiniteDuration, times: Vector[FiniteDuration]): ByteArray = {
val centis = times.map(_.toHundredths.toInt).dropRight(1)
val centis = times.map(_.toHundredths.toInt)
val startCentis = start.toHundredths.toInt
val endCentis = end.toHundredths.toInt
if (centis.isEmpty) { ByteArray.empty }
@ -37,19 +37,20 @@ object BinaryFormat {
}
def readSide(start: FiniteDuration, end: FiniteDuration, ba: ByteArray, numMoves: Int): Vector[FiniteDuration] = {
if (ba.isEmpty) { Vector(end) }
if (ba.isEmpty) { Vector.empty }
else {
val startCentis = start.toHundredths.toInt
val endCentis = end.toHundredths.toInt
clockencoder.Encoder.decode(ba.value, numMoves - 1, startCentis, endCentis).map(_ * 10.millis).toVector :+ end
clockencoder.Encoder.decode(ba.value, numMoves, startCentis, endCentis).map(_ * 10.millis).toVector
}
}
def read(start: FiniteDuration, ew: FiniteDuration, eb: FiniteDuration, bw: ByteArray, bb: ByteArray, startTurn: Int, turns: Int): ClockHistory = {
val ply = turns - startTurn
val wmoves = (if (startTurn % 2 == 0) { ply + 1 } else { ply }) / 2 - 1
val bmoves = (if (startTurn % 2 == 0) { ply } else { ply + 1 }) / 2 - 1
ClockHistory(readSide(start, ew, bw, wmoves), readSide(start, eb, bb, bmoves))
val wmoves = (if (startTurn % 2 == 0) { ply + 1 } else { ply }) / 2 - 2
val bmoves = (if (startTurn % 2 == 0) { ply } else { ply + 1 }) / 2 - 2
val end = if (turns % 2 == 0) { ew } else { eb }
ClockHistory(end, readSide(start, ew, bw, wmoves), readSide(start, eb, bb, bmoves))
}
}

View File

@ -27,7 +27,7 @@ case class Game(
daysPerTurn: Option[Int],
positionHashes: PositionHash = Array(),
checkCount: CheckCount = CheckCount(0, 0),
legacyMoveTimes: Option[ByteArray] = None,
binaryMoveTimes: Option[ByteArray] = None,
clockHistory: Option[ClockHistory] = None,
mode: Mode = Mode.default,
variant: Variant = Variant.default,
@ -112,19 +112,8 @@ case class Game(
def lastMoveTimeInSeconds: Option[Int] = lastMoveTime.map(x => (x / 10).toInt)
def initialClockTime: Option[FiniteDuration] = clock.map(_.limit.seconds) orElse daysPerTurn.map(_.days)
def remainingClockTime(color: Color): Option[FiniteDuration] =
clock.map { clk =>
(clk.remainingTime(color) * 1000.toLong).millis
} orElse daysPerTurn.map { days =>
lastMoveDateTime.fold(days.days) { lmt =>
(days.days - new org.joda.time.Duration(lmt, DateTime.now).getMillis.millis) max 0.millis
}
}
def moveTimes: Option[Vector[FiniteDuration]] = {
legacyMoveTimes.map { binary =>
binaryMoveTimes.map { binary =>
BinaryFormat.moveTime.read(binary, playedTurns)
} orElse {
for {
@ -135,7 +124,7 @@ case class Game(
}
def moveTimes(color: Color): Option[List[FiniteDuration]] = {
legacyMoveTimes.map { binary =>
binaryMoveTimes.map { binary =>
val pivot = if (color == startColor) 0 else 1
BinaryFormat.moveTime.read(binary, playedTurns).toList.zipWithIndex.collect {
case (e, i) if (i % 2) == pivot => e
@ -148,8 +137,6 @@ case class Game(
clockTimes.drop(1),
0.seconds +: Stream.continually(clk.increment.seconds)
).zipped.map(_ - _ + _).map(_ max 0.millis).toList
} orElse daysPerTurn.map { days =>
clockTimes.map(days.days - _).map(_ max 0.millis).toList
}
}
}
@ -222,8 +209,16 @@ case class Game(
check = situation.checkSquare
),
unmovedRooks = game.board.unmovedRooks,
clockHistory = remainingClockTime(turnColor).map { remaining =>
(clockHistory | ClockHistory.empty).record(turnColor)(remaining)
binaryMoveTimes = (!isPgnImport && !clock.isDefined).option {
BinaryFormat.moveTime.write(binaryMoveTimes.??(t => BinaryFormat.moveTime.read(t, playedTurns)) :+ lastMoveTime.?? { lmt =>
((nowTenths - lmt - (lag.??(_.roundTenths))) max 0) * 100 millis
})
},
clockHistory = clock.map { clk =>
val remaining = (clk.remainingTime(turnColor) * 1000).toLong.millis
clockHistory.fold(ClockHistory(remaining)) { history =>
history.record(turnColor)(remaining)
}
},
status = situation.status | status,
clock = game.clock
@ -659,7 +654,7 @@ object Game {
val castleLastMoveTime = "cl"
val unmovedRooks = "ur"
val daysPerTurn = "cd"
val legacyMoveTimes = "mt"
val moveTimes = "mt"
val whiteClockHistory = "cw"
val blackClockHistory = "cb"
val rated = "ra"
@ -710,20 +705,16 @@ object CastleLastMoveTime {
}
case class ClockHistory(
white: Vector[FiniteDuration],
black: Vector[FiniteDuration]
latest: FiniteDuration,
white: Vector[FiniteDuration] = Vector.empty,
black: Vector[FiniteDuration] = Vector.empty
) {
def apply(color: Color): Vector[FiniteDuration] = color.fold(white, black)
def recordWhite(remaining: FiniteDuration) = copy(white = white :+ remaining)
def recordBlack(remaining: FiniteDuration) = copy(black = black :+ remaining)
def recordWhite(remaining: FiniteDuration) = copy(latest = remaining, black = black :+ latest)
def recordBlack(remaining: FiniteDuration) = copy(latest = remaining, white = white :+ latest)
def record(color: Color)(remaining: FiniteDuration): ClockHistory =
color.fold(recordWhite _, recordBlack _)(remaining)
}
object ClockHistory {
def empty = ClockHistory(Vector.empty, Vector.empty)
}

View File

@ -1,8 +1,8 @@
package lila.game
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.duration._
import chess.{ White, Black, Clock, CheckCount, UnmovedRooks }
import chess.{ Color, White, Black, Clock, CheckCount, UnmovedRooks }
import chess.variant.Crazyhouse
import Game.BSONFields._
import org.joda.time.DateTime
@ -41,6 +41,12 @@ private[game] object GameDiff {
}
}
def getClockHistory(color: Color)(g: Game) = for {
history <- g.clockHistory
clk <- g.clock
end = (clk.remainingTime(color) * 1000).toLong.millis
} yield (clk.limit.seconds, end, history(color))
val w = lila.db.BSON.writer
d(binaryPieces, _.binaryPieces, ByteArrayBSONHandler.write)
@ -49,9 +55,9 @@ private[game] object GameDiff {
d(turns, _.turns, w.int)
d(castleLastMoveTime, _.castleLastMoveTime, CastleLastMoveTime.castleLastMoveTimeBSONHandler.write)
d(unmovedRooks, _.unmovedRooks, (x: UnmovedRooks) => ByteArrayBSONHandler.write(BinaryFormat.unmovedRooks write x))
dOpt(legacyMoveTimes, _.legacyMoveTimes, (o: Option[ByteArray]) => o map ByteArrayBSONHandler.write)
dOpt(whiteClockHistory, (g: Game) => g.clockHistory.map(h => (~g.initialClockTime, ~g.remainingClockTime(White), h.white)), (o: Option[Tuple3[FiniteDuration, FiniteDuration, Vector[FiniteDuration]]]) => o.map { case (x, y, z) => ByteArrayBSONHandler.write(BinaryFormat.clockHistory.writeSide(x, y, z)) })
dOpt(blackClockHistory, (g: Game) => g.clockHistory.map(h => (~g.initialClockTime, ~g.remainingClockTime(Black), h.black)), (o: Option[Tuple3[FiniteDuration, FiniteDuration, Vector[FiniteDuration]]]) => o.map { case (x, y, z) => ByteArrayBSONHandler.write(BinaryFormat.clockHistory.writeSide(x, y, z)) })
dOpt(moveTimes, _.binaryMoveTimes, (o: Option[ByteArray]) => o map ByteArrayBSONHandler.write)
dOpt(whiteClockHistory, getClockHistory(White)(_), (o: Option[Tuple3[FiniteDuration, FiniteDuration, Vector[FiniteDuration]]]) => o.map { case (x, y, z) => ByteArrayBSONHandler.write(BinaryFormat.clockHistory.writeSide(x, y, z)) })
dOpt(blackClockHistory, getClockHistory(Black)(_), (o: Option[Tuple3[FiniteDuration, FiniteDuration, Vector[FiniteDuration]]]) => o.map { case (x, y, z) => ByteArrayBSONHandler.write(BinaryFormat.clockHistory.writeSide(x, y, z)) })
dOpt(positionHashes, _.positionHashes, w.bytesO)
dOpt(clock, _.clock, (o: Option[Clock]) => o map { c =>
BSONHandlers.clockBSONWrite(a.createdAt, c)

View File

@ -1,5 +1,7 @@
package lila.game
import scala.concurrent.duration._
import chess.{ Color, White, Black }
import chess.format.{ pgn => chessPgn }
@ -26,6 +28,7 @@ object Rewind {
if (color == game.startColor) (playedTurns + 1) / 2
else playedTurns / 2
}
val newClock = game.clock map (_.takeback)
val newGame = game.copy(
whitePlayer = rewindPlayer(game.whitePlayer),
blackPlayer = rewindPlayer(game.blackPlayer),
@ -41,15 +44,17 @@ object Rewind {
check = if (rewindedSituation.check) rewindedSituation.kingPos else None
),
unmovedRooks = rewindedGame.board.unmovedRooks,
clockHistory = game.clockHistory.map { history =>
ClockHistory(
history.white.take(rewindedPlayerMoves(White)),
history.black.take(rewindedPlayerMoves(Black))
)
},
clockHistory = for {
history <- game.clockHistory
clk <- newClock
} yield ClockHistory(
(clk.remainingTime(rewindedSituation.color) * 1000).toLong.millis,
history.white.take(rewindedPlayerMoves(White) - 2),
history.black.take(rewindedPlayerMoves(Black) - 2)
),
crazyData = rewindedSituation.board.crazyData,
status = game.status,
clock = game.clock map (_.takeback)
clock = newClock
)
Progress(game, newGame, List(
newGame.clock.map(Event.Clock.apply),