remove CastleLastMove from Game

irwin-new-api
Thibault Duplessis 2018-01-22 17:30:23 -05:00
parent e14790191a
commit 1b2878c17b
19 changed files with 104 additions and 79 deletions

View File

@ -31,7 +31,7 @@ trait ChessgroundHelper {
def chessground(pov: Pov)(implicit ctx: Context): Html = chessground(
board = pov.game.toChess.board,
orient = pov.color,
lastMove = pov.game.castleLastMoveTime.lastMove.?? {
lastMove = pov.game.lastMove.map(_.origDest) ?? {
case (orig, dest) => List(orig, dest)
}
)

View File

@ -219,7 +219,7 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
val cssClass = isLive ?? ("live live_" + game.id)
val live = isLive ?? game.id
val fen = Forsyth exportBoard game.toChess.board
val lastMove = ~game.castleLastMoveTime.lastMoveString
val lastMove = ~game.lastMoveKeys
val variant = game.variant.key
val tag = if (withLink) "a" else "span"
s"""<$tag $href $title class="mini_board mini_board_${game.id} parse_fen is2d $cssClass $variant" data-live="$live" data-color="${pov.color.name}" data-fen="$fen" data-lastmove="$lastMove">$miniBoardContent</$tag>"""
@ -236,7 +236,7 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
isLive ?? pov.game.id,
pov.color.name,
Forsyth exportBoard pov.game.toChess.board,
~pov.game.castleLastMoveTime.lastMoveString,
~pov.game.lastMoveKeys,
blank ?? """ target="_blank""""
)
}

View File

