explicit lazy loading of chess.Game and clock history
Results in better performances than pre-huffman, and is huffman-compatible. Potential nasty bugs if lazily loaded values have side effects, including reading current time.lazy-decoded
parent
6f4886c604
commit
ea8ded9349
|
@ -41,7 +41,7 @@ private[challenge] final class Joiner(onStart: String => Unit) {
|
|||
).copy(id = c.id).|> { g =>
|
||||
state.fold(g) {
|
||||
case sit @ SituationPlus(Situation(board, _), _) => g.copy(
|
||||
chess = g.chess.copy(
|
||||
loadChess = () => g.chess.copy(
|
||||
situation = g.situation.copy(
|
||||
board = g.board.copy(
|
||||
history = board.history,
|
||||
|
|
|
@ -72,19 +72,6 @@ object BSONHandlers {
|
|||
val plies = r int F.turns atMost Game.maxPlies // unlimited can cause StackOverflowError
|
||||
val turnColor = Color.fromPly(plies)
|
||||
|
||||
val decoded = r.bytesO(F.huffmanPgn).map { PgnStorage.Huffman.decode(_, plies) } | {
|
||||
val clm = r.get[CastleLastMove](F.castleLastMove)
|
||||
PgnStorage.Decoded(
|
||||
pgnMoves = PgnStorage.OldBin.decode(r bytesD F.oldPgn, plies),
|
||||
pieces = BinaryFormat.piece.read(r bytes F.binaryPieces, gameVariant),
|
||||
positionHashes = r.getO[chess.PositionHash](F.positionHashes) | Array.empty,
|
||||
unmovedRooks = r.getO[UnmovedRooks](F.unmovedRooks) | UnmovedRooks.default,
|
||||
lastMove = clm.lastMove,
|
||||
castles = clm.castles,
|
||||
format = PgnStorage.OldBin
|
||||
)
|
||||
}
|
||||
|
||||
val winC = r boolO F.winnerColor map Color.apply
|
||||
val uids = ~r.getO[List[String]](F.playerUids)
|
||||
val (whiteUid, blackUid) = (uids.headOption.filter(_.nonEmpty), uids.lift(1).filter(_.nonEmpty))
|
||||
|
@ -100,44 +87,61 @@ object BSONHandlers {
|
|||
val createdAt = r date F.createdAt
|
||||
val status = r.get[Status](F.status)
|
||||
|
||||
val chessGame = ChessGame(
|
||||
situation = chess.Situation(
|
||||
chess.Board(
|
||||
pieces = decoded.pieces,
|
||||
history = ChessHistory(
|
||||
lastMove = decoded.lastMove,
|
||||
castles = decoded.castles,
|
||||
positionHashes = decoded.positionHashes,
|
||||
unmovedRooks = decoded.unmovedRooks,
|
||||
checkCount = if (gameVariant.threeCheck) {
|
||||
val counts = r.intsD(F.checkCount)
|
||||
CheckCount(~counts.headOption, ~counts.lastOption)
|
||||
} else Game.emptyCheckCount
|
||||
val pgnFormat =
|
||||
if (r contains F.huffmanPgn) PgnStorage.Huffman else PgnStorage.OldBin
|
||||
|
||||
val loadChess: () => chess.Game = () => {
|
||||
|
||||
val decoded = r.bytesO(F.huffmanPgn).map { PgnStorage.Huffman.decode(_, plies) } | {
|
||||
val clm = r.get[CastleLastMove](F.castleLastMove)
|
||||
PgnStorage.Decoded(
|
||||
pgnMoves = PgnStorage.OldBin.decode(r bytesD F.oldPgn, plies),
|
||||
pieces = BinaryFormat.piece.read(r bytes F.binaryPieces, gameVariant),
|
||||
positionHashes = r.getO[chess.PositionHash](F.positionHashes) | Array.empty,
|
||||
unmovedRooks = r.getO[UnmovedRooks](F.unmovedRooks) | UnmovedRooks.default,
|
||||
lastMove = clm.lastMove,
|
||||
castles = clm.castles
|
||||
)
|
||||
}
|
||||
ChessGame(
|
||||
situation = chess.Situation(
|
||||
chess.Board(
|
||||
pieces = decoded.pieces,
|
||||
history = ChessHistory(
|
||||
lastMove = decoded.lastMove,
|
||||
castles = decoded.castles,
|
||||
positionHashes = decoded.positionHashes,
|
||||
unmovedRooks = decoded.unmovedRooks,
|
||||
checkCount = if (gameVariant.threeCheck) {
|
||||
val counts = r.intsD(F.checkCount)
|
||||
CheckCount(~counts.headOption, ~counts.lastOption)
|
||||
} else Game.emptyCheckCount
|
||||
),
|
||||
variant = gameVariant,
|
||||
crazyData = gameVariant.crazyhouse option r.get[Crazyhouse.Data](F.crazyData)
|
||||
),
|
||||
variant = gameVariant,
|
||||
crazyData = gameVariant.crazyhouse option r.get[Crazyhouse.Data](F.crazyData)
|
||||
color = turnColor
|
||||
),
|
||||
color = turnColor
|
||||
),
|
||||
pgnMoves = decoded.pgnMoves,
|
||||
clock = r.getO[Color => Clock](F.clock) {
|
||||
clockBSONReader(createdAt, wPlayer.berserk, bPlayer.berserk)
|
||||
} map (_(turnColor)),
|
||||
turns = plies,
|
||||
startedAtTurn = r intD F.startedAtTurn
|
||||
)
|
||||
pgnMoves = decoded.pgnMoves,
|
||||
clock = r.getO[Color => Clock](F.clock) {
|
||||
clockBSONReader(createdAt, wPlayer.berserk, bPlayer.berserk)
|
||||
} map (_(turnColor)),
|
||||
turns = plies,
|
||||
startedAtTurn = r intD F.startedAtTurn
|
||||
)
|
||||
}
|
||||
|
||||
Game(
|
||||
id = r str F.id,
|
||||
whitePlayer = wPlayer,
|
||||
blackPlayer = bPlayer,
|
||||
chess = chessGame,
|
||||
pgnStorage = decoded.format,
|
||||
loadChess = loadChess,
|
||||
pgnStorage = pgnFormat,
|
||||
status = status,
|
||||
daysPerTurn = r intO F.daysPerTurn,
|
||||
binaryMoveTimes = r bytesO F.moveTimes,
|
||||
clockHistory = for {
|
||||
clk <- chessGame.clock
|
||||
loadClockHistory = () => for {
|
||||
clk <- loadChess().clock
|
||||
bw <- r bytesO F.whiteClockHistory
|
||||
bb <- r bytesO F.blackClockHistory
|
||||
history <- BinaryFormat.clockHistory.read(clk.limit, bw, bb, (status == Status.Outoftime).option(turnColor))
|
||||
|
|
|
@ -16,12 +16,12 @@ case class Game(
|
|||
id: String,
|
||||
whitePlayer: Player,
|
||||
blackPlayer: Player,
|
||||
chess: ChessGame,
|
||||
loadChess: () => ChessGame,
|
||||
loadClockHistory: () => Option[ClockHistory] = () => Some(ClockHistory()),
|
||||
pgnStorage: PgnStorage,
|
||||
status: Status,
|
||||
daysPerTurn: Option[Int],
|
||||
binaryMoveTimes: Option[ByteArray] = None,
|
||||
clockHistory: Option[ClockHistory] = Some(ClockHistory()),
|
||||
mode: Mode = Mode.default,
|
||||
next: Option[String] = None,
|
||||
bookmarks: Int = 0,
|
||||
|
@ -30,6 +30,9 @@ case class Game(
|
|||
metadata: Metadata
|
||||
) {
|
||||
|
||||
lazy val chess = loadChess()
|
||||
lazy val clockHistory = loadClockHistory()
|
||||
|
||||
def situation = chess.situation
|
||||
def board = chess.situation.board
|
||||
def history = chess.situation.board.history
|
||||
|
@ -172,7 +175,7 @@ case class Game(
|
|||
val updated = copy(
|
||||
whitePlayer = copyPlayer(whitePlayer),
|
||||
blackPlayer = copyPlayer(blackPlayer),
|
||||
chess = game,
|
||||
loadChess = () => game,
|
||||
binaryMoveTimes = (!isPgnImport && !chess.clock.isDefined).option {
|
||||
BinaryFormat.moveTime.write {
|
||||
binaryMoveTimes.?? { t =>
|
||||
|
@ -180,7 +183,7 @@ case class Game(
|
|||
} :+ Centis(nowCentis - movedAt.getCentis).nonNeg
|
||||
}
|
||||
},
|
||||
clockHistory = for {
|
||||
loadClockHistory = () => for {
|
||||
clk <- game.clock
|
||||
ch <- clockHistory
|
||||
} yield ch.record(turnColor, clk),
|
||||
|
@ -326,8 +329,8 @@ case class Game(
|
|||
clock.ifTrue(berserkable && !player(color).berserk).map { c =>
|
||||
val newClock = c goBerserk color
|
||||
Progress(this, copy(
|
||||
chess = chess.copy(clock = Some(newClock)),
|
||||
clockHistory = clockHistory.map(history => {
|
||||
loadChess = () => chess.copy(clock = Some(newClock)),
|
||||
loadClockHistory = () => loadClockHistory().map(history => {
|
||||
if (history(color).isEmpty) history
|
||||
else history.reset(color).record(color, newClock)
|
||||
})
|
||||
|
@ -350,8 +353,8 @@ case class Game(
|
|||
status = status,
|
||||
whitePlayer = whitePlayer.finish(winner contains White),
|
||||
blackPlayer = blackPlayer.finish(winner contains Black),
|
||||
chess = chess.copy(clock = newClock),
|
||||
clockHistory = for {
|
||||
loadChess = () => chess.copy(clock = newClock),
|
||||
loadClockHistory = () => for {
|
||||
clk <- clock
|
||||
history <- clockHistory
|
||||
} yield {
|
||||
|
@ -438,7 +441,7 @@ case class Game(
|
|||
|
||||
def isClockRunning = clock ?? (_.isRunning)
|
||||
|
||||
def withClock(c: Clock) = Progress(this, copy(chess = chess.copy(clock = Some(c))))
|
||||
def withClock(c: Clock) = Progress(this, copy(loadChess = () => chess.copy(clock = Some(c))))
|
||||
|
||||
def correspondenceGiveTime = Progress(this, copy(movedAt = DateTime.now))
|
||||
|
||||
|
@ -535,7 +538,7 @@ case class Game(
|
|||
def isPgnImport = pgnImport.isDefined
|
||||
|
||||
def resetTurns = copy(
|
||||
chess = chess.copy(turns = 0, startedAtTurn = 0)
|
||||
loadChess = () => chess.copy(turns = 0, startedAtTurn = 0)
|
||||
)
|
||||
|
||||
lazy val opening: Option[FullOpening.AtPly] =
|
||||
|
@ -632,7 +635,7 @@ object Game {
|
|||
id = IdGenerator.game,
|
||||
whitePlayer = whitePlayer,
|
||||
blackPlayer = blackPlayer,
|
||||
chess = chess,
|
||||
loadChess = () => chess,
|
||||
pgnStorage = PgnStorage(chess.situation.board.variant, List(whitePlayer.userId, blackPlayer.userId).flatten),
|
||||
status = Status.Created,
|
||||
daysPerTurn = daysPerTurn,
|
||||
|
|
|
@ -48,8 +48,7 @@ private object PgnStorage {
|
|||
whiteQueenSide = unmovedRooks(Pos.A1),
|
||||
blackKingSide = unmovedRooks(Pos.H8),
|
||||
blackQueenSide = unmovedRooks(Pos.A8)
|
||||
),
|
||||
format = Huffman
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -71,8 +70,7 @@ private object PgnStorage {
|
|||
positionHashes: PositionHash, // irrelevant after game ends
|
||||
unmovedRooks: UnmovedRooks, // irrelevant after game ends
|
||||
lastMove: Option[Uci],
|
||||
castles: Castles, // irrelevant after game ends
|
||||
format: PgnStorage
|
||||
castles: Castles // irrelevant after game ends
|
||||
)
|
||||
|
||||
private val betaTesters = Set("thibault", "revoof", "isaacly")
|
||||
|
|
|
@ -32,12 +32,12 @@ object Rewind {
|
|||
val newGame = game.copy(
|
||||
whitePlayer = rewindPlayer(game.whitePlayer),
|
||||
blackPlayer = rewindPlayer(game.blackPlayer),
|
||||
chess = rewindedGame.copy(clock = newClock),
|
||||
loadChess = () => rewindedGame.copy(clock = newClock),
|
||||
binaryMoveTimes = game.binaryMoveTimes.map { binary =>
|
||||
val moveTimes = BinaryFormat.moveTime.read(binary, game.playedTurns)
|
||||
BinaryFormat.moveTime.write(moveTimes.dropRight(1))
|
||||
},
|
||||
clockHistory = game.clockHistory.map(_.update(!color, _.dropRight(1))),
|
||||
loadClockHistory = () => game.clockHistory.map(_.update(!color, _.dropRight(1))),
|
||||
movedAt = DateTime.now
|
||||
)
|
||||
Progress(game, newGame)
|
||||
|
|
|
@ -74,7 +74,7 @@ trait Positional { self: Config =>
|
|||
val game = builder(chessGame)
|
||||
state.fold(game) {
|
||||
case sit @ SituationPlus(Situation(board, _), _) => game.copy(
|
||||
chess = game.chess.copy(
|
||||
loadChess = () => game.chess.copy(
|
||||
situation = game.situation.copy(
|
||||
board = game.board.copy(
|
||||
history = board.history,
|
||||
|
|
Loading…
Reference in New Issue