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
|
import scala.math.max
|
||||||
|
|
||||||
|
// All durations are expressed in milliseconds
|
||||||
sealed trait Clock {
|
sealed trait Clock {
|
||||||
val color: Color
|
|
||||||
val whiteTime: Long
|
|
||||||
val blackTime: Long
|
|
||||||
val increment: Int
|
|
||||||
val limit: Int
|
val limit: Int
|
||||||
|
val increment: Int
|
||||||
|
val color: Color
|
||||||
|
val whiteTime: Int
|
||||||
|
val blackTime: Int
|
||||||
val timer: Long
|
val timer: Long
|
||||||
|
|
||||||
def time(c: Color) = if (c == White) whiteTime else blackTime
|
def time(c: Color) = if (c == White) whiteTime else blackTime
|
||||||
|
@ -20,50 +21,64 @@ sealed trait Clock {
|
||||||
|
|
||||||
def elapsedTime(c: Color) = time(c)
|
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 estimateTotalTime = limit + 30 * increment
|
||||||
|
|
||||||
|
def step: RunningClock
|
||||||
|
|
||||||
override def toString =
|
override def toString =
|
||||||
"%d minutes/side + %d seconds/move".format(limitInMinutes, increment)
|
"%d minutes/side + %d seconds/move".format(limitInMinutes, increment)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class RunningClock(
|
case class RunningClock(
|
||||||
color: Color = White,
|
limit: Int,
|
||||||
whiteTime: Long = 0l,
|
|
||||||
blackTime: Long = 0l,
|
|
||||||
timer: Long = 0l,
|
|
||||||
increment: 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) + {
|
override def elapsedTime(c: Color) = time(c) + {
|
||||||
if (c == color) now - timer else 0
|
if (c == color) (now - timer).toInt else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
def step =
|
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,
|
color = !color,
|
||||||
timer = now
|
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 White ⇒ copy(whiteTime = whiteTime + t)
|
||||||
case Black ⇒ copy(blackTime = blackTime + 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(
|
case class PausedClock(
|
||||||
color: Color = White,
|
limit: Int,
|
||||||
whiteTime: Long = 0l,
|
|
||||||
blackTime: Long = 0l,
|
|
||||||
increment: 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 {
|
object Clock {
|
||||||
|
|
|
@ -21,6 +21,7 @@ case class Game(
|
||||||
board = move.finalizeAfter,
|
board = move.finalizeAfter,
|
||||||
player = !player,
|
player = !player,
|
||||||
turns = turns + 1,
|
turns = turns + 1,
|
||||||
|
clock = clock map (_.step),
|
||||||
deads = (for {
|
deads = (for {
|
||||||
cpos ← move.capture
|
cpos ← move.capture
|
||||||
cpiece ← board(cpos)
|
cpiece ← board(cpos)
|
||||||
|
@ -30,12 +31,6 @@ case class Game(
|
||||||
(newGame.copy(pgnMoves = (pgnMoves + " " + pgnMove).trim), move)
|
(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)
|
lazy val situation = Situation(board, player)
|
||||||
|
|
||||||
def pgnMovesList = pgnMoves.split(' ').toList
|
def pgnMovesList = pgnMoves.split(' ').toList
|
||||||
|
@ -54,5 +49,5 @@ case class Game(
|
||||||
*/
|
*/
|
||||||
def fullMoveNumber: Int = 1 + turns / 2
|
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] =
|
def playMoveList(moves: Iterable[(Pos, Pos)]): Valid[Game] =
|
||||||
moves.foldLeft(success(game): Valid[Game]) { (vg, move) ⇒
|
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 {
|
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,
|
color: String,
|
||||||
increment: Int,
|
increment: Int,
|
||||||
limit: Int,
|
limit: Int,
|
||||||
times: Map[String, Long],
|
times: Map[String, Int],
|
||||||
timer: Long = 0l) {
|
timer: Long = 0l) {
|
||||||
|
|
||||||
def decode: Option[Clock] = for {
|
def decode: Option[Clock] = for {
|
||||||
|
|
Loading…
Reference in New Issue