@ -40,7 +40,7 @@ final class LobbyApi(
"gameId" -> pov.gameId,
"fen" -> (chess.format.Forsyth exportBoard pov.game.toChess.board),
"color" -> pov.color.name,
"lastMove" -> ~pov.game.castleLastMoveTime.lastMoveString,
"lastMove" -> ~pov.game.lastMoveKeys,
"variant" -> Json.obj(
"key" -> pov.game.variant.key,
"name" -> pov.game.variant.name

View File

@ -50,7 +50,7 @@ final class UserGameApi(
).add("bookmarked" -> bookmarked)
.add("analysed" -> g.metadata.analysed)
.add("opening" -> g.opening)
.add("lastMove" -> g.castleLastMoveTime.lastMoveString)
.add("lastMove" -> g.lastMoveKeys)
.add("clock" -> g.clock)
.add("correspondence" -> g.daysPerTurn.map { d =>
Json.obj("daysPerTurn" -> d)

View File

@ -48,10 +48,8 @@ private[challenge] final class Joiner(onStart: String => Unit) {
state.fold(g) {
case sit @ SituationPlus(Situation(board, _), _) => g.copy(
variant = chess.variant.FromPosition,
castleLastMoveTime = g.castleLastMoveTime.copy(
lastMove = board.history.lastMove.map(_.origDest),
castles = board.history.castles
),
lastMove = board.history.lastMove,
castles = board.history.castles,
turns = sit.turns
)
}

View File

@ -79,13 +79,18 @@ object BSONHandlers {
val gameVariant = Variant(r intD variant) | chess.variant.Standard
val plies = r int turns
val decoded = r.bytesO(huffmanPgn).map { PgnStorage.Huffman.decode(_, plies) } getOrElse PgnStorage.Decoded(
pgnMoves = PgnStorage.OldBin.decode(r bytesD oldPgn, plies),
pieces = BinaryFormat.piece.read(r bytes binaryPieces, gameVariant),
positionHashes = r.getO[chess.PositionHash](positionHashes) | Array.empty,
unmovedRooks = r.getO[UnmovedRooks](unmovedRooks) | UnmovedRooks.default,
format = PgnStorage.OldBin
)
val decoded = r.bytesO(huffmanPgn).map { PgnStorage.Huffman.decode(_, plies) } | {
val clm = r.get[CastleLastMove](castleLastMove)
PgnStorage.Decoded(
pgnMoves = PgnStorage.OldBin.decode(r bytesD oldPgn, plies),
pieces = BinaryFormat.piece.read(r bytes binaryPieces, gameVariant),
positionHashes = r.getO[chess.PositionHash](positionHashes) | Array.empty,
unmovedRooks = r.getO[UnmovedRooks](unmovedRooks) | UnmovedRooks.default,
lastMove = clm.lastMove,
castles = clm.castles,
format = PgnStorage.OldBin
)
}
val g = Game(
id = r str id,
@ -95,6 +100,8 @@ object BSONHandlers {
pieces = decoded.pieces,
positionHashes = decoded.positionHashes,
unmovedRooks = decoded.unmovedRooks,
lastMove = decoded.lastMove,
castles = decoded.castles,
pgnStorage = decoded.format,
status = r.get[Status](status),
turns = plies,
@ -103,7 +110,6 @@ object BSONHandlers {
val counts = r.intsD(checkCount)
CheckCount(~counts.headOption, ~counts.lastOption)
},
castleLastMoveTime = r.get[CastleLastMoveTime](castleLastMoveTime)(CastleLastMoveTime.castleLastMoveTimeBSONHandler),
daysPerTurn = r intO daysPerTurn,
binaryMoveTimes = r bytesO moveTimes,
mode = Mode(r boolD rated),
@ -147,7 +153,6 @@ object BSONHandlers {
startedAtTurn -> w.intO(o.startedAtTurn),
clock -> (o.clock map { c => clockBSONWrite(o.createdAt, c) }),
checkCount -> o.checkCount.nonEmpty.option(o.checkCount),
castleLastMoveTime -> CastleLastMoveTime.castleLastMoveTimeBSONHandler.write(o.castleLastMoveTime),
daysPerTurn -> o.daysPerTurn,
moveTimes -> o.binaryMoveTimes,
whiteClockHistory -> clockHistory(White, o.clockHistory, o.clock, o.flagged),
@ -171,7 +176,12 @@ object BSONHandlers {
oldPgn -> f.encode(o.pgnMoves),
binaryPieces -> BinaryFormat.piece.write(o.pieces),
positionHashes -> o.positionHashes,
unmovedRooks -> o.unmovedRooks
unmovedRooks -> o.unmovedRooks,
castleLastMove -> CastleLastMove.castleLastMoveBSONHandler.write(CastleLastMove(
castles = o.castles,
lastMove = o.lastMove,
check = o.toChess.situation.checkSquare
))
)
case f @ PgnStorage.Huffman => $doc(
huffmanPgn -> f.encode(o.pgnMoves)

View File

@ -8,6 +8,7 @@ import scala.util.Try
import chess.variant.Variant
import chess.{ ToOptionOpsFromOption => _, _ }
import chess.format.Uci
import org.lichess.compression.clock.{ Encoder => ClockEncoder }
import lila.common.Chronometer
@ -165,9 +166,9 @@ object BinaryFormat {
def apply(start: DateTime) = new clock(Timestamp(start.getMillis))
}
object castleLastMoveTime {
object castleLastMove {
def write(clmt: CastleLastMoveTime): ByteArray = {
def write(clmt: CastleLastMove): ByteArray = {
val castleInt = clmt.castles.toSeq.zipWithIndex.foldLeft(0) {
case (acc, (false, _)) => acc
@ -175,14 +176,14 @@ object BinaryFormat {
}
def posInt(pos: Pos): Int = ((pos.x - 1) << 3) + pos.y - 1
val lastMoveInt = clmt.lastMove.fold(0) {
case (f, t) => (posInt(f) << 6) + posInt(t)
val lastMoveInt = clmt.lastMove.map(_.origDest).fold(0) {
case (o, d) => (posInt(o) << 6) + posInt(d)
}
Array((castleInt << 4) + (lastMoveInt >> 8) toByte, lastMoveInt.toByte) ++
clmt.check.map(x => posInt(x).toByte)
}
def read(ba: ByteArray): CastleLastMoveTime = {
def read(ba: ByteArray): CastleLastMove = {
val ints = ba.value map toInt
val size = ints.size
@ -195,13 +196,13 @@ object BinaryFormat {
private def posAt(x: Int, y: Int) = Pos.posAt(x + 1, y + 1)
private def doRead(b1: Int, b2: Int, checkByte: Option[Int]) =
CastleLastMoveTime(
CastleLastMove(
castles = Castles(b1 > 127, (b1 & 64) != 0, (b1 & 32) != 0, (b1 & 16) != 0),
lastMove = for {
from posAt((b1 & 15) >> 1, ((b1 & 1) << 2) + (b2 >> 6))
to posAt((b2 & 63) >> 3, b2 & 7)
if from != Pos.A1 || to != Pos.A1
} yield from -> to,
orig posAt((b1 & 15) >> 1, ((b1 & 1) << 2) + (b2 >> 6))
dest posAt((b2 & 63) >> 3, b2 & 7)
if orig != Pos.A1 || dest != Pos.A1
} yield Uci.Move(orig, dest),
check = checkByte flatMap { x => posAt(x >> 3, x & 7) }
)
}

View File

@ -20,12 +20,13 @@ case class Game(
pieces: PieceMap,
positionHashes: PositionHash,
unmovedRooks: UnmovedRooks,
lastMove: Option[Uci],
castles: Castles,
pgnStorage: PgnStorage,
status: Status,
turns: Int, // = ply
startedAtTurn: Int,
clock: Option[Clock] = None,
castleLastMoveTime: CastleLastMoveTime,
daysPerTurn: Option[Int],
checkCount: CheckCount = CheckCount(0, 0),
binaryMoveTimes: Option[ByteArray] = None,
@ -169,10 +170,8 @@ case class Game(
)
lazy val toChessHistory = ChessHistory(
lastMove = castleLastMoveTime.lastMove map {
case (orig, dest) => Uci.Move(orig, dest)
},
castles = castleLastMoveTime.castles,
lastMove = lastMove,
castles = castles,
positionHashes = positionHashes,
checkCount = checkCount,
unmovedRooks = unmovedRooks
@ -200,14 +199,11 @@ case class Game(
pieces = game.board.pieces,
positionHashes = history.positionHashes,
unmovedRooks = game.board.unmovedRooks,
lastMove = history.lastMove,
castles = history.castles,
turns = game.turns,
checkCount = history.checkCount,
crazyData = situation.board.crazyData,
castleLastMoveTime = CastleLastMoveTime(
castles = history.castles,
lastMove = history.lastMove.map(_.origDest),
check = situation.checkSquare
),
binaryMoveTimes = (!isPgnImport && !clock.isDefined).option {
BinaryFormat.moveTime.write {
binaryMoveTimes.?? { t =>
@ -254,7 +250,12 @@ case class Game(
Progress(this, updated, events)
}
def check = castleLastMoveTime.check
def check = toChess.situation.checkSquare
def lastMoveKeys: Option[String] = lastMove map {
case Uci.Drop(target, _) => s"$target$target"
case m: Uci.Move => m.keys
}
def updatePlayer(color: Color, f: Player => Player) = color.fold(
copy(whitePlayer = f(whitePlayer)),
@ -666,12 +667,13 @@ object Game {
pieces = game.board.pieces,
positionHashes = game.board.history.positionHashes,
unmovedRooks = game.board.unmovedRooks,
lastMove = none,
castles = game.board.history.castles,
pgnStorage = PgnStorage(variant, List(whitePlayer.userId, blackPlayer.userId).flatten),
status = Status.Created,
turns = game.turns,
startedAtTurn = game.startedAtTurn,
clock = game.clock,
castleLastMoveTime = CastleLastMoveTime.init.copy(castles = game.board.history.castles),
daysPerTurn = daysPerTurn,
mode = mode,
variant = variant,
@ -706,7 +708,7 @@ object Game {
val clock = "c"
val positionHashes = "ph"
val checkCount = "cc"
val castleLastMoveTime = "cl"
val castleLastMove = "cl"
val unmovedRooks = "ur"
val daysPerTurn = "cd"
val moveTimes = "mt"
@ -732,28 +734,28 @@ object Game {
}
}
case class CastleLastMoveTime(
case class CastleLastMove(
castles: Castles,
lastMove: Option[(Pos, Pos)],
lastMove: Option[Uci],
check: Option[Pos]
) {
def lastMoveString = lastMove map { case (a, b) => s"$a$b" }
def lastMoveString = lastMove map (_.origDest)
}
object CastleLastMoveTime {
object CastleLastMove {
def init = CastleLastMoveTime(Castles.all, None, None)
def init = CastleLastMove(Castles.all, None, None)
import reactivemongo.bson._
import lila.db.ByteArray.ByteArrayBSONHandler
private[game] implicit val castleLastMoveTimeBSONHandler = new BSONHandler[BSONBinary, CastleLastMoveTime] {
def read(bin: BSONBinary) = BinaryFormat.castleLastMoveTime read {
private[game] implicit val castleLastMoveBSONHandler = new BSONHandler[BSONBinary, CastleLastMove] {
def read(bin: BSONBinary) = BinaryFormat.castleLastMove read {
ByteArrayBSONHandler read bin
}
def write(clmt: CastleLastMoveTime) = ByteArrayBSONHandler write {
BinaryFormat.castleLastMoveTime write clmt
def write(clmt: CastleLastMove) = ByteArrayBSONHandler write {
BinaryFormat.castleLastMove write clmt
}
}
}

View File

@ -62,12 +62,12 @@ private[game] object GameDiff {
d(binaryPieces, _.pieces, writeBytes compose BinaryFormat.piece.write)
d(positionHashes, _.positionHashes, w.bytes)
d(unmovedRooks, _.unmovedRooks, writeBytes compose BinaryFormat.unmovedRooks.write)
d(castleLastMove, makeCastleLastMove, CastleLastMove.castleLastMoveBSONHandler.write)
case f @ PgnStorage.Huffman =>
d(huffmanPgn, _.pgnMoves, writeBytes compose f.encode)
}
d(status, _.status.id, w.int)
d(turns, _.turns, w.int)
d(castleLastMoveTime, _.castleLastMoveTime, CastleLastMoveTime.castleLastMoveTimeBSONHandler.write)
dOpt(moveTimes, _.binaryMoveTimes, (o: Option[ByteArray]) => o map ByteArrayBSONHandler.write)
dOpt(whiteClockHistory, getClockHistory(White), clockHistoryToBytes)
dOpt(blackClockHistory, getClockHistory(Black), clockHistoryToBytes)
@ -95,4 +95,10 @@ private[game] object GameDiff {
private val bTrue = BSONBoolean(true)
private val writeBytes = ByteArrayBSONHandler.write _
private def makeCastleLastMove(g: Game) = CastleLastMove(
lastMove = g.lastMove,
castles = g.castles,
check = g.check
)
}

View File

@ -26,7 +26,7 @@ object JsonView {
.add("boosted" -> game.boosted)
.add("tournamentId" -> game.tournamentId)
.add("winner" -> game.winnerColor)
.add("lastMove" -> game.castleLastMoveTime.lastMoveString)
.add("lastMove" -> game.lastMoveKeys)
.add("check" -> game.check.map(_.key))
.add("rematch" -> game.next)

View File

@ -1,7 +1,8 @@
package lila.game
import chess.{ variant => _, ToOptionOpsFromOption => _, _ }
import chess.format.Uci
import chess.variant.Variant
import chess.{ variant => _, ToOptionOpsFromOption => _, _ }
import lila.db.ByteArray
sealed trait PgnStorage
@ -33,11 +34,21 @@ object PgnStorage {
}
def decode(bytes: ByteArray, plies: Int): Decoded = monitor(lila.mon.game.pgn.huffman.decode) {
val decoded = Encoder.decode(bytes.value, plies)
val unmovedRooks = asScalaSet(decoded.unmovedRooks.flatMap(javaPos)).toSet
Decoded(
pgnMoves = decoded.pgnMoves.toVector,
pieces = mapAsScalaMap(decoded.pieces).flatMap { case (k, v) => javaPos(k).map(_ -> javaPiece(v)) }.toMap,
pieces = mapAsScalaMap(decoded.pieces).flatMap {
case (k, v) => javaPos(k).map(_ -> javaPiece(v))
}.toMap,
positionHashes = decoded.positionHashes,
unmovedRooks = UnmovedRooks(asScalaSet(decoded.unmovedRooks).flatMap(javaPos).toSet),
unmovedRooks = UnmovedRooks(unmovedRooks),
lastMove = Option(decoded.lastUci) flatMap Uci.apply,
castles = Castles(
whiteKingSide = unmovedRooks(Pos.H1),
whiteQueenSide = unmovedRooks(Pos.A1),
blackKingSide = unmovedRooks(Pos.H8),
blackQueenSide = unmovedRooks(Pos.A8)
),
format = Huffman
)
}
@ -60,6 +71,8 @@ object PgnStorage {
pieces: PieceMap,
positionHashes: PositionHash, // irrelevant after game ends
unmovedRooks: UnmovedRooks, // irrelevant after game ends
lastMove: Option[Uci],
castles: Castles,
format: PgnStorage
)

View File

@ -4,13 +4,13 @@ import play.api.libs.iteratee._
import play.api.libs.ws.WS
import play.api.Play.current
import chess.format.{ Forsyth, FEN }
import chess.format.{ Forsyth, FEN, Uci }
final class PngExport(url: String, size: Int) {
def fromGame(game: Game): Fu[Enumerator[Array[Byte]]] = apply(
fen = FEN(Forsyth >> game.toChess),
lastMove = game.castleLastMoveTime.lastMoveString,
lastMove = game.lastMoveKeys,
check = game.toChess.situation.checkSquare,
orientation = game.firstColor.some,
logHint = s"game ${game.id}"

View File

@ -38,11 +38,8 @@ object Rewind {
unmovedRooks = rewindedGame.board.unmovedRooks,
turns = rewindedGame.turns,
checkCount = rewindedHistory.checkCount,
castleLastMoveTime = CastleLastMoveTime(
castles = rewindedHistory.castles,
lastMove = rewindedHistory.lastMove.map(_.origDest),
check = if (rewindedSituation.check) rewindedSituation.kingPos else None
),
lastMove = rewindedHistory.lastMove,
castles = rewindedHistory.castles,
binaryMoveTimes = game.binaryMoveTimes.map { binary =>
val moveTimes = BinaryFormat.moveTime.read(binary, game.playedTurns)
BinaryFormat.moveTime.write(moveTimes.dropRight(1))

View File

@ -9,14 +9,14 @@ import lila.db.ByteArray
class BinaryCLMTTest extends Specification {
val _0_ = "00000000"
def write(all: CastleLastMoveTime): List[String] =
(BinaryFormat.castleLastMoveTime write all).showBytes.split(',').toList
def read(bytes: List[String]): CastleLastMoveTime =
BinaryFormat.castleLastMoveTime read ByteArray.parseBytes(bytes)
def write(all: CastleLastMove): List[String] =
(BinaryFormat.castleLastMove write all).showBytes.split(',').toList
def read(bytes: List[String]): CastleLastMove =
BinaryFormat.CastleLastMove read ByteArray.parseBytes(bytes)
"binary CastleLastMoveTime" should {
"binary CastleLastMove" should {
"write" in {
val clmt = CastleLastMoveTime.init
val clmt = CastleLastMove.init
write(clmt) must_== {
"11110000" :: _0_ :: Nil
}
@ -43,7 +43,7 @@ class BinaryCLMTTest extends Specification {
}
}
"read" in {
val clmt = CastleLastMoveTime.init
val clmt = CastleLastMove.init
read("11110000" :: _0_ :: List.fill(3)(_0_)) must_== {
clmt
}

View File

@ -51,7 +51,7 @@ object Chart {
"id" -> pov.game.id,
"fen" -> (chess.format.Forsyth exportBoard pov.game.toChess.board),
"color" -> pov.player.color.name,
"lastMove" -> ~pov.game.castleLastMoveTime.lastMoveString,
"lastMove" -> ~pov.game.lastMoveKeys,
"user1" -> gameUserJson(pov.player),
"user2" -> gameUserJson(pov.opponent)
)

View File

@ -41,7 +41,7 @@ private final class PushApi(
"fullId" -> pov.fullId,
"color" -> pov.color.name,
"fen" -> Forsyth.exportBoard(game.toChess.board),
"lastMove" -> game.castleLastMoveTime.lastMoveString,
"lastMove" -> game.lastMoveKeys,
"win" -> pov.win
)
)
@ -140,7 +140,7 @@ private final class PushApi(
"fullId" -> pov.fullId,
"color" -> pov.color.name,
"fen" -> Forsyth.exportBoard(pov.game.toChess.board),
"lastMove" -> pov.game.castleLastMoveTime.lastMoveString,
"lastMove" -> pov.game.lastMoveKeys,
"secondsLeft" -> pov.remainingSeconds
)

View File

@ -75,10 +75,8 @@ trait Positional { self: Config =>
state.fold(game) {
case sit @ SituationPlus(Situation(board, _), _) => game.copy(
variant = chess.variant.FromPosition,
castleLastMoveTime = game.castleLastMoveTime.copy(
lastMove = board.history.lastMove.map(_.origDest),
castles = board.history.castles
),
lastMove = board.history.lastMove,
castles = board.history.castles,
turns = sit.turns
)
}

View File

@ -68,7 +68,7 @@ final class JsonView(getLightUser: LightUser.Getter) {
"id" -> g.id,
"status" -> g.status.id,
"fen" -> (chess.format.Forsyth exportBoard g.toChess.board),
"lastMove" -> ~g.castleLastMoveTime.lastMoveString,
"lastMove" -> ~g.lastMoveKeys,
"orient" -> g.playerByUserId(hostId).map(_.color)
)

View File

@ -232,7 +232,7 @@ final class JsonView(
case chess.variant.RacingKings => chess.White
case _ => game.firstColor
}).name,
"lastMove" -> ~game.castleLastMoveTime.lastMoveString,
"lastMove" -> ~game.lastMoveKeys,
"white" -> ofPlayer(featured.white, game player chess.White),
"black" -> ofPlayer(featured.black, game player chess.Black)
)