replace more Game fields with Game.chess

irwin-new-api
Thibault Duplessis 2018-01-23 10:41:42 -05:00
parent e618d542f4
commit 6e3b1c4057
37 changed files with 221 additions and 249 deletions

View File

@ -56,7 +56,7 @@ object Editor extends LilaController {
OptionResult(GameRepo game id) { game =>
Redirect {
if (game.playable) routes.Round.watcher(game.id, "white")
else routes.Editor.load(get("fen") | (chess.format.Forsyth >> game.toChess))
else routes.Editor.load(get("fen") | (chess.format.Forsyth >> game.chess))
}
}
}

View File

@ -265,7 +265,7 @@ object Round extends LilaController with TheftPrevention {
OptionResult(GameRepo game id) { game =>
Redirect("%s?fen=%s#%s".format(
routes.Lobby.home(),
get("fen") | (chess.format.Forsyth >> game.toChess),
get("fen") | (chess.format.Forsyth >> game.chess),
mode
))
}

View File

@ -49,14 +49,13 @@ object UserAnalysis extends LilaController with TheftPrevention {
private[controllers] def makePov(from: SituationPlus): Pov = Pov(
lila.game.Game.make(
game = chess.Game(
chess = chess.Game(
situation = from.situation,
turns = from.turns
),
whitePlayer = lila.game.Player.white,
blackPlayer = lila.game.Player.black,
mode = chess.Mode.Casual,
variant = from.situation.board.variant,
source = lila.game.Source.Api,
pgnImport = None
).copy(id = "synthetic"),

View File

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

View File

@ -61,7 +61,7 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
}
case _ => "Game is still being played"
}
val moves = s"${game.toChess.fullMoveNumber} moves"
val moves = s"${game.chess.fullMoveNumber} moves"
s"$p1 plays $p2 in a $mode $speedAndClock game of $variant. $result after $moves. Click to replay, analyse, and discuss the game!"
}
@ -218,7 +218,7 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
val title = withTitle ?? s"""title="${gameTitle(game, pov.color)}""""
val cssClass = isLive ?? ("live live_" + game.id)
val live = isLive ?? game.id
val fen = Forsyth exportBoard game.toChess.board
val fen = Forsyth exportBoard game.board
val lastMove = ~game.lastMoveKeys
val variant = game.variant.key
val tag = if (withLink) "a" else "span"
@ -235,7 +235,7 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
isLive ?? ("live live_" + pov.game.id),
isLive ?? pov.game.id,
pov.color.name,
Forsyth exportBoard pov.game.toChess.board,
Forsyth exportBoard pov.game.board,
~pov.game.lastMoveKeys,
blank ?? """ target="_blank""""
)

View File

@ -7,7 +7,7 @@
<dt>PGN</dt>
<dd>@Html(pov.game.pgnMoves.mkString("<br />"))</dd>
<dt>FEN</dt>
<dd>@{chess.format.Forsyth.>>(pov.game.toChess)}</dd>
<dd>@{chess.format.Forsyth.>>(pov.game.chess)}</dd>
@if(playing) {
<dt>Your color</dt>
<dd>@pov.color.name</dd>

View File

@ -23,7 +23,7 @@ object Accuracy {
implicit def povToPovLike(pov: Pov): PovLike = PovLike(
color = pov.color,
startColor = pov.game.startColor,
startedAtTurn = pov.game.startedAtTurn
startedAtTurn = pov.game.chess.startedAtTurn
)
def diffsList(pov: PovLike, analysis: Analysis): List[Int] =

View File

@ -38,7 +38,7 @@ final class LobbyApi(
def nowPlaying(pov: Pov) = Json.obj(
"fullId" -> pov.fullId,
"gameId" -> pov.gameId,
"fen" -> (chess.format.Forsyth exportBoard pov.game.toChess.board),
"fen" -> (chess.format.Forsyth exportBoard pov.game.board),
"color" -> pov.color.name,
"lastMove" -> ~pov.game.lastMoveKeys,
"variant" -> Json.obj(

View File

@ -44,7 +44,7 @@ final class UserGameApi(
.add("rating" -> p.rating)
.add("ratingDiff" -> p.ratingDiff)
}),
"fen" -> Forsyth.exportBoard(g.toChess.board),
"fen" -> Forsyth.exportBoard(g.board),
"winner" -> g.winnerColor.map(_.name),
"bookmarks" -> g.bookmarks
).add("bookmarked" -> bookmarked)

View File

@ -36,20 +36,25 @@ private[challenge] final class Joiner(onStart: String => Unit) {
}
}
val game = Game.make(
game = chessGame,
chess = chessGame,
whitePlayer = makePlayer(chess.White, c.finalColor.fold(challengerUser, destUser)),
blackPlayer = makePlayer(chess.Black, c.finalColor.fold(destUser, challengerUser)),
mode = (realVariant == chess.variant.FromPosition).fold(Mode.Casual, c.mode),
variant = realVariant,
source = (realVariant == chess.variant.FromPosition).fold(Source.Position, Source.Friend),
mode = realVariant.fromPosition.fold(Mode.Casual, c.mode),
source = realVariant.fromPosition.fold(Source.Position, Source.Friend),
daysPerTurn = c.daysPerTurn,
pgnImport = None
).copy(id = c.id).|> { g =>
state.fold(g) {
case sit @ SituationPlus(Situation(board, _), _) => g.copy(
variant = chess.variant.FromPosition,
history = board.history,
turns = sit.turns
chess = g.chess.copy(
situation = g.situation.copy(
board = g.board.copy(
history = board.history,
variant = chess.variant.FromPosition
)
),
turns = sit.turns
)
)
}
}.start

View File

@ -97,7 +97,7 @@ final class Analyser(
variant = game.variant,
moves = moves take maxPlies mkString " "
),
startPly = game.startedAtTurn,
startPly = game.chess.startedAtTurn,
sender = sender
)
}

