Merge remote-tracking branch 'isaacl/clockCleanup'
* isaacl/clockCleanup: Update chess submodule Fix compile errors Clean up clock API and tests
This commit is contained in:
commit
8dad601b46
|
@ -34,7 +34,7 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
|
|||
val speedAndClock =
|
||||
if (game.imported) "imported"
|
||||
else game.clock.fold(chess.Speed.Correspondence.name) { c =>
|
||||
s"${chess.Speed(c.config).name} (${c.show})"
|
||||
s"${chess.Speed(c.config).name} (${c.config.show})"
|
||||
}
|
||||
val mode = game.mode.name
|
||||
val variant = if (game.variant == chess.variant.FromPosition) "position setup chess"
|
||||
|
@ -174,7 +174,7 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
|
|||
private def gameTitle(game: Game, color: Color): String = {
|
||||
val u1 = playerText(game player color, withRating = true)
|
||||
val u2 = playerText(game opponent color, withRating = true)
|
||||
val clock = game.clock ?? { c => " • " + c.show }
|
||||
val clock = game.clock ?? { c => " • " + c.config.show }
|
||||
val variant = game.variant.exotic ?? s" • ${game.variant.name}"
|
||||
s"$u1 vs $u2$clock$variant"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
@game.variant.name.toUpperCase
|
||||
}
|
||||
} else {
|
||||
@game.clock.map(_.show).getOrElse {
|
||||
@game.clock.map(_.config.show).getOrElse {
|
||||
@game.daysPerTurn.map { days =>
|
||||
<span data-hint="@trans.correspondence()" class="hint--top">@{(days == 1).fold(trans.oneDay(), trans.nbDays(days))}</span>
|
||||
}.getOrElse {
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
@g.variant.name.toUpperCase
|
||||
}
|
||||
} else {
|
||||
@g.clock.map(_.show).getOrElse {
|
||||
@g.clock.map(_.config.show).getOrElse {
|
||||
@g.daysPerTurn.map { days =>
|
||||
<span data-hint="@trans.correspondence()" class="hint--top">@if(days == 1) {@trans.oneDay()} else {@trans.nbDays(days)}</span>
|
||||
}.getOrElse {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
@setup.select(form("timeMode"), translatedTimeModeChoices)
|
||||
</div>
|
||||
<div class="time_choice slider">
|
||||
@trans.minutesPerSide(): <span>@(chess.Clock.showLimit(~form("time").value.map(x => (x.toDouble * 60).toInt)))</span>
|
||||
@trans.minutesPerSide(): <span>@(chess.Clock.Config(~form("time").value.map(x => (x.toDouble * 60).toInt), 0).limitString)</span>
|
||||
@setup.input(form("time"))
|
||||
</div>
|
||||
<div class="increment_choice slider">
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<div class="game_legend">
|
||||
@playerText(pov.opponent, withRating = true)
|
||||
@pov.game.clock.map { c =>
|
||||
• @c.show
|
||||
• @c.config.show
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 5ab69132aaa4203f6280a896a03ec187e566c753
|
||||
Subproject commit 03014d9b3a2d4d861492c3dbe95f72bf71c156fc
|
|
@ -79,60 +79,58 @@ object BinaryFormat {
|
|||
}.take(turns).map(Centis.apply)(breakOut)
|
||||
}
|
||||
|
||||
case class clock(since: DateTime) {
|
||||
|
||||
case class clock(start: Timestamp) {
|
||||
def write(clock: Clock): ByteArray = {
|
||||
Array(writeClockLimit(clock.limitSeconds), clock.incrementSeconds.toByte) ++
|
||||
writeSignedInt24(clock.whiteTime.centis) ++
|
||||
writeSignedInt24(clock.blackTime.centis) ++
|
||||
writeTimer(clock.timerOption.fold(0l)(_.value / 10l))
|
||||
(clock.timerOption map writeTimer getOrElse Array())
|
||||
}
|
||||
|
||||
def read(ba: ByteArray, whiteBerserk: Boolean, blackBerserk: Boolean): Color => Clock = color => ba.value map toInt match {
|
||||
case Array(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12) =>
|
||||
readTimer(b9, b10, b11, b12) match {
|
||||
case 0 => PausedClock(
|
||||
config = Clock.Config(readClockLimit(b1), b2),
|
||||
color = color,
|
||||
whiteTime = Centis(readSignedInt24(b3, b4, b5)),
|
||||
blackTime = Centis(readSignedInt24(b6, b7, b8)),
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk
|
||||
)
|
||||
case timer => RunningClock(
|
||||
config = Clock.Config(readClockLimit(b1), b2),
|
||||
color = color,
|
||||
whiteTime = Centis(readSignedInt24(b3, b4, b5)),
|
||||
blackTime = Centis(readSignedInt24(b6, b7, b8)),
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk,
|
||||
timer = Timestamp(timer * 10l)
|
||||
)
|
||||
def read(ba: ByteArray, whiteBerserk: Boolean, blackBerserk: Boolean): Color => Clock = color => {
|
||||
val ia = ba.value map toInt
|
||||
|
||||
// ba.size might be greater than 12 with 5 bytes timers
|
||||
// ba.size might be 8 if there was no timer.
|
||||
// #TODO remove 5 byte timer case! But fix the DB first!
|
||||
val timer = {
|
||||
if (ia.size == 12) readTimer(readInt(ia(8), ia(9), ia(10), ia(11)))
|
||||
else None
|
||||
}
|
||||
|
||||
ia match {
|
||||
case Array(b1, b2, b3, b4, b5, b6, b7, b8, _*) => {
|
||||
val config = Clock.Config(readClockLimit(b1), b2)
|
||||
val whiteTime = Centis(readSignedInt24(b3, b4, b5))
|
||||
val blackTime = Centis(readSignedInt24(b6, b7, b8))
|
||||
timer.fold[Clock](
|
||||
PausedClock(
|
||||
config = config,
|
||||
color = color,
|
||||
whiteTime = whiteTime,
|
||||
blackTime = blackTime,
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk
|
||||
)
|
||||
)(t =>
|
||||
RunningClock(
|
||||
config = config,
|
||||
color = color,
|
||||
whiteTime = whiteTime,
|
||||
blackTime = blackTime,
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk,
|
||||
timer = t
|
||||
))
|
||||
}
|
||||
// compatibility with 5 bytes timers
|
||||
// #TODO remove me! But fix the DB first!
|
||||
case Array(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, _) =>
|
||||
PausedClock(
|
||||
config = Clock.Config(readClockLimit(b1), b2),
|
||||
color = color,
|
||||
whiteTime = Centis(readSignedInt24(b3, b4, b5)),
|
||||
blackTime = Centis(readSignedInt24(b6, b7, b8)),
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk
|
||||
)
|
||||
case _ => sys error s"BinaryFormat.clock.read invalid bytes: ${ba.showBytes}"
|
||||
case _ => sys error s"BinaryFormat.clock.read invalid bytes: ${ba.showBytes}"
|
||||
}
|
||||
}
|
||||
|
||||
private def decay = (since.getMillis / 10) - 10
|
||||
private def writeTimer(timer: Timestamp) = writeInt((timer - start).centis)
|
||||
|
||||
private def writeTimer(long: Long) = {
|
||||
writeInt(math.max(0l, long - decay).toInt)
|
||||
}
|
||||
|
||||
private def readTimer(b1: Int, b2: Int, b3: Int, b4: Int) = {
|
||||
val l = readInt(b1, b2, b3, b4)
|
||||
if (l == 0) 0 else l + decay
|
||||
}
|
||||
private def readTimer(l: Int) =
|
||||
if (l != 0) Some(start + Centis(l)) else None
|
||||
|
||||
private def writeClockLimit(limit: Int): Byte = {
|
||||
// The database expects a byte for a limit, and this is limit / 60.
|
||||
|
@ -149,6 +147,10 @@ object BinaryFormat {
|
|||
}
|
||||
}
|
||||
|
||||
object clock {
|
||||
def apply(start: DateTime) = new clock(Timestamp(start.getMillis))
|
||||
}
|
||||
|
||||
object castleLastMoveTime {
|
||||
|
||||
def write(clmt: CastleLastMoveTime): ByteArray = {
|
||||
|
|
|
@ -220,7 +220,7 @@ case class Game(
|
|||
binaryMoveTimes.?? { t =>
|
||||
BinaryFormat.moveTime.read(t, playedTurns)
|
||||
} :+ {
|
||||
(Centis(nowCentis - movedAt.getCentis) - ~lag) nonNeg
|
||||
Centis(nowCentis - movedAt.getCentis) nonNeg
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -365,7 +365,7 @@ case class Game(
|
|||
|
||||
def goBerserk(color: Color) =
|
||||
clock.ifTrue(berserkable && !player(color).berserk).map { c =>
|
||||
val newClock = c berserk color
|
||||
val newClock = c goBerserk color
|
||||
Progress(this, copy(
|
||||
clock = Some(newClock),
|
||||
clockHistory = clockHistory.map(history => {
|
||||
|
@ -456,7 +456,7 @@ case class Game(
|
|||
}
|
||||
|
||||
private def outoftimeCorrespondence: Boolean =
|
||||
playableCorrespondenceClock ?? { _ outoftime player.color }
|
||||
playableCorrespondenceClock ?? { _ outoftime turnColor }
|
||||
|
||||
def isCorrespondence = speed == chess.Speed.Correspondence
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila.game
|
|||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import chess.{ Centis, Clock }
|
||||
import chess.{ Centis, Clock, White, Black }
|
||||
import org.specs2.mutable._
|
||||
import org.specs2.specification._
|
||||
|
||||
|
@ -12,57 +12,82 @@ class BinaryClockTest extends Specification {
|
|||
|
||||
val _0_ = "00000000"
|
||||
val since = org.joda.time.DateTime.now.minusHours(1)
|
||||
def write(c: Clock): List[String] =
|
||||
(BinaryFormat.clock(since) write c).showBytes.split(',').toList
|
||||
def read(bytes: List[String]): Clock =
|
||||
(BinaryFormat.clock(since).read(ByteArray.parseBytes(bytes), false, false))(chess.White)
|
||||
def isomorphism(c: Clock): Clock =
|
||||
(BinaryFormat.clock(since).read(BinaryFormat.clock(since) write c, false, false))(chess.White)
|
||||
def writeBytes(c: Clock) = BinaryFormat.clock(since) write c
|
||||
def readBytes(bytes: ByteArray, berserk: Boolean = false): Clock =
|
||||
(BinaryFormat.clock(since).read(bytes, berserk, false))(White)
|
||||
def isomorphism(c: Clock): Clock = readBytes(writeBytes(c))
|
||||
|
||||
def write(c: Clock): List[String] = writeBytes(c).showBytes.split(',').toList
|
||||
def read(bytes: List[String]) = readBytes(ByteArray.parseBytes(bytes))
|
||||
|
||||
"binary Clock" should {
|
||||
val clock = Clock(120, 2)
|
||||
val bits22 = List("00000010", "00000010")
|
||||
"write" in {
|
||||
write(clock) must_== {
|
||||
bits22 ::: List.fill(10)(_0_)
|
||||
bits22 ::: List.fill(6)(_0_)
|
||||
}
|
||||
write(clock.giveTime(chess.White, Centis(3))) must_== {
|
||||
bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(7)(_0_)
|
||||
write(clock.giveTime(White, Centis(3))) must_== {
|
||||
bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(3)(_0_)
|
||||
}
|
||||
write(clock.giveTime(chess.White, Centis(-3))) must_== {
|
||||
bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(7)(_0_)
|
||||
write(clock.giveTime(White, Centis(-3))) must_== {
|
||||
bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(3)(_0_)
|
||||
}
|
||||
write(Clock(0, 3)) must_== {
|
||||
List("00000000", "00000011", "10000000", "00000001", "00101100", "10000000", "00000001", "00101100") ::: List.fill(4)(_0_)
|
||||
List("00000000", "00000011", "10000000", "00000001", "00101100", "10000000", "00000001", "00101100")
|
||||
}
|
||||
}
|
||||
"read" in {
|
||||
read(bits22 ::: List.fill(11)(_0_)) must_== {
|
||||
clock
|
||||
"with timer" in {
|
||||
read(bits22 ::: List.fill(11)(_0_)) must_== {
|
||||
clock
|
||||
}
|
||||
read(bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(8)(_0_)) must_== {
|
||||
clock.giveTime(White, Centis(3))
|
||||
}
|
||||
read(bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(8)(_0_)) must_== {
|
||||
clock.giveTime(White, Centis(-3))
|
||||
}
|
||||
}
|
||||
read(bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(8)(_0_)) must_== {
|
||||
clock.giveTime(chess.White, Centis(3))
|
||||
}
|
||||
read(bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(8)(_0_)) must_== {
|
||||
clock.giveTime(chess.White, Centis(-3))
|
||||
"without timer bytes" in {
|
||||
read(bits22 ::: List.fill(7)(_0_)) must_== {
|
||||
clock
|
||||
}
|
||||
read(bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(4)(_0_)) must_== {
|
||||
clock.giveTime(White, Centis(3))
|
||||
}
|
||||
read(bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(4)(_0_)) must_== {
|
||||
clock.giveTime(White, Centis(-3))
|
||||
}
|
||||
}
|
||||
}
|
||||
"isomorphism" in {
|
||||
|
||||
isomorphism(clock) must_== clock
|
||||
|
||||
val c2 = clock.giveTime(chess.White, Centis.ofSeconds(15))
|
||||
val c2 = clock.giveTime(White, Centis.ofSeconds(15))
|
||||
isomorphism(c2) must_== c2
|
||||
|
||||
val c3 = clock.giveTime(chess.Black, Centis.ofSeconds(5))
|
||||
isomorphism(c3) must_== c3
|
||||
|
||||
val c4 = clock.start
|
||||
isomorphism(c4).timerOption.get.value must beCloseTo(c4.timerOption.get.value, 10)
|
||||
isomorphism(c4).timerOption.get.value must beCloseTo(c4.timer.value, 10)
|
||||
|
||||
Clock(120, 60) |> { c =>
|
||||
isomorphism(c) must_== c
|
||||
}
|
||||
|
||||
"with berserk" in {
|
||||
val b1 = clock.goBerserk(White)
|
||||
readBytes(writeBytes(b1), true) must_== b1
|
||||
|
||||
val b2 = clock.giveTime(White, Centis(15)).goBerserk(White)
|
||||
readBytes(writeBytes(b2), true) must_== b2
|
||||
|
||||
val b3 = Clock(60, 2).goBerserk(White)
|
||||
readBytes(writeBytes(b3), true) must_== b3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ private final class GameJson(
|
|||
tree = TreeBuilder(game, plies)
|
||||
} yield Json.obj(
|
||||
"id" -> game.id,
|
||||
"clock" -> game.clock.map(_.show),
|
||||
"clock" -> game.clock.map(_.config.show),
|
||||
"perf" -> Json.obj(
|
||||
"icon" -> perfType.iconChar.toString,
|
||||
"name" -> perfType.name
|
||||
|
|
|
@ -355,7 +355,7 @@ object JsonView {
|
|||
"increment" -> c.incrementSeconds,
|
||||
"white" -> c.remainingTime(Color.White).toSeconds,
|
||||
"black" -> c.remainingTime(Color.Black).toSeconds,
|
||||
"emerg" -> c.emergTime
|
||||
"emerg" -> c.config.emergSeconds
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila.round
|
|||
|
||||
import chess.format.Forsyth
|
||||
import chess.variant._
|
||||
import chess.{ Game => ChessGame, Board, Color => ChessColor, Castles }
|
||||
import chess.{ Clock, Game => ChessGame, Board, Color => ChessColor, Castles }
|
||||
import ChessColor.{ White, Black }
|
||||
|
||||
import lila.game.{ GameRepo, Game, Event, Progress, Pov, Source, AnonCookie, PerfPicker }
|
||||
|
@ -77,7 +77,7 @@ private[round] final class Rematcher(
|
|||
board = Board(pieces, variant = pov.game.variant).withCastles {
|
||||
situation.fold(Castles.init)(_.situation.board.history.castles)
|
||||
},
|
||||
clock = pov.game.clock map (_.reset),
|
||||
clock = pov.game.clock map { c => Clock(c.config) },
|
||||
turns = situation ?? (_.turns),
|
||||
startedAtTurn = situation ?? (_.turns)
|
||||
),
|
||||
|
|
|
@ -45,7 +45,7 @@ object DataForm {
|
|||
val clockTimesPrivate: Seq[Double] = clockTimes ++ (10d to 30d by 5d) ++ (40d to 60d by 10d)
|
||||
val clockTimeDefault = 2d
|
||||
private def formatLimit(l: Double) =
|
||||
chess.Clock.showLimit(l * 60 toInt) + {
|
||||
chess.Clock.Config(l * 60 toInt, 0).limitString + {
|
||||
if (l <= 1) " minute" else " minutes"
|
||||
}
|
||||
val clockTimeChoices = optionsDouble(clockTimes, formatLimit)
|
||||
|
|
|
@ -3,9 +3,7 @@ var makeAckable = require('./ackable');
|
|||
// versioned events, acks, retries, resync
|
||||
lichess.StrongSocket = function(url, version, settings) {
|
||||
|
||||
var now = function() {
|
||||
return Date.now();
|
||||
};
|
||||
var now = Date.now;
|
||||
|
||||
var settings = $.extend(true, {}, lichess.StrongSocket.defaults, settings);
|
||||
var url = url;
|
||||
|
|
Loading…
Reference in a new issue