Better chess clock and implementation
parent
dd63efc80e
commit
c7fd3d8760
|
@ -0,0 +1,2 @@
|
|||
Game.lastMove (and no more in pieces notation)
|
||||
clock time in milliseconds (and no more floating seconds)
|
|
@ -2,12 +2,13 @@ package lila.chess
|
|||
|
||||
import scala.math.max
|
||||
|
||||
// All durations are expressed in milliseconds
|
||||
sealed trait Clock {
|
||||
val color: Color
|
||||
val whiteTime: Long
|
||||
val blackTime: Long
|
||||
val increment: Int
|
||||
val limit: Int
|
||||
val increment: Int
|
||||
val color: Color
|
||||
val whiteTime: Int
|
||||
val blackTime: Int
|
||||
val timer: Long
|
||||
|
||||
def time(c: Color) = if (c == White) whiteTime else blackTime
|
||||
|
@ -20,50 +21,64 @@ sealed trait Clock {
|
|||
|
||||
def elapsedTime(c: Color) = time(c)
|
||||
|
||||
def limitInMinutes = limit / 60
|
||||
def limitInSeconds = limit / 1000
|
||||
|
||||
def now = System.currentTimeMillis
|
||||
def limitInMinutes = limitInSeconds / 60
|
||||
|
||||
def estimateTotalTime = limit + 30 * increment
|
||||
|
||||
def step: RunningClock
|
||||
|
||||
override def toString =
|
||||
"%d minutes/side + %d seconds/move".format(limitInMinutes, increment)
|
||||
}
|
||||
|
||||
case class RunningClock(
|
||||
color: Color = White,
|
||||
whiteTime: Long = 0l,
|
||||
blackTime: Long = 0l,
|
||||
timer: Long = 0l,
|
||||
limit: Int,
|
||||
increment: Int,
|
||||
limit: Int) extends Clock {
|
||||
color: Color = White,
|
||||
whiteTime: Int = 0,
|
||||
blackTime: Int = 0,
|
||||
timer: Long = 0l) extends Clock {
|
||||
|
||||
override def elapsedTime(c: Color) = time(c) + {
|
||||
if (c == color) now - timer else 0
|
||||
if (c == color) (now - timer).toInt else 0
|
||||
}
|
||||
|
||||
def step =
|
||||
addTime(color, max(0, now - timer - Clock.httpDelay - increment)).copy(
|
||||
addTime(
|
||||
color,
|
||||
max(0, (now - timer).toInt - Clock.httpDelay - increment)
|
||||
).copy(
|
||||
color = !color,
|
||||
timer = now
|
||||
)
|
||||
|
||||
def addTime(c: Color, t: Long) = c match {
|
||||
def addTime(c: Color, t: Int) = c match {
|
||||
case White ⇒ copy(whiteTime = whiteTime + t)
|
||||
case Black ⇒ copy(blackTime = blackTime + t)
|
||||
}
|
||||
|
||||
def giveTime(c: Color, t: Long) = addTime(c, -t)
|
||||
def giveTime(c: Color, t: Int) = addTime(c, -t)
|
||||
|
||||
private def now = System.currentTimeMillis
|
||||
}
|
||||
|
||||
case class PausedClock(
|
||||
color: Color = White,
|
||||
whiteTime: Long = 0l,
|
||||
blackTime: Long = 0l,
|
||||
limit: Int,
|
||||
increment: Int,
|
||||
limit: Int) extends Clock {
|
||||
color: Color = White,
|
||||
whiteTime: Int = 0,
|
||||
blackTime: Int = 0) extends Clock {
|
||||
|
||||
val timer = 0l
|
||||
val timer = 0l
|
||||
|
||||
def step = RunningClock(
|
||||
color = color,
|
||||
whiteTime = whiteTime,
|
||||
blackTime = blackTime,
|
||||
increment = increment,
|
||||
limit = limit).giveTime(White, increment).step
|
||||
}
|
||||
|
||||
object Clock {
|
||||
|
|
|
@ -21,6 +21,7 @@ case class Game(
|
|||
board = move.finalizeAfter,
|
||||
player = !player,
|
||||
turns = turns + 1,
|
||||
clock = clock map (_.step),
|
||||
deads = (for {
|
||||
cpos ← move.capture
|
||||
cpiece ← board(cpos)
|
||||
|
@ -30,12 +31,6 @@ case class Game(
|
|||
(newGame.copy(pgnMoves = (pgnMoves + " " + pgnMove).trim), move)
|
||||
}
|
||||
|
||||
def playMove(
|
||||
orig: Pos,
|
||||
dest: Pos,
|
||||
promotion: PromotableRole = Queen): Valid[Game] =
|
||||
apply(orig, dest, promotion) map (_._1)
|
||||
|
||||
lazy val situation = Situation(board, player)
|
||||
|
||||
def pgnMovesList = pgnMoves.split(' ').toList
|
||||
|
@ -54,5 +49,5 @@ case class Game(
|
|||
*/
|
||||
def fullMoveNumber: Int = 1 + turns / 2
|
||||
|
||||
def updateBoard(f: Board ⇒ Board) = copy(board = f(board))
|
||||
def updateBoard(f: Board => Board) = copy(board = f(board))
|
||||
}
|
||||
|
|
|
@ -25,8 +25,16 @@ trait ChessTest
|
|||
|
||||
def playMoveList(moves: Iterable[(Pos, Pos)]): Valid[Game] =
|
||||
moves.foldLeft(success(game): Valid[Game]) { (vg, move) ⇒
|
||||
vg flatMap { g ⇒ g.playMove(move._1, move._2) }
|
||||
vg flatMap { g ⇒ g(move._1, move._2) map (_._1) }
|
||||
}
|
||||
|
||||
def playMove(
|
||||
orig: Pos,
|
||||
dest: Pos,
|
||||
promotion: PromotableRole = Queen): Valid[Game] =
|
||||
game.apply(orig, dest, promotion) map (_._1)
|
||||
|
||||
def withClock(c: Clock) = game.copy(clock = Some(c))
|
||||
}
|
||||
|
||||
def bePoss(poss: Pos*): Matcher[Option[Iterable[Pos]]] = beSome.like {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package lila.chess
|
||||
|
||||
import Pos._
|
||||
|
||||
class ClockTest extends ChessTest {
|
||||
|
||||
"play with a clock" in {
|
||||
val clock = PausedClock(5 * 60 * 1000, 0)
|
||||
val game = Game() withClock clock
|
||||
"new game" in {
|
||||
game.clock must beSome.like {
|
||||
case c ⇒ c.color must_== White
|
||||
}
|
||||
}
|
||||
"one move played" in {
|
||||
game.playMoves(E2 -> E4) must beSuccess.like {
|
||||
case g ⇒ g.clock must beSome.like {
|
||||
case c ⇒ c.color must_== Black
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ case class RawDbClock(
|
|||
color: String,
|
||||
increment: Int,
|
||||
limit: Int,
|
||||
times: Map[String, Long],
|
||||
times: Map[String, Int],
|
||||
timer: Long = 0l) {
|
||||
|
||||
def decode: Option[Clock] = for {
|
||||
|
|
Loading…
Reference in New Issue