View File

@ -20,7 +20,7 @@ final class Player(
}
private def makeWork(game: Game, level: Int): Fu[Work.Move] =
if (game.toChess.situation playable true)
if (game.situation playable true)
if (game.turns <= maxPlies) GameRepo.initialFen(game) zip uciMemo.get(game) map {
case (initialFen, moves) => Work.Move(
_id = Work.makeId,
@ -31,7 +31,7 @@ final class Player(
variant = game.variant,
moves = moves mkString " "
),
currentFen = FEN(Forsyth >> game.toChess),
currentFen = FEN(Forsyth >> game.chess),
level = level,
clock = game.clock.map { clk =>
Work.Clock(

View File

@ -4,8 +4,8 @@ import org.joda.time.DateTime
import reactivemongo.bson._
import scala.collection.breakOut
import chess.variant.{ Variant, Crazyhouse, ThreeCheck }
import chess.{ CheckCount, Color, Clock, White, Black, Status, Mode, UnmovedRooks, History => ChessHistory }
import chess.variant.{ Variant, Crazyhouse }
import chess.{ CheckCount, Color, Clock, White, Black, Status, Mode, UnmovedRooks, History => ChessHistory, Game => ChessGame }
import lila.db.BSON
import lila.db.dsl._
@ -60,132 +60,146 @@ object BSONHandlers {
implicit val gameBSONHandler: BSON[Game] = new BSON[Game] {
import Game.BSONFields._
import Game.{ BSONFields => F }
import PgnImport.pgnImportBSONHandler
import Player.playerBSONHandler
private val emptyPlayerBuilder = playerBSONHandler.read(BSONDocument())
def reads(r: BSON.Reader): Game = {
val winC = r boolO winnerColor map Color.apply
val (whiteId, blackId) = r str playerIds splitAt 4
val uids = ~r.getO[List[String]](playerUids)
val (whiteUid, blackUid) = (uids.headOption.filter(_.nonEmpty), uids.lift(1).filter(_.nonEmpty))
def player(field: String, color: Color, id: Player.Id, uid: Player.UserId): Player = {
val builder = r.getO[Player.Builder](field)(playerBSONHandler) | emptyPlayerBuilder
val win = winC map (_ == color)
builder(color)(id)(uid)(win)
}
val gameVariant = Variant(r intD variant) | chess.variant.Standard
val plies = r int turns
val decoded = r.bytesO(huffmanPgn).map { PgnStorage.Huffman.decode(_, plies) } | {
val clm = r.get[CastleLastMove](castleLastMove)
val gameVariant = Variant(r intD F.variant) | chess.variant.Standard
val plies = r int F.turns
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 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,
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 g = Game(
id = r str id,
whitePlayer = player(whitePlayer, White, whiteId, whiteUid),
blackPlayer = player(blackPlayer, Black, blackId, blackUid),
pgnMoves = decoded.pgnMoves,
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(checkCount)
CheckCount(~counts.headOption, ~counts.lastOption)
} else Game.emptyCheckCount
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))
def makePlayer(field: String, color: Color, id: Player.Id, uid: Player.UserId): Player = {
val builder = r.getO[Player.Builder](field)(playerBSONHandler) | emptyPlayerBuilder
val win = winC map (_ == color)
builder(color)(id)(uid)(win)
}
val (whiteId, blackId) = r str F.playerIds splitAt 4
val wPlayer = makePlayer(F.whitePlayer, White, whiteId, whiteUid)
val bPlayer = makePlayer(F.blackPlayer, Black, blackId, blackUid)
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
),
variant = gameVariant,
crazyData = gameVariant.crazyhouse option r.get[Crazyhouse.Data](F.crazyData)
),
color = turnColor
),
pgnStorage = decoded.format,
status = r.get[Status](status),
pgnMoves = decoded.pgnMoves,
clock = r.getO[Color => Clock](F.clock) {
clockBSONReader(createdAt, wPlayer.berserk, bPlayer.berserk)
} map (_(turnColor)),
turns = plies,
startedAtTurn = r intD startedAtTurn,
daysPerTurn = r intO daysPerTurn,
binaryMoveTimes = r bytesO moveTimes,
mode = Mode(r boolD rated),
variant = gameVariant,
next = r strO next,
bookmarks = r intD bookmarks,
createdAt = r date createdAt,
movedAt = r.dateD(movedAt, r date createdAt),
metadata = Metadata(
source = r intO source flatMap Source.apply,
pgnImport = r.getO[PgnImport](pgnImport)(PgnImport.pgnImportBSONHandler),
tournamentId = r strO tournamentId,
simulId = r strO simulId,
tvAt = r dateO tvAt,
analysed = r boolD analysed
)
startedAtTurn = r intD F.startedAtTurn
)
val gameClock = r.getO[Color => Clock](clock)(clockBSONReader(g.createdAt, g.whitePlayer.berserk, g.blackPlayer.berserk)) map (_(g.turnColor))
g.copy(
clock = gameClock,
crazyData = (g.variant == Crazyhouse) option r.get[Crazyhouse.Data](crazyData),
Game(
id = r str F.id,
whitePlayer = wPlayer,
blackPlayer = bPlayer,
chess = chessGame,
pgnStorage = decoded.format,
status = status,
daysPerTurn = r intO F.daysPerTurn,
binaryMoveTimes = r bytesO F.moveTimes,
clockHistory = for {
clk <- gameClock
bw <- r bytesO whiteClockHistory
bb <- r bytesO blackClockHistory
history <- BinaryFormat.clockHistory.read(clk.limit, bw, bb, g.flagged, g.id)
} yield history
clk <- chessGame.clock
bw <- r bytesO F.whiteClockHistory
bb <- r bytesO F.blackClockHistory
history <- BinaryFormat.clockHistory.read(clk.limit, bw, bb, (status == Status.Outoftime).option(turnColor))
} yield history,
mode = Mode(r boolD F.rated),
next = r strO F.next,
bookmarks = r intD F.bookmarks,
createdAt = createdAt,
movedAt = r.dateD(F.movedAt, createdAt),
metadata = Metadata(
source = r intO F.source flatMap Source.apply,
pgnImport = r.getO[PgnImport](F.pgnImport)(PgnImport.pgnImportBSONHandler),
tournamentId = r strO F.tournamentId,
simulId = r strO F.simulId,
tvAt = r dateO F.tvAt,
analysed = r boolD F.analysed
)
)
}
def writes(w: BSON.Writer, o: Game) = BSONDocument(
id -> o.id,
playerIds -> (o.whitePlayer.id + o.blackPlayer.id),
playerUids -> w.strListO(List(~o.whitePlayer.userId, ~o.blackPlayer.userId)),
whitePlayer -> w.docO(playerBSONHandler write ((_: Color) => (_: Player.Id) => (_: Player.UserId) => (_: Player.Win) => o.whitePlayer)),
blackPlayer -> w.docO(playerBSONHandler write ((_: Color) => (_: Player.Id) => (_: Player.UserId) => (_: Player.Win) => o.blackPlayer)),
status -> o.status,
turns -> o.turns,
startedAtTurn -> w.intO(o.startedAtTurn),
clock -> (o.clock map { c => clockBSONWrite(o.createdAt, c) }),
checkCount -> o.history.checkCount.nonEmpty.option(o.history.checkCount),
daysPerTurn -> o.daysPerTurn,
moveTimes -> o.binaryMoveTimes,
whiteClockHistory -> clockHistory(White, o.clockHistory, o.clock, o.flagged),
blackClockHistory -> clockHistory(Black, o.clockHistory, o.clock, o.flagged),
rated -> w.boolO(o.mode.rated),
variant -> o.variant.exotic.option(o.variant.id).map(w.int),
crazyData -> o.crazyData,
next -> o.next,
bookmarks -> w.intO(o.bookmarks),
createdAt -> w.date(o.createdAt),
movedAt -> w.date(o.movedAt),
source -> o.metadata.source.map(_.id),
pgnImport -> o.metadata.pgnImport,
tournamentId -> o.metadata.tournamentId,
simulId -> o.metadata.simulId,
tvAt -> o.metadata.tvAt.map(w.date),
analysed -> w.boolO(o.metadata.analysed)
F.id -> o.id,
F.playerIds -> (o.whitePlayer.id + o.blackPlayer.id),
F.playerUids -> w.strListO(List(~o.whitePlayer.userId, ~o.blackPlayer.userId)),
F.whitePlayer -> w.docO(playerBSONHandler write ((_: Color) => (_: Player.Id) => (_: Player.UserId) => (_: Player.Win) => o.whitePlayer)),
F.blackPlayer -> w.docO(playerBSONHandler write ((_: Color) => (_: Player.Id) => (_: Player.UserId) => (_: Player.Win) => o.blackPlayer)),
F.status -> o.status,
F.turns -> o.chess.turns,
F.startedAtTurn -> w.intO(o.chess.startedAtTurn),
F.clock -> (o.chess.clock map { c => clockBSONWrite(o.createdAt, c) }),
F.checkCount -> o.history.checkCount.nonEmpty.option(o.history.checkCount),
F.daysPerTurn -> o.daysPerTurn,
F.moveTimes -> o.binaryMoveTimes,
F.whiteClockHistory -> clockHistory(White, o.clockHistory, o.chess.clock, o.flagged),
F.blackClockHistory -> clockHistory(Black, o.clockHistory, o.chess.clock, o.flagged),
F.rated -> w.boolO(o.mode.rated),
F.variant -> o.board.variant.exotic.option(w int o.board.variant.id),
F.crazyData -> o.board.crazyData,
F.next -> o.next,
F.bookmarks -> w.intO(o.bookmarks),
F.createdAt -> w.date(o.createdAt),
F.movedAt -> w.date(o.movedAt),
F.source -> o.metadata.source.map(_.id),
F.pgnImport -> o.metadata.pgnImport,
F.tournamentId -> o.metadata.tournamentId,
F.simulId -> o.metadata.simulId,
F.tvAt -> o.metadata.tvAt.map(w.date),
F.analysed -> w.boolO(o.metadata.analysed)
) ++ {
o.pgnStorage match {
case f @ PgnStorage.OldBin => $doc(
oldPgn -> f.encode(o.pgnMoves),
binaryPieces -> BinaryFormat.piece.write(o.pieces),
positionHashes -> o.history.positionHashes,
unmovedRooks -> o.history.unmovedRooks,
castleLastMove -> CastleLastMove.castleLastMoveBSONHandler.write(CastleLastMove(
F.oldPgn -> f.encode(o.pgnMoves),
F.binaryPieces -> BinaryFormat.piece.write(o.board.pieces),
F.positionHashes -> o.history.positionHashes,
F.unmovedRooks -> o.history.unmovedRooks,
F.castleLastMove -> CastleLastMove.castleLastMoveBSONHandler.write(CastleLastMove(
castles = o.history.castles,
lastMove = o.history.lastMove
))
)
case f @ PgnStorage.Huffman => $doc(
huffmanPgn -> f.encode(o.pgnMoves)
F.huffmanPgn -> f.encode(o.pgnMoves)
)
}
}

View File

@ -42,13 +42,13 @@ object BinaryFormat {
if (flagged) decoded :+ Centis(0) else decoded
}
def read(start: Centis, bw: ByteArray, bb: ByteArray, flagged: Option[Color], gameId: String) = Try {
def read(start: Centis, bw: ByteArray, bb: ByteArray, flagged: Option[Color]) = Try {
ClockHistory(
readSide(start, bw, flagged has White),
readSide(start, bb, flagged has Black)
)
}.fold(
e => { logger.warn(s"Exception decoding history on game $gameId", e); none },
e => { logger.warn(s"Exception decoding history", e); none },
some
)
}

View File

@ -3,8 +3,8 @@ package lila.game
import chess.Color.{ White, Black }
import chess.format.{ Uci, FEN }
import chess.opening.{ FullOpening, FullOpeningDB }
import chess.variant.{ Variant, Crazyhouse }
import chess.{ PieceMap, MoveMetrics, History => ChessHistory, CheckCount, Castles, Board, MoveOrDrop, Pos, Game => ChessGame, Clock, Status, Color, Mode, PositionHash, UnmovedRooks, Centis, Situation }
import chess.variant.{ Variant, Standard }
import chess.{ Speed, PieceMap, MoveMetrics, History => ChessHistory, CheckCount, Castles, Board, MoveOrDrop, Pos, Game => ChessGame, Clock, Status, Color, Mode, PositionHash, UnmovedRooks, Centis, Situation }
import org.joda.time.DateTime
import lila.common.Sequence
@ -16,20 +16,13 @@ case class Game(
id: String,
whitePlayer: Player,
blackPlayer: Player,
pgnMoves: PgnMoves,
pieces: PieceMap,
history: ChessHistory,
chess: ChessGame,
pgnStorage: PgnStorage,
status: Status,
turns: Int, // = ply
startedAtTurn: Int,
clock: Option[Clock] = None,
daysPerTurn: Option[Int],
binaryMoveTimes: Option[ByteArray] = None,
clockHistory: Option[ClockHistory] = Option(ClockHistory()),
mode: Mode = Mode.default,
variant: Variant = Variant.default,
crazyData: Option[Crazyhouse.Data] = None,
next: Option[String] = None,
bookmarks: Int = 0,
createdAt: DateTime = DateTime.now,
@ -37,6 +30,14 @@ case class Game(
metadata: Metadata
) {
def situation = chess.situation
def board = chess.situation.board
def history = chess.situation.board.history
def variant = chess.situation.board.variant
def turns = chess.turns
def clock = chess.clock
def pgnMoves = chess.pgnMoves
val players = List(whitePlayer, blackPlayer)
def player(color: Color): Player = color match {
@ -74,7 +75,7 @@ case class Game(
def turnOf(c: Color): Boolean = c == turnColor
def turnOf(u: User): Boolean = player(u) ?? turnOf
def playedTurns = turns - startedAtTurn
def playedTurns = turns - chess.startedAtTurn
def flagged = (status == Status.Outoftime).option(turnColor)
@ -154,17 +155,6 @@ case class Game(
}
}
lazy val toChess: ChessGame = ChessGame(
situation = Situation(
Board(pieces, history, variant, crazyData),
Color.fromPly(turns)
),
pgnMoves = pgnMoves,
clock = clock,
turns = turns,
startedAtTurn = startedAtTurn
)
def update(
game: ChessGame,
moveOrDrop: MoveOrDrop,
@ -182,12 +172,8 @@ case class Game(
val updated = copy(
whitePlayer = copyPlayer(whitePlayer),
blackPlayer = copyPlayer(blackPlayer),
pgnMoves = game.pgnMoves,
pieces = game.board.pieces,
history = game.board.history,
turns = game.turns,
crazyData = game.situation.board.crazyData,
binaryMoveTimes = (!isPgnImport && !clock.isDefined).option {
chess = game,
binaryMoveTimes = (!isPgnImport && !chess.clock.isDefined).option {
BinaryFormat.moveTime.write {
binaryMoveTimes.?? { t =>
BinaryFormat.moveTime.read(t, playedTurns)
@ -199,7 +185,6 @@ case class Game(
ch <- clockHistory
} yield ch.record(turnColor, clk),
status = game.situation.status | status,
clock = game.clock,
movedAt = DateTime.now
)
@ -212,17 +197,17 @@ case class Game(
blackOffersDraw = blackPlayer.isOfferingDraw
)
val clockEvent = updated.clock map Event.Clock.apply orElse {
val clockEvent = updated.chess.clock map Event.Clock.apply orElse {
updated.playableCorrespondenceClock map Event.CorrespondenceClock.apply
}
val events = moveOrDrop.fold(
Event.Move(_, game.situation, state, clockEvent, updated.crazyData),
Event.Drop(_, game.situation, state, clockEvent, updated.crazyData)
Event.Move(_, game.situation, state, clockEvent, updated.board.crazyData),
Event.Drop(_, game.situation, state, clockEvent, updated.board.crazyData)
) ::
{
// abstraction leak, I know.
(updated.variant.threeCheck && game.situation.check) ?? List(Event.CheckCount(
(updated.board.variant.threeCheck && game.situation.check) ?? List(Event.CheckCount(
white = updated.history.checkCount.white,
black = updated.history.checkCount.black
))
@ -231,8 +216,6 @@ case class Game(
Progress(this, updated, events)
}
def check = toChess.situation.checkSquare
def lastMoveKeys: Option[String] = history.lastMove map {
case Uci.Drop(target, _) => s"$target$target"
case m: Uci.Move => m.keys
@ -266,7 +249,7 @@ case class Game(
def playableCorrespondenceClock: Option[CorrespondenceClock] =
playable ?? correspondenceClock
def speed = chess.Speed(clock.map(_.config))
def speed = Speed(chess.clock.map(_.config))
def perfKey = PerfPicker.key(this)
def perfType = PerfType(perfKey)
@ -343,7 +326,7 @@ case class Game(
clock.ifTrue(berserkable && !player(color).berserk).map { c =>
val newClock = c goBerserk color
Progress(this, copy(
clock = Some(newClock),
chess = chess.copy(clock = Some(newClock)),
clockHistory = clockHistory.map(history => {
if (history(color).isEmpty) history
else history.reset(color).record(color, newClock)
@ -367,7 +350,7 @@ case class Game(
status = status,
whitePlayer = whitePlayer.finish(winner contains White),
blackPlayer = blackPlayer.finish(winner contains Black),
clock = newClock,
chess = chess.copy(clock = newClock),
clockHistory = for {
clk <- clock
history <- clockHistory
@ -404,10 +387,10 @@ case class Game(
!Game.isOldHorde(this)
def ratingVariant =
if (isTournament && variant == chess.variant.FromPosition) chess.variant.Standard
if (isTournament && variant.fromPosition) Standard
else variant
def fromPosition = variant == chess.variant.FromPosition || source.??(Source.Position==)
def fromPosition = variant.fromPosition || source.??(Source.Position==)
def imported = source contains Source.Import
@ -443,7 +426,7 @@ case class Game(
private def outoftimeCorrespondence: Boolean =
playableCorrespondenceClock ?? { _ outoftime turnColor }
def isCorrespondence = speed == chess.Speed.Correspondence
def isCorrespondence = speed == Speed.Correspondence
def isSwitchable = nonAi && (isCorrespondence || isSimul)
@ -455,7 +438,7 @@ case class Game(
def isClockRunning = clock ?? (_.isRunning)
def withClock(c: Clock) = Progress(this, copy(clock = Some(c)))
def withClock(c: Clock) = Progress(this, copy(chess = chess.copy(clock = Some(c))))
def correspondenceGiveTime = Progress(this, copy(movedAt = DateTime.now))
@ -465,7 +448,7 @@ case class Game(
correspondenceClock.map(_.estimateTotalTime) getOrElse 1200
def timeForFirstMove: Centis = Centis ofSeconds {
import chess.Speed._
import Speed._
val base = if (isTournament) speed match {
case UltraBullet => 11
case Bullet => 16
@ -480,7 +463,7 @@ case class Game(
case Rapid => 30
case _ => 35
}
if (variant == chess.variant.Chess960) (base * 2) atMost 90
if (variant.chess960) (base * 2) atMost 90
else base
}
@ -500,7 +483,7 @@ case class Game(
def onePlayerHasMoved = playedTurns > 0
def bothPlayersHaveMoved = playedTurns > 1
def startColor = Color(startedAtTurn % 2 == 0)
def startColor = Color(chess.startedAtTurn % 2 == 0)
def playerMoves(color: Color): Int =
if (color == startColor) (playedTurns + 1) / 2
@ -551,7 +534,9 @@ case class Game(
def pgnImport = metadata.pgnImport
def isPgnImport = pgnImport.isDefined
def resetTurns = copy(turns = 0, startedAtTurn = 0)
def resetTurns = copy(
chess = chess.copy(turns = 0, startedAtTurn = 0)
)
lazy val opening: Option[FullOpening.AtPly] =
if (fromPosition || !Variant.openingSensibleVariants(variant)) none
@ -632,11 +617,10 @@ object Game {
private[game] val emptyCheckCount = CheckCount(0, 0)
def make(
game: ChessGame,
chess: ChessGame,
whitePlayer: Player,
blackPlayer: Player,
mode: Mode,
variant: Variant,
source: Source,
pgnImport: Option[PgnImport],
daysPerTurn: Option[Int] = None
@ -646,24 +630,11 @@ object Game {
id = IdGenerator.game,
whitePlayer = whitePlayer,
blackPlayer = blackPlayer,
pgnMoves = Vector.empty,
pieces = game.board.pieces,
history = ChessHistory(
lastMove = none,
castles = game.board.history.castles,
positionHashes = game.board.history.positionHashes,
checkCount = emptyCheckCount,
unmovedRooks = game.board.unmovedRooks
),
pgnStorage = PgnStorage(variant, List(whitePlayer.userId, blackPlayer.userId).flatten),
chess = chess,
pgnStorage = PgnStorage(chess.situation.board.variant, List(whitePlayer.userId, blackPlayer.userId).flatten),
status = Status.Created,
turns = game.turns,
startedAtTurn = game.startedAtTurn,
clock = game.clock,
daysPerTurn = daysPerTurn,
mode = mode,
variant = variant,
crazyData = game.board.crazyData,
metadata = Metadata(
source = source.some,
pgnImport = pgnImport,

View File

@ -1,6 +1,5 @@
package lila.game
import chess.variant.{ Crazyhouse, ThreeCheck }
import chess.{ Color, White, Black, Clock, CheckCount, UnmovedRooks }
import Game.BSONFields._
import reactivemongo.bson._
@ -59,15 +58,15 @@ private[game] object GameDiff {
a.pgnStorage match {
case f @ PgnStorage.OldBin =>
d(oldPgn, _.pgnMoves, writeBytes compose f.encode)
d(binaryPieces, _.pieces, writeBytes compose BinaryFormat.piece.write)
d(binaryPieces, _.board.pieces, writeBytes compose BinaryFormat.piece.write)
d(positionHashes, _.history.positionHashes, w.bytes)
d(unmovedRooks, _.history.unmovedRooks, writeBytes compose BinaryFormat.unmovedRooks.write)
d(castleLastMove, makeCastleLastMove, CastleLastMove.castleLastMoveBSONHandler.write)
// since variants are always OldBin
if (a.variant == ThreeCheck)
if (a.variant.threeCheck)
dOpt(checkCount, _.history.checkCount, (o: CheckCount) => o.nonEmpty option { BSONHandlers.checkCountWriter write o })
if (a.variant == Crazyhouse)
dOpt(crazyData, _.crazyData, (o: Option[Crazyhouse.Data]) => o map BSONHandlers.crazyhouseDataBSONHandler.write)
if (a.variant.crazyhouse)
dOpt(crazyData, _.board.crazyData, (o: Option[chess.variant.Crazyhouse.Data]) => o map BSONHandlers.crazyhouseDataBSONHandler.write)
case f @ PgnStorage.Huffman =>
d(huffmanPgn, _.pgnMoves, writeBytes compose f.encode)
}

View File

@ -274,7 +274,7 @@ object GameRepo {
val userIds = g2.userIds.distinct
val fen = initialFen.map(_.value) orElse {
(!g2.variant.standardInitialPosition)
.option(Forsyth >> g2.toChess)
.option(Forsyth >> g2.chess)
.filter(Forsyth.initial !=)
}
val checkInHours =

View File

@ -15,10 +15,10 @@ object JsonView {
"perf" -> PerfPicker.key(game),
"rated" -> game.rated,
"initialFen" -> initialFen.fold(chess.format.Forsyth.initial)(_.value),
"fen" -> (Forsyth >> game.toChess),
"fen" -> (Forsyth >> game.chess),
"player" -> game.turnColor,
"turns" -> game.turns,
"startedAtTurn" -> game.startedAtTurn,
"startedAtTurn" -> game.chess.startedAtTurn,
"source" -> game.source,
"status" -> game.status,
"createdAt" -> game.createdAt
@ -27,7 +27,7 @@ object JsonView {
.add("tournamentId" -> game.tournamentId)
.add("winner" -> game.winnerColor)
.add("lastMove" -> game.lastMoveKeys)
.add("check" -> game.check.map(_.key))
.add("check" -> game.situation.checkSquare.map(_.key))
.add("rematch" -> game.next)
implicit val statusWrites: OWrites[chess.Status] = OWrites { s =>

View File

@ -9,9 +9,9 @@ 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),
fen = FEN(Forsyth >> game.chess),
lastMove = game.lastMoveKeys,
check = game.toChess.situation.checkSquare,
check = game.situation.checkSquare,
orientation = game.firstColor.some,
logHint = s"game ${game.id}"
)

View File

@ -32,18 +32,12 @@ object Rewind {
val newGame = game.copy(
whitePlayer = rewindPlayer(game.whitePlayer),
blackPlayer = rewindPlayer(game.blackPlayer),
pgnMoves = rewindedGame.pgnMoves,
pieces = rewindedGame.board.pieces,
history = rewindedHistory,
turns = rewindedGame.turns,
chess = 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))),
crazyData = rewindedSituation.board.crazyData,
status = game.status,
clock = newClock,
movedAt = DateTime.now
)
Progress(game, newGame)

View File

@ -87,18 +87,13 @@ case class ImportData(pgn: String, analyse: Option[String]) {
}
val dbGame = Game.make(
game = replay.state,
chess = replay.state,
whitePlayer = Player.white withName name(_.White, _.WhiteElo),
blackPlayer = Player.black withName name(_.Black, _.BlackElo),
mode = Mode.Casual,
variant = variant,
source = Source.Import,
pgnImport = PgnImport.make(user = user, date = date, pgn = pgn).some
) |> { g =>
g.copy(
pgnMoves = replay.state.pgnMoves
).start
}
).start
Preprocessed(dbGame, replay, result, initialFen, parsed)
}

View File

@ -19,7 +19,7 @@ class DataFormTest extends Specification with ValidationMatchers {
1. Bd7 b4 2. Kf7 b3 3. Ke8 b2 4. Kd8 g6 5. Bxc7# { Black is checkmated } 1-0"""
ImportData(pgn, None).preprocess(None) must beSuccess.like {
case s => s.game.toChess.board.history.castles must_== Castles.none
case s => s.game.history.castles must_== Castles.none
}
}
}

View File

@ -49,7 +49,7 @@ object Chart {
def games = povs.map { pov =>
Json.obj(
"id" -> pov.game.id,
"fen" -> (chess.format.Forsyth exportBoard pov.game.toChess.board),
"fen" -> (chess.format.Forsyth exportBoard pov.game.board),
"color" -> pov.player.color.name,
"lastMove" -> ~pov.game.lastMoveKeys,
"user1" -> gameUserJson(pov.player),

View File

@ -57,27 +57,25 @@ private[lobby] object Biter {
}
private def makeGame(hook: Hook) = Game.make(
game = ChessGame(
chess = ChessGame(
situation = Situation(hook.realVariant),
clock = hook.clock.toClock.some
),
whitePlayer = Player.white,
blackPlayer = Player.black,
mode = hook.realMode,
variant = hook.realVariant,
source = lila.game.Source.Lobby,
pgnImport = None
)
private def makeGame(seek: Seek) = Game.make(
game = ChessGame(
chess = ChessGame(
situation = Situation(seek.realVariant),
clock = none
),
whitePlayer = Player.white,
blackPlayer = Player.black,
mode = seek.realMode,
variant = seek.realVariant,
source = lila.game.Source.Lobby,
daysPerTurn = seek.daysPerTurn,
pgnImport = None

View File

@ -59,14 +59,13 @@ private final class GameStarter(
whiteUser: (User.ID, Perf),
blackUser: (User.ID, Perf)
) = Game.make(
game = chess.Game(
chess = chess.Game(
situation = chess.Situation(chess.variant.Standard),
clock = pool.clock.toClock.some
),
whitePlayer = Player.white.withUser(whiteUser._1, whiteUser._2),
blackPlayer = Player.black.withUser(blackUser._1, blackUser._2),
mode = chess.Mode.Rated,
variant = chess.variant.Standard,
source = lila.game.Source.Pool,
pgnImport = None
)

View File

@ -40,7 +40,7 @@ private final class PushApi(
"gameId" -> game.id,
"fullId" -> pov.fullId,
"color" -> pov.color.name,
"fen" -> Forsyth.exportBoard(game.toChess.board),
"fen" -> Forsyth.exportBoard(game.board),
"lastMove" -> game.lastMoveKeys,
"win" -> pov.win
)
@ -139,7 +139,7 @@ private final class PushApi(
"gameId" -> pov.game.id,
"fullId" -> pov.fullId,
"color" -> pov.color.name,
"fen" -> Forsyth.exportBoard(pov.game.toChess.board),
"fen" -> Forsyth.exportBoard(pov.game.board),
"lastMove" -> pov.game.lastMoveKeys,
"secondsLeft" -> pov.remainingSeconds
)

View File

@ -36,7 +36,7 @@ private[round] final class Finisher(
other(game, _.Aborted, none)
} else {
val winner = Some(!game.player.color) filterNot { color =>
game.toChess.board.variant.insufficientWinningMaterial(game.toChess.situation.board, color)
game.variant.insufficientWinningMaterial(game.board, color)
}
apply(game, _.Outoftime, winner) >>-
winner.?? { w => playban.flag(game, !w) }

View File

@ -110,7 +110,7 @@ final class JsonView(
).add("clock" -> game.clock.map(clockJson))
.add("correspondence" -> game.correspondenceClock)
.add("takebackable" -> takebackable)
.add("crazyhouse" -> pov.game.crazyData)
.add("crazyhouse" -> pov.game.board.crazyData)
.add("possibleMoves" -> possibleMoves(pov))
.add("possibleDrops" -> possibleDrops(pov))
.add("expiration" -> game.expirable.option {
@ -204,7 +204,7 @@ final class JsonView(
division: Option[chess.Division] = none
) = {
import pov._
val fen = Forsyth >> game.toChess
val fen = Forsyth >> game.chess
Json.obj(
"game" -> Json.obj(
"id" -> gameId,
@ -256,13 +256,13 @@ final class JsonView(
private def possibleMoves(pov: Pov): Option[Map[String, String]] =
(pov.game playableBy pov.player) option {
pov.game.toChess.situation.destinations map {
pov.game.situation.destinations map {
case (from, dests) => from.key -> dests.mkString
}
}
private def possibleDrops(pov: Pov): Option[JsValue] = (pov.game playableBy pov.player) ?? {
pov.game.toChess.situation.drops map { drops =>
pov.game.situation.drops map { drops =>
JsString(drops.map(_.key).mkString)
}
}

View File

@ -51,7 +51,7 @@ private[round] final class Player(
def fishnet(game: Game, uci: Uci, currentFen: FEN, round: ActorRef)(implicit proxy: GameProxy): Fu[Events] =
if (game.playable && game.player.isAi) {
if (currentFen == FEN(Forsyth >> game.toChess))
if (currentFen == FEN(Forsyth >> game.chess))
applyUci(game, uci, blur = false, metrics = fishnetLag)
.fold(errs => fufail(ClientError(errs.shows)), fuccess).flatMap {
case Flagged => finisher.outOfTime(game)
@ -76,10 +76,10 @@ private[round] final class Player(
private def applyUci(game: Game, uci: Uci, blur: Boolean, metrics: MoveMetrics): Valid[MoveResult] =
(uci match {
case Uci.Move(orig, dest, prom) => game.toChess.apply(orig, dest, prom, metrics) map {
case Uci.Move(orig, dest, prom) => game.chess(orig, dest, prom, metrics) map {
case (ncg, move) => ncg -> (Left(move): MoveOrDrop)
}
case Uci.Drop(role, pos) => game.toChess.drop(role, pos, metrics) map {
case Uci.Drop(role, pos) => game.chess.drop(role, pos, metrics) map {
case (ncg, drop) => ncg -> (Right(drop): MoveOrDrop)
}
}).map {
@ -95,7 +95,7 @@ private[round] final class Player(
val color = moveOrDrop.fold(_.color, _.color)
val moveEvent = MoveEvent(
gameId = game.id,
fen = Forsyth exportBoard game.toChess.board,
fen = Forsyth exportBoard game.board,
move = moveOrDrop.fold(_.toUci.keys, _.toUci.uci)
)
// publish all moves
@ -124,8 +124,8 @@ private[round] final class Player(
}
private def moveFinish(game: Game, color: Color)(implicit proxy: GameProxy): Fu[Events] = game.status match {
case Status.Mate => finisher.other(game, _.Mate, game.toChess.situation.winner)
case Status.VariantEnd => finisher.other(game, _.VariantEnd, game.toChess.situation.winner)
case Status.Mate => finisher.other(game, _.Mate, game.situation.winner)
case Status.VariantEnd => finisher.other(game, _.VariantEnd, game.situation.winner)
case status @ (Status.Stalemate | Status.Draw) => finisher.other(game, _ => status, None)
case _ => fuccess(Nil)
}

View File

@ -81,7 +81,7 @@ private[round] final class Rematcher(
}
users <- UserRepo byIds pov.game.userIds
} yield Game.make(
game = ChessGame(
chess = ChessGame(
situation = Situation(
board = Board(pieces, variant = pov.game.variant).withCastles {
situation.fold(Castles.init)(_.situation.board.history.castles)
@ -95,7 +95,6 @@ private[round] final class Rematcher(
whitePlayer = returnPlayer(pov.game, White, users),
blackPlayer = returnPlayer(pov.game, Black, users),
mode = if (users.exists(_.lame)) chess.Mode.Casual else pov.game.mode,
variant = pov.game.variant,
source = pov.game.source | Source.Lobby,
daysPerTurn = pov.game.daysPerTurn,
pgnImport = None

View File

@ -20,9 +20,8 @@ case class AiConfig(
def >> = (variant.id, timeMode.id, time, increment, days, level, color.name, fen).some
def game = fenGame { chessGame =>
val realVariant = chessGame.board.variant
Game.make(
game = chessGame,
chess = chessGame,
whitePlayer = Player.make(
color = ChessColor.White,
aiLevel = creatorColor.black option level
@ -32,8 +31,7 @@ case class AiConfig(
aiLevel = creatorColor.white option level
),
mode = Mode.Casual,
variant = realVariant,
source = (realVariant == chess.variant.FromPosition).fold(Source.Position, Source.Ai),
source = (chessGame.board.variant.fromPosition).fold(Source.Position, Source.Ai),
daysPerTurn = makeDaysPerTurn,
pgnImport = None
)

View File

@ -15,11 +15,10 @@ private[setup] case object ApiConfig extends Config {
val days = 2
def game = Game.make(
game = makeGame,
chess = makeGame,
whitePlayer = Player.white,
blackPlayer = Player.black,
mode = mode,
variant = variant,
source = Source.Api,
pgnImport = None
)

View File

@ -74,9 +74,15 @@ trait Positional { self: Config =>
val game = builder(chessGame)
state.fold(game) {
case sit @ SituationPlus(Situation(board, _), _) => game.copy(
variant = chess.variant.FromPosition,
history = board.history,
turns = sit.turns
chess = game.chess.copy(
situation = game.situation.copy(
board = game.board.copy(
history = board.history,
variant = chess.variant.FromPosition
)
),
turns = sit.turns
)
)
}
}

View File

@ -67,7 +67,7 @@ final class JsonView(getLightUser: LightUser.Getter) {
private def gameJson(hostId: String)(g: Game) = Json.obj(
"id" -> g.id,
"status" -> g.status.id,
"fen" -> (chess.format.Forsyth exportBoard g.toChess.board),
"fen" -> (chess.format.Forsyth exportBoard g.board),
"lastMove" -> ~g.lastMoveKeys,
"orient" -> g.playerByUserId(hostId).map(_.color)
)

View File

@ -186,14 +186,13 @@ final class SimulApi(
whiteUser = hostColor.fold(host, user)
blackUser = hostColor.fold(user, host)
game1 = Game.make(
game = chess.Game(
chess = chess.Game(
situation = chess.Situation(pairing.player.variant),
clock = simul.clock.chessClockOf(hostColor).start.some
),
whitePlayer = lila.game.Player.white,
blackPlayer = lila.game.Player.black,
mode = chess.Mode.Casual,
variant = pairing.player.variant,
source = lila.game.Source.Simul,
pgnImport = None
)

View File

@ -15,7 +15,7 @@ final class AutoPairing(
val user1 = usersMap get pairing.user1 err s"Missing pairing user1 $pairing"
val user2 = usersMap get pairing.user2 err s"Missing pairing user2 $pairing"
val game1 = Game.make(
game = chess.Game(
chess = chess.Game(
variantOption = tour.variant.some,
fen = tour.position.some.filterNot(_.initial).map(_.fen)
) |> { g =>
@ -29,9 +29,6 @@ final class AutoPairing(
whitePlayer = GamePlayer.white,
blackPlayer = GamePlayer.black,
mode = tour.mode,
variant =
if (tour.position.initial) tour.variant
else chess.variant.FromPosition,
source = Source.Tournament,
pgnImport = None
)

View File

@ -227,7 +227,7 @@ final class JsonView(
}
Json.obj(
"id" -> game.id,
"fen" -> (chess.format.Forsyth exportBoard game.toChess.board),
"fen" -> (chess.format.Forsyth exportBoard game.board),
"color" -> (game.variant match {
case chess.variant.RacingKings => chess.White
case _ => game.firstColor