fix move time recording in correspondence
parent
02f94be28c
commit
ae45082d2b
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue