lila/modules/challenge/src/main/ChallengeJoiner.scala

86 lines
2.9 KiB
Scala

package lila.challenge
import cats.data.Validated
import cats.data.Validated.{ Invalid, Valid }
import chess.format.Forsyth
import chess.format.Forsyth.SituationPlus
import chess.{ Color, Mode, Situation }
import scala.util.chaining._
import lila.game.{ Game, Player, Pov, Source }
import lila.user.User
final private class ChallengeJoiner(
gameRepo: lila.game.GameRepo,
userRepo: lila.user.UserRepo,
onStart: lila.round.OnStart
)(implicit ec: scala.concurrent.ExecutionContext) {
def apply(c: Challenge, destUser: Option[User], color: Option[Color]): Fu[Validated[String, Pov]] =
gameRepo exists c.id flatMap {
case true => fuccess(Invalid("The challenge has already been accepted"))
case _ if color.map(Challenge.ColorChoice.apply).has(c.colorChoice) =>
fuccess(Invalid("This color has already been chosen"))
case _ =>
c.challengerUserId.??(userRepo.byId) flatMap { origUser =>
val game = ChallengeJoiner.createGame(c, origUser, destUser, color)
(gameRepo insertDenormalized game) >>- onStart(game.id) inject
Valid(Pov(game, !c.finalColor))
}
}
}
private object ChallengeJoiner {
def createGame(
c: Challenge,
origUser: Option[User],
destUser: Option[User],
color: Option[Color]
): Game = {
def makeChess(variant: chess.variant.Variant): chess.Game =
chess.Game(situation = Situation(variant), clock = c.clock.map(_.config.toClock))
val baseState = c.initialFen.ifTrue(c.variant.fromPosition || c.variant.chess960) flatMap {
Forsyth.<<<@(c.variant, _)
}
val (chessGame, state) = baseState.fold(makeChess(c.variant) -> none[SituationPlus]) {
case sp @ SituationPlus(sit, _) =>
val game = chess.Game(
situation = sit,
turns = sp.turns,
startedAtTurn = sp.turns,
clock = c.clock.map(_.config.toClock)
)
if (c.variant.fromPosition && Forsyth.>>(game).initial)
makeChess(chess.variant.Standard) -> none
else game -> baseState
}
val perfPicker = (perfs: lila.user.Perfs) => perfs(c.perfType)
Game
.make(
chess = chessGame,
whitePlayer = Player.make(chess.White, c.finalColor.fold(origUser, destUser), perfPicker),
blackPlayer = Player.make(chess.Black, c.finalColor.fold(destUser, origUser), perfPicker),
mode = if (chessGame.board.variant.fromPosition) Mode.Casual else c.mode,
source = Source.Friend,
daysPerTurn = c.daysPerTurn,
pgnImport = None
)
.withId(c.id)
.pipe { g =>
state.fold(g) { case sit @ SituationPlus(Situation(board, _), _) =>
g.copy(
chess = g.chess.copy(
situation = g.situation.copy(
board = g.board.copy(history = board.history)
),
turns = sit.turns
)
)
}
}
.start
}
}