move pgn to a distinct mongodb collection - wip
parent
0f05de9582
commit
4d920e2ac6
|
@ -5,12 +5,11 @@ import chess.{ Game, Move }
|
|||
import game.DbGame
|
||||
import analyse.Analysis
|
||||
|
||||
import scalaz.effects._
|
||||
import akka.dispatch.Future
|
||||
|
||||
trait Ai {
|
||||
|
||||
def play(dbGame: DbGame, initialFen: Option[String]): Future[Valid[(Game, Move)]]
|
||||
def play(dbGame: DbGame, pgn: String, initialFen: Option[String]): Future[Valid[(Game, Move)]]
|
||||
|
||||
def analyse(dbGame: DbGame, initialFen: Option[String]): Future[Valid[Analysis]]
|
||||
def analyse(pgn: String, initialFen: Option[String]): Future[Valid[Analysis]]
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import akka.dispatch.Future
|
|||
|
||||
final class StupidAi extends Ai with core.Futuristic {
|
||||
|
||||
def play(dbGame: DbGame, initialFen: Option[String]): Future[Valid[(Game, Move)]] = Future {
|
||||
def play(dbGame: DbGame, pgn: String, initialFen: Option[String]): Future[Valid[(Game, Move)]] = Future {
|
||||
|
||||
val game = dbGame.toChess
|
||||
|
||||
|
@ -21,6 +21,6 @@ final class StupidAi extends Ai with core.Futuristic {
|
|||
} yield newChessGameAndMove
|
||||
}
|
||||
|
||||
def analyse(dbGame: DbGame, initialFen: Option[String]) =
|
||||
def analyse(pgn: String, initialFen: Option[String]) =
|
||||
throw new RuntimeException("Stupid analysis is not implemented")
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@ final class Ai(server: Server) extends lila.ai.Ai with Stockfish {
|
|||
|
||||
import model._
|
||||
|
||||
def play(dbGame: DbGame, initialFen: Option[String]): Future[Valid[(Game, Move)]] =
|
||||
server.play(dbGame.pgn, initialFen, dbGame.aiLevel | 1) map { validMove ⇒
|
||||
def play(dbGame: DbGame, pgn: String, initialFen: Option[String]): Future[Valid[(Game, Move)]] =
|
||||
server.play(pgn, initialFen, dbGame.aiLevel | 1) map { validMove ⇒
|
||||
validMove flatMap { applyMove(dbGame, _) }
|
||||
}
|
||||
|
||||
def analyse(dbGame: DbGame, initialFen: Option[String]): Future[Valid[Analysis]] =
|
||||
server.analyse(dbGame.pgn, initialFen)
|
||||
def analyse(pgn: String, initialFen: Option[String]): Future[Valid[Analysis]] =
|
||||
server.analyse(pgn, initialFen)
|
||||
|
||||
private implicit val executor = Akka.system.dispatcher
|
||||
}
|
||||
|
|
|
@ -18,14 +18,14 @@ final class Client(
|
|||
val playUrl: String,
|
||||
analyseUrl: String) extends ai.Client with Stockfish {
|
||||
|
||||
def play(dbGame: DbGame, initialFen: Option[String]): Future[Valid[(Game, Move)]] = {
|
||||
fetchMove(dbGame.pgn, initialFen | "", dbGame.aiLevel | 1) map {
|
||||
def play(dbGame: DbGame, pgn: String, initialFen: Option[String]): Future[Valid[(Game, Move)]] = {
|
||||
fetchMove(pgn, initialFen | "", dbGame.aiLevel | 1) map {
|
||||
applyMove(dbGame, _)
|
||||
}
|
||||
}
|
||||
|
||||
def analyse(dbGame: DbGame, initialFen: Option[String]): Future[Valid[Analysis]] =
|
||||
fetchAnalyse(dbGame.pgn, initialFen | "") map {
|
||||
def analyse(pgn: String, initialFen: Option[String]): Future[Valid[Analysis]] =
|
||||
fetchAnalyse(pgn, initialFen | "") map {
|
||||
Analysis(_, true)
|
||||
} recover {
|
||||
case e ⇒ !![Analysis](e.getMessage)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package lila
|
||||
package analyse
|
||||
|
||||
import game.{ DbGame, GameRepo }
|
||||
import game.{ DbGame, GameRepo, PgnRepo }
|
||||
import user.UserRepo
|
||||
import core.Settings
|
||||
|
||||
|
@ -12,9 +12,10 @@ import akka.dispatch.Future
|
|||
final class AnalyseEnv(
|
||||
settings: Settings,
|
||||
gameRepo: GameRepo,
|
||||
pgnRepo: PgnRepo,
|
||||
userRepo: UserRepo,
|
||||
mongodb: String ⇒ MongoCollection,
|
||||
generator: () ⇒ (DbGame, Option[String]) ⇒ Future[Valid[Analysis]]) {
|
||||
generator: () ⇒ (String, Option[String]) ⇒ Future[Valid[Analysis]]) {
|
||||
|
||||
import settings._
|
||||
|
||||
|
@ -29,6 +30,7 @@ final class AnalyseEnv(
|
|||
lazy val analyser = new Analyser(
|
||||
analysisRepo = analysisRepo,
|
||||
gameRepo = gameRepo,
|
||||
pgnRepo = pgnRepo,
|
||||
generator = generator)
|
||||
|
||||
lazy val paginator = new PaginatorBuilder(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package lila
|
||||
package analyse
|
||||
|
||||
import game.{ DbGame, GameRepo }
|
||||
import game.{ DbGame, GameRepo, PgnRepo }
|
||||
|
||||
import scalaz.effects._
|
||||
import play.api.libs.concurrent.Akka
|
||||
|
@ -13,7 +13,8 @@ import akka.util.Timeout
|
|||
final class Analyser(
|
||||
analysisRepo: AnalysisRepo,
|
||||
gameRepo: GameRepo,
|
||||
generator: () ⇒ (DbGame, Option[String]) ⇒ Future[Valid[Analysis]]) {
|
||||
pgnRepo: PgnRepo,
|
||||
generator: () ⇒ (String, Option[String]) ⇒ Future[Valid[Analysis]]) {
|
||||
|
||||
private implicit val executor = Akka.system.dispatcher
|
||||
private implicit val timeout = Timeout(5 minutes)
|
||||
|
@ -35,11 +36,12 @@ final class Analyser(
|
|||
analysisRepo userInProgress userId
|
||||
))
|
||||
gameOption ← ioToFuture(gameRepo game id)
|
||||
pgnString <- ioToFuture(pgnRepo get id)
|
||||
result ← gameOption.filterNot(_ ⇒ userInProgress).fold(
|
||||
game ⇒ for {
|
||||
_ ← ioToFuture(analysisRepo.progress(id, userId))
|
||||
initialFen ← ioToFuture(gameRepo initialFen id)
|
||||
analysis ← generator()(game, initialFen)
|
||||
analysis ← generator()(pgnString, initialFen)
|
||||
_ ← ioToFuture(analysis.fold(
|
||||
analysisRepo.fail(id, _),
|
||||
analysisRepo.done(id, _)
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila
|
|||
package analyse
|
||||
|
||||
import chess.format.Forsyth
|
||||
import chess.format.pgn
|
||||
import chess.format.{ pgn => chessPgn }
|
||||
import chess.format.pgn.{ Pgn, Tag }
|
||||
import game.{ DbGame, DbPlayer, GameRepo }
|
||||
import user.{ User, UserRepo }
|
||||
|
@ -17,9 +17,9 @@ final class PgnDump(
|
|||
|
||||
import PgnDump._
|
||||
|
||||
def >>(game: DbGame): IO[Pgn] = for {
|
||||
def apply(game: DbGame, pgn: String): IO[Pgn] = for {
|
||||
ts ← tags(game)
|
||||
pgnObj = Pgn(ts, turns(game))
|
||||
pgnObj = Pgn(ts, turns(pgn))
|
||||
analysis ← analyser get game.id
|
||||
} yield analysis.fold(Annotator(pgnObj, _), pgnObj)
|
||||
|
||||
|
@ -65,12 +65,12 @@ final class PgnDump(
|
|||
Tag("SetUp", "1")
|
||||
))
|
||||
|
||||
private def turns(game: DbGame): List[pgn.Turn] =
|
||||
(game.pgn split ' ' grouped 2).zipWithIndex.toList map {
|
||||
case (moves, index) ⇒ pgn.Turn(
|
||||
private def turns(pgn: String): List[chessPgn.Turn] =
|
||||
(pgn split ' ' grouped 2).zipWithIndex.toList map {
|
||||
case (moves, index) ⇒ chessPgn.Turn(
|
||||
number = index + 1,
|
||||
white = moves.headOption map { pgn.Move(_) },
|
||||
black = moves.tail.headOption map { pgn.Move(_) })
|
||||
white = moves.headOption map { chessPgn.Move(_) },
|
||||
black = moves.tail.headOption map { chessPgn.Move(_) })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import scalaz.effects._
|
|||
object Analyse extends LilaController {
|
||||
|
||||
private def gameRepo = env.game.gameRepo
|
||||
private def pgnRepo = env.game.pgnRepo
|
||||
private def pgnDump = env.analyse.pgnDump
|
||||
private def openingExplorer = chess.OpeningExplorer
|
||||
private def bookmarkApi = env.bookmark.api
|
||||
|
@ -40,7 +41,8 @@ object Analyse extends LilaController {
|
|||
for {
|
||||
roomHtml ← roundMessenger renderWatcher pov.game
|
||||
bookmarkers ← bookmarkApi usersByGame pov.game
|
||||
pgn ← pgnDump >> pov.game
|
||||
pgnString ← pgnRepo get id
|
||||
pgn ← pgnDump(pov.game, pgnString)
|
||||
analysis ← analyser get pov.game.id
|
||||
tour ← tournamentRepo byId pov.game.tournamentId
|
||||
} yield html.analyse.replay(
|
||||
|
@ -48,7 +50,7 @@ object Analyse extends LilaController {
|
|||
pgn.toString,
|
||||
Html(roomHtml),
|
||||
bookmarkers,
|
||||
openingExplorer openingOf pov.game.pgn,
|
||||
openingExplorer openingOf pgnString,
|
||||
analysis,
|
||||
roundSocket blockingVersion pov.gameId,
|
||||
tour)
|
||||
|
@ -68,7 +70,8 @@ object Analyse extends LilaController {
|
|||
gameOption ← gameRepo game id
|
||||
res ← gameOption.fold(
|
||||
game ⇒ for {
|
||||
pgnObj ← pgnDump >> game
|
||||
pgnString ← pgnRepo get id
|
||||
pgnObj ← pgnDump(game, pgnString)
|
||||
content = pgnObj.toString
|
||||
filename ← pgnDump filename game
|
||||
} yield Ok(content).withHeaders(
|
||||
|
|
|
@ -65,6 +65,7 @@ final class CoreEnv private (application: Application, val settings: Settings) {
|
|||
settings = settings,
|
||||
mongodb = mongodb.apply _,
|
||||
gameRepo = game.gameRepo,
|
||||
pgnRepo = game.pgnRepo,
|
||||
hookRepo = lobby.hookRepo,
|
||||
fisherman = lobby.fisherman,
|
||||
userRepo = user.userRepo,
|
||||
|
@ -90,6 +91,8 @@ final class CoreEnv private (application: Application, val settings: Settings) {
|
|||
settings = settings,
|
||||
mongodb = mongodb.apply _,
|
||||
gameRepo = game.gameRepo,
|
||||
pgnRepo = game.pgnRepo,
|
||||
rewind = game.rewind,
|
||||
userRepo = user.userRepo,
|
||||
eloUpdater = user.eloUpdater,
|
||||
i18nKeys = i18n.keys,
|
||||
|
@ -114,6 +117,7 @@ final class CoreEnv private (application: Application, val settings: Settings) {
|
|||
lazy val analyse = new lila.analyse.AnalyseEnv(
|
||||
settings = settings,
|
||||
gameRepo = game.gameRepo,
|
||||
pgnRepo = game.pgnRepo,
|
||||
userRepo = user.userRepo,
|
||||
mongodb = mongodb.apply _,
|
||||
() ⇒ ai.ai().analyse _)
|
||||
|
@ -127,7 +131,8 @@ final class CoreEnv private (application: Application, val settings: Settings) {
|
|||
lazy val site = new lila.site.SiteEnv(
|
||||
app = app,
|
||||
settings = settings,
|
||||
gameRepo = game.gameRepo)
|
||||
gameRepo = game.gameRepo,
|
||||
pgnRepo = game.pgnRepo)
|
||||
|
||||
lazy val security = new lila.security.SecurityEnv(
|
||||
settings = settings,
|
||||
|
@ -138,7 +143,8 @@ final class CoreEnv private (application: Application, val settings: Settings) {
|
|||
lazy val search = new lila.search.SearchEnv(
|
||||
settings = settings,
|
||||
mongodb = mongodb.apply _,
|
||||
gameRepo = game.gameRepo)
|
||||
gameRepo = game.gameRepo,
|
||||
pgnRepo = game.pgnRepo)
|
||||
|
||||
lazy val metaHub = new lila.socket.MetaHub(
|
||||
List(site.hub, lobby.hub, round.hubMaster, tournament.hubMaster))
|
||||
|
|
|
@ -24,6 +24,7 @@ final class Settings(config: Config, val IsDev: Boolean) {
|
|||
val GameCachedNbTtl = millis("game.cached.nb.ttl")
|
||||
val GamePaginatorMaxPerPage = getInt("game.paginator.max_per_page")
|
||||
val GameCollectionGame = getString("game.collection.game")
|
||||
val GameCollectionPgn = getString("game.collection.pgn")
|
||||
|
||||
val SearchESHost = getString("search.elasticsearch.host")
|
||||
val SearchESPort = getInt("search.elasticsearch.port")
|
||||
|
|
|
@ -5,7 +5,6 @@ import round.{ Event, Progress }
|
|||
import user.User
|
||||
import chess.{ History ⇒ ChessHistory, Role, Board, Move, Pos, Game, Clock, Status, Color, Piece, Variant, Mode }
|
||||
import Color._
|
||||
import chess.format.{ pgn ⇒ chessPgn }
|
||||
import chess.Pos.piotr
|
||||
import chess.Role.forsyth
|
||||
|
||||
|
@ -18,7 +17,6 @@ case class DbGame(
|
|||
id: String,
|
||||
whitePlayer: DbPlayer,
|
||||
blackPlayer: DbPlayer,
|
||||
pgn: String,
|
||||
status: Status,
|
||||
turns: Int,
|
||||
clock: Option[Clock],
|
||||
|
@ -104,7 +102,6 @@ case class DbGame(
|
|||
Game(
|
||||
board = Board(pieces, toChessHistory, variant),
|
||||
player = Color(0 == turns % 2),
|
||||
pgnMoves = pgn,
|
||||
clock = clock,
|
||||
deads = deads,
|
||||
turns = turns
|
||||
|
@ -145,7 +142,6 @@ case class DbGame(
|
|||
)
|
||||
|
||||
val updated = copy(
|
||||
pgn = game.pgnMoves,
|
||||
whitePlayer = copyPlayer(whitePlayer),
|
||||
blackPlayer = copyPlayer(blackPlayer),
|
||||
turns = game.turns,
|
||||
|
@ -174,43 +170,6 @@ case class DbGame(
|
|||
Progress(this, updated, finalEvents)
|
||||
}
|
||||
|
||||
def rewind(initialFen: Option[String]): Valid[Progress] = {
|
||||
chessPgn.Reader.withSans(
|
||||
pgn = pgn,
|
||||
op = _.init,
|
||||
tags = initialFen.fold(
|
||||
fen ⇒ List(
|
||||
chessPgn.Tag(_.FEN, fen),
|
||||
chessPgn.Tag(_.Variant, variant.name)
|
||||
),
|
||||
Nil)
|
||||
) map { replay ⇒
|
||||
val rewindedGame = replay.game
|
||||
val rewindedHistory = rewindedGame.board.history
|
||||
val rewindedSituation = rewindedGame.situation
|
||||
def rewindPlayer(player: DbPlayer) = player.copy(
|
||||
ps = player encodePieces rewindedGame.allPieces,
|
||||
isProposingTakeback = false)
|
||||
Progress(this, copy(
|
||||
pgn = rewindedGame.pgnMoves,
|
||||
whitePlayer = rewindPlayer(whitePlayer),
|
||||
blackPlayer = rewindPlayer(blackPlayer),
|
||||
turns = rewindedGame.turns,
|
||||
positionHashes = rewindedHistory.positionHashes mkString,
|
||||
castles = rewindedHistory.castleNotation,
|
||||
lastMove = rewindedHistory.lastMove map { case (a, b) ⇒ a + " " + b },
|
||||
status =
|
||||
if (rewindedSituation.checkMate) Status.Mate
|
||||
else if (rewindedSituation.staleMate) Status.Stalemate
|
||||
else if (rewindedSituation.autoDraw) Status.Draw
|
||||
else status,
|
||||
clock = clock map (_.switch),
|
||||
check = if (rewindedSituation.check) rewindedSituation.kingPos else None,
|
||||
lastMoveTime = nowSeconds.some
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
def updatePlayer(color: Color, f: DbPlayer ⇒ DbPlayer) = color match {
|
||||
case White ⇒ copy(whitePlayer = f(whitePlayer))
|
||||
case Black ⇒ copy(blackPlayer = f(blackPlayer))
|
||||
|
@ -366,7 +325,6 @@ case class DbGame(
|
|||
def encode = RawDbGame(
|
||||
id = id,
|
||||
p = players map (_.encode),
|
||||
pgn = Some(pgn),
|
||||
s = status.id,
|
||||
t = turns,
|
||||
c = clock map RawDbClock.encode,
|
||||
|
@ -426,7 +384,6 @@ object DbGame {
|
|||
id = IdGenerator.game,
|
||||
whitePlayer = whitePlayer withEncodedPieces game.allPieces,
|
||||
blackPlayer = blackPlayer withEncodedPieces game.allPieces,
|
||||
pgn = "",
|
||||
status = Status.Created,
|
||||
turns = game.turns,
|
||||
clock = game.clock,
|
||||
|
@ -444,7 +401,6 @@ object DbGame {
|
|||
case class RawDbGame(
|
||||
@Key("_id") id: String,
|
||||
p: List[RawDbPlayer],
|
||||
pgn: Option[String],
|
||||
s: Int,
|
||||
t: Int,
|
||||
c: Option[RawDbClock],
|
||||
|
@ -471,7 +427,6 @@ case class RawDbGame(
|
|||
id = id,
|
||||
whitePlayer = whitePlayer,
|
||||
blackPlayer = blackPlayer,
|
||||
pgn = pgn | "",
|
||||
status = trueStatus,
|
||||
turns = t,
|
||||
clock = c map (_.decode),
|
||||
|
|
|
@ -11,7 +11,6 @@ final class GameDiff(a: RawDbGame, b: RawDbGame) {
|
|||
if (f(a) != f(b)) builder += name -> f(b)
|
||||
}
|
||||
|
||||
d("pgn", _.pgn)
|
||||
d("s", _.s)
|
||||
d("t", _.t)
|
||||
d("lm", _.lm) // lastMove
|
||||
|
|
|
@ -13,6 +13,8 @@ final class GameEnv(
|
|||
|
||||
lazy val gameRepo = new GameRepo(mongodb(GameCollectionGame))
|
||||
|
||||
lazy val pgnRepo = new PgnRepo(mongodb(GameCollectionPgn))
|
||||
|
||||
lazy val cached = new Cached(
|
||||
gameRepo = gameRepo,
|
||||
nbTtl = GameCachedNbTtl)
|
||||
|
@ -29,4 +31,6 @@ final class GameEnv(
|
|||
lazy val export = Export(gameRepo) _
|
||||
|
||||
lazy val listMenu = ListMenu(cached) _
|
||||
|
||||
lazy val rewind = new Rewind
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package lila
|
||||
package game
|
||||
|
||||
import com.novus.salat._
|
||||
import com.novus.salat.dao._
|
||||
import com.mongodb.casbah.MongoCollection
|
||||
import com.mongodb.casbah.query.Imports._
|
||||
import scalaz.effects._
|
||||
|
||||
final class PgnRepo(collection: MongoCollection) {
|
||||
|
||||
def get(id: String): IO[String] = io {
|
||||
collection.findOne(idSelector(id)).flatMap(_.getAs[String]("p")) | ""
|
||||
}
|
||||
|
||||
def save(id: String, pgn: String): IO[Unit] = io {
|
||||
collection.update(idSelector(id), DBObject("p" -> pgn), upsert = true, multi = false)
|
||||
}
|
||||
|
||||
private def idSelector(id: String) = DBObject("_id" -> id)
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package lila
|
||||
package game
|
||||
|
||||
import round.Progress
|
||||
import chess.format.{ pgn ⇒ chessPgn }
|
||||
import chess.Status
|
||||
|
||||
final class Rewind {
|
||||
|
||||
def apply(
|
||||
game: DbGame,
|
||||
pgn: String,
|
||||
initialFen: Option[String]): Valid[(Progress, String)] = {
|
||||
chessPgn.Reader.withSans(
|
||||
pgn = pgn,
|
||||
op = _.init,
|
||||
tags = initialFen.fold(
|
||||
fen ⇒ List(
|
||||
chessPgn.Tag(_.FEN, fen),
|
||||
chessPgn.Tag(_.Variant, game.variant.name)
|
||||
),
|
||||
Nil)
|
||||
) map { replay ⇒
|
||||
val rewindedGame = replay.game
|
||||
val rewindedHistory = rewindedGame.board.history
|
||||
val rewindedSituation = rewindedGame.situation
|
||||
def rewindPlayer(player: DbPlayer) = player.copy(
|
||||
ps = player encodePieces rewindedGame.allPieces,
|
||||
isProposingTakeback = false)
|
||||
Progress(game, game.copy(
|
||||
whitePlayer = rewindPlayer(game.whitePlayer),
|
||||
blackPlayer = rewindPlayer(game.blackPlayer),
|
||||
turns = rewindedGame.turns,
|
||||
positionHashes = rewindedHistory.positionHashes mkString,
|
||||
castles = rewindedHistory.castleNotation,
|
||||
lastMove = rewindedHistory.lastMove map { case (a, b) ⇒ a + " " + b },
|
||||
status =
|
||||
if (rewindedSituation.checkMate) Status.Mate
|
||||
else if (rewindedSituation.staleMate) Status.Stalemate
|
||||
else if (rewindedSituation.autoDraw) Status.Draw
|
||||
else game.status,
|
||||
clock = game.clock map (_.switch),
|
||||
check = if (rewindedSituation.check) rewindedSituation.kingPos else None,
|
||||
lastMoveTime = nowSeconds.some
|
||||
)) -> rewindedGame.pgnMoves
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ package lila
|
|||
package round
|
||||
|
||||
import ai.Ai
|
||||
import game.{ GameRepo, Pov, PovRef, Handler }
|
||||
import game.{ GameRepo, PgnRepo, Pov, PovRef, Handler }
|
||||
import i18n.I18nKey.{ Select ⇒ SelectI18nKey }
|
||||
import chess.Role
|
||||
import chess.Pos.posAt
|
||||
|
@ -17,6 +17,7 @@ import akka.util.Timeout
|
|||
|
||||
final class Hand(
|
||||
gameRepo: GameRepo,
|
||||
pgnRepo: PgnRepo,
|
||||
messenger: Messenger,
|
||||
takeback: Takeback,
|
||||
ai: () ⇒ Ai,
|
||||
|
@ -52,7 +53,8 @@ final class Hand(
|
|||
initialFen ← progress.game.variant.standard.fold(
|
||||
io(none[String]),
|
||||
gameRepo initialFen progress.game.id).toFuture
|
||||
aiResult ← ai().play(progress.game, initialFen)
|
||||
pgnString ← (pgnRepo get povRef.gameId).toFuture
|
||||
aiResult ← ai().play(progress.game, pgnString, initialFen)
|
||||
eventsAndFen ← aiResult.fold(
|
||||
err ⇒ Future(failure(err)), {
|
||||
case (newChessGame, move) ⇒ (for {
|
||||
|
@ -178,7 +180,8 @@ final class Hand(
|
|||
def takebackAccept(fullId: String): IOValidEvents = fromPov(fullId) { pov ⇒
|
||||
if (pov.opponent.isProposingTakeback && pov.game.nonTournament) for {
|
||||
fen ← gameRepo initialFen pov.game.id
|
||||
res ← takeback(pov.game, fen).sequence
|
||||
pgn ← pgnRepo get pov.game.id
|
||||
res ← takeback(pov.game, pgn, fen).sequence
|
||||
} yield res
|
||||
else io {
|
||||
!!("opponent is not proposing a takeback")
|
||||
|
@ -188,11 +191,13 @@ final class Hand(
|
|||
def takebackOffer(fullId: String): IOValidEvents = fromPov(fullId) {
|
||||
case pov @ Pov(g1, color) ⇒
|
||||
if (g1.playable && g1.bothPlayersHaveMoved && g1.nonTournament) {
|
||||
gameRepo initialFen pov.game.id flatMap { fen ⇒
|
||||
if (g1.player(!color).isAi)
|
||||
takeback.double(pov.game, fen).sequence
|
||||
for {
|
||||
fen ← gameRepo initialFen pov.game.id
|
||||
pgn ← pgnRepo get pov.game.id
|
||||
result ← if (g1.player(!color).isAi)
|
||||
takeback.double(pov.game, pgn, fen).sequence
|
||||
else if (g1.player(!color).isProposingTakeback)
|
||||
takeback(pov.game, fen).sequence
|
||||
takeback(pov.game, pgn, fen).sequence
|
||||
else for {
|
||||
p1 ← messenger.systemMessage(g1, _.takebackPropositionSent) map { es ⇒
|
||||
Progress(g1, Event.ReloadTable(!color) :: es)
|
||||
|
@ -200,7 +205,7 @@ final class Hand(
|
|||
p2 = p1 map { g ⇒ g.updatePlayer(color, _.proposeTakeback) }
|
||||
_ ← gameRepo save p2
|
||||
} yield success(p2.events)
|
||||
}
|
||||
} yield result
|
||||
}
|
||||
else io {
|
||||
!!("invalid takeback proposition " + fullId)
|
||||
|
|
|
@ -7,7 +7,7 @@ import com.mongodb.casbah.MongoCollection
|
|||
import com.mongodb.casbah.query.Imports._
|
||||
import scalaz.effects._
|
||||
|
||||
class RoomRepo(collection: MongoCollection)
|
||||
final class RoomRepo(collection: MongoCollection)
|
||||
extends SalatDAO[Room, String](collection) {
|
||||
|
||||
def room(id: String): IO[Room] = io {
|
||||
|
|
|
@ -7,7 +7,7 @@ import akka.actor.Props
|
|||
import play.api.libs.concurrent._
|
||||
import play.api.Application
|
||||
|
||||
import game.{ GameRepo, DbGame }
|
||||
import game.{ GameRepo, PgnRepo, DbGame, Rewind }
|
||||
import user.{ UserRepo, User }
|
||||
import elo.EloUpdater
|
||||
import ai.Ai
|
||||
|
@ -20,6 +20,8 @@ final class RoundEnv(
|
|||
settings: Settings,
|
||||
mongodb: String ⇒ MongoCollection,
|
||||
gameRepo: GameRepo,
|
||||
pgnRepo: PgnRepo,
|
||||
rewind: Rewind,
|
||||
userRepo: UserRepo,
|
||||
eloUpdater: EloUpdater,
|
||||
i18nKeys: I18nKeys,
|
||||
|
@ -57,6 +59,7 @@ final class RoundEnv(
|
|||
|
||||
lazy val hand = new Hand(
|
||||
gameRepo = gameRepo,
|
||||
pgnRepo = pgnRepo,
|
||||
messenger = messenger,
|
||||
ai = ai,
|
||||
finisher = finisher,
|
||||
|
@ -80,6 +83,8 @@ final class RoundEnv(
|
|||
|
||||
lazy val takeback = new Takeback(
|
||||
gameRepo = gameRepo,
|
||||
pgnRepo = pgnRepo,
|
||||
rewind = rewind,
|
||||
messenger = messenger)
|
||||
|
||||
lazy val messenger = new Messenger(
|
||||
|
|
|
@ -1,23 +1,29 @@
|
|||
package lila
|
||||
package round
|
||||
|
||||
import game.{ GameRepo, DbGame }
|
||||
import game.{ GameRepo, PgnRepo, DbGame, Rewind }
|
||||
import scalaz.effects._
|
||||
|
||||
final class Takeback(
|
||||
gameRepo: GameRepo,
|
||||
pgnRepo: PgnRepo,
|
||||
rewind: Rewind,
|
||||
messenger: Messenger) {
|
||||
|
||||
def apply(game: DbGame, initialFen: Option[String]): Valid[IO[List[Event]]] =
|
||||
game rewind initialFen map save mapFail failInfo(game)
|
||||
def apply(game: DbGame, pgn: String, initialFen: Option[String]): Valid[IO[List[Event]]] =
|
||||
rewind(game, pgn, initialFen) map {
|
||||
case (progress, newPgn) ⇒ pgnRepo.save(game.id, newPgn) flatMap { _ ⇒ save(progress) }
|
||||
} mapFail failInfo(game)
|
||||
|
||||
def double(game: DbGame, initialFen: Option[String]): Valid[IO[List[Event]]] = {
|
||||
def double(game: DbGame, pgn: String, initialFen: Option[String]): Valid[IO[List[Event]]] = {
|
||||
for {
|
||||
p1 ← game rewind initialFen
|
||||
p2 ← p1.game rewind initialFen map { p ⇒
|
||||
p1 withGame p.game
|
||||
first ← rewind(game, pgn, initialFen)
|
||||
(prog1, pgn1) = first
|
||||
second ← rewind(prog1.game, pgn1, initialFen) map {
|
||||
case (progress, newPgn) ⇒ (prog1 withGame progress.game, newPgn)
|
||||
}
|
||||
} yield save(p2)
|
||||
(prog2, pgn2) = second
|
||||
} yield pgnRepo.save(game.id, pgn2) flatMap { _ ⇒ save(prog2) }
|
||||
} mapFail failInfo(game)
|
||||
|
||||
def failInfo(game: DbGame) =
|
||||
|
|
|
@ -48,7 +48,7 @@ object Game {
|
|||
)
|
||||
}
|
||||
|
||||
def from(game: DbGame) = game.id -> (List(
|
||||
def from(pgn: String)(game: DbGame) = game.id -> (List(
|
||||
status -> game.status.is(_.Timeout).fold(Status.Resign, game.status).id.some,
|
||||
turns -> Some(math.ceil(game.turns.toFloat / 2)),
|
||||
rated -> game.rated.some,
|
||||
|
@ -59,7 +59,7 @@ object Game {
|
|||
ai -> game.aiLevel,
|
||||
date -> (dateFormatter print game.createdAt).some,
|
||||
duration -> game.estimateTotalTime.some,
|
||||
opening -> (OpeningExplorer openingOf game.pgn map (_.code.toLowerCase))
|
||||
opening -> (OpeningExplorer openingOf pgn map (_.code.toLowerCase))
|
||||
) collect {
|
||||
case (x, Some(y)) ⇒ x -> y
|
||||
}).toMap
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package lila
|
||||
package search
|
||||
|
||||
import game.{ GameRepo, DbGame, Query ⇒ GameQuery }
|
||||
import game.{ GameRepo, PgnRepo, DbGame, Query ⇒ GameQuery }
|
||||
|
||||
import scalaz.effects._
|
||||
import com.codahale.jerkson.Json
|
||||
|
@ -14,6 +14,7 @@ import com.mongodb.casbah.query.Imports._
|
|||
final class Indexer(
|
||||
es: EsIndexer,
|
||||
gameRepo: GameRepo,
|
||||
pgnRepo: PgnRepo,
|
||||
queue: Queue) {
|
||||
|
||||
val indexName = "lila"
|
||||
|
@ -49,15 +50,19 @@ final class Indexer(
|
|||
es.waitTillActive()
|
||||
es.putMapping(indexName, typeName, Json generate Map(typeName -> Game.mapping))
|
||||
es.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
private def indexQuery(query: DBObject, logging: Boolean = false): IO[Int] = io {
|
||||
val cursor = gameRepo find (GameQuery.frozen ++ query) sort GameQuery.sortCreated //limit 3000
|
||||
val size = cursor.count
|
||||
var nb = 0
|
||||
for (games ← cursor grouped 5000) {
|
||||
val pgns = games.map(g ⇒ pgnRepo get g.id).sequence.unsafePerformIO
|
||||
val gamesWithPgn = games zip pgns
|
||||
if (logging) println("Indexing %d of %d".format(nb, size))
|
||||
val actions = games map (_.decode map Game.from) collect {
|
||||
val actions = gamesWithPgn map {
|
||||
case (game, pgn) ⇒ game.decode map Game.from(pgn)
|
||||
} collect {
|
||||
case Some((id, doc)) ⇒
|
||||
es.index_prepare(indexName, typeName, id, Json generate doc).request
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package lila
|
||||
package search
|
||||
|
||||
import game.{ GameRepo, DbGame }
|
||||
import game.{ GameRepo, PgnRepo, DbGame }
|
||||
import core.Settings
|
||||
|
||||
import com.traackr.scalastic.elasticsearch
|
||||
|
@ -10,7 +10,8 @@ import com.mongodb.casbah.MongoCollection
|
|||
final class SearchEnv(
|
||||
settings: Settings,
|
||||
mongodb: String ⇒ MongoCollection,
|
||||
gameRepo: GameRepo) {
|
||||
gameRepo: GameRepo,
|
||||
pgnRepo: PgnRepo) {
|
||||
|
||||
import settings._
|
||||
|
||||
|
@ -19,6 +20,7 @@ final class SearchEnv(
|
|||
lazy val indexer = new Indexer(
|
||||
es = esIndexer,
|
||||
gameRepo = gameRepo,
|
||||
pgnRepo = pgnRepo,
|
||||
queue = queue)
|
||||
|
||||
lazy val paginator = new PaginatorBuilder(
|
||||
|
|
|
@ -2,9 +2,9 @@ package lila
|
|||
package setup
|
||||
|
||||
import http.Context
|
||||
import game.{ DbGame, GameRepo, Pov }
|
||||
import game.{ DbGame, GameRepo, PgnRepo, Pov }
|
||||
import user.User
|
||||
import chess.{ Game, Board, Color => ChessColor }
|
||||
import chess.{ Game, Board, Color ⇒ ChessColor }
|
||||
import ai.Ai
|
||||
import lobby.{ Hook, Fisherman }
|
||||
import i18n.I18nDomain
|
||||
|
@ -16,6 +16,7 @@ final class Processor(
|
|||
configRepo: UserConfigRepo,
|
||||
friendConfigMemo: FriendConfigMemo,
|
||||
gameRepo: GameRepo,
|
||||
pgnRepo: PgnRepo,
|
||||
fisherman: Fisherman,
|
||||
timelinePush: DbGame ⇒ IO[Unit],
|
||||
ai: () ⇒ Ai) extends core.Futuristic {
|
||||
|
@ -38,7 +39,8 @@ final class Processor(
|
|||
initialFen ← game.variant.standard.fold(
|
||||
io(none[String]),
|
||||
gameRepo initialFen game.id)
|
||||
aiResult ← { ai().play(game, initialFen) map (_.err) }.toIo
|
||||
pgnString ← pgnRepo get game.id
|
||||
aiResult ← { ai().play(game, pgnString, initialFen) map (_.err) }.toIo
|
||||
(newChessGame, move) = aiResult
|
||||
progress = game.update(newChessGame, move)
|
||||
_ ← gameRepo save progress
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila
|
|||
package setup
|
||||
|
||||
import core.Settings
|
||||
import game.{ DbGame, GameRepo }
|
||||
import game.{ DbGame, GameRepo, PgnRepo }
|
||||
import lobby.{ HookRepo, Fisherman }
|
||||
import round.Messenger
|
||||
import ai.Ai
|
||||
|
@ -15,6 +15,7 @@ final class SetupEnv(
|
|||
settings: Settings,
|
||||
mongodb: String ⇒ MongoCollection,
|
||||
gameRepo: GameRepo,
|
||||
pgnRepo: PgnRepo,
|
||||
hookRepo: HookRepo,
|
||||
fisherman: Fisherman,
|
||||
userRepo: UserRepo,
|
||||
|
@ -33,6 +34,7 @@ final class SetupEnv(
|
|||
configRepo = configRepo,
|
||||
friendConfigMemo = friendConfigMemo,
|
||||
gameRepo = gameRepo,
|
||||
pgnRepo = pgnRepo,
|
||||
fisherman = fisherman,
|
||||
timelinePush = timelinePush,
|
||||
ai = ai)
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.joda.time.DateTime
|
|||
import org.scala_tools.time.Imports._
|
||||
|
||||
// only works with standard chess (not chess960)
|
||||
final class Captcha(gameRepo: GameRepo) {
|
||||
final class Captcha(gameRepo: GameRepo, pgnRepo: PgnRepo) {
|
||||
|
||||
import Captcha._
|
||||
|
||||
|
@ -51,23 +51,27 @@ final class Captcha(gameRepo: GameRepo) {
|
|||
val gameOption = findCheckmateInDb(100) orElse findCheckmateInDb(1)
|
||||
for {
|
||||
game ← gameOption toValid "No checkmate available in db"
|
||||
challenge ← makeChallenge(game)
|
||||
pgnString = getGamePgn(game.id)
|
||||
challenge ← makeChallenge(game, pgnString)
|
||||
} yield challenge
|
||||
}
|
||||
|
||||
private def findCheckmateInDb(distribution: Int) =
|
||||
gameRepo.findRandomStandardCheckmate(distribution).unsafePerformIO
|
||||
|
||||
private def getGamePgn(id: String) = (pgnRepo get id).unsafePerformIO
|
||||
|
||||
private def getFromDb(id: String): Valid[Challenge] = {
|
||||
val gameOption = (gameRepo game id).unsafePerformIO
|
||||
for {
|
||||
game ← gameOption toValid "No such game: " + id
|
||||
challenge ← makeChallenge(game)
|
||||
pgnString = getGamePgn(game.id)
|
||||
challenge ← makeChallenge(game, pgnString)
|
||||
} yield challenge
|
||||
}
|
||||
|
||||
private def makeChallenge(game: DbGame): Valid[Challenge] = for {
|
||||
rewinded ← rewind(game)
|
||||
private def makeChallenge(game: DbGame, pgnString: String): Valid[Challenge] = for {
|
||||
rewinded ← rewind(game, pgnString)
|
||||
solutions ← solve(rewinded)
|
||||
} yield Challenge(game.id, fen(rewinded), rewinded.player, solutions)
|
||||
|
||||
|
@ -81,8 +85,8 @@ final class Captcha(gameRepo: GameRepo) {
|
|||
}
|
||||
} map (_.notation)
|
||||
|
||||
private def rewind(game: DbGame): Valid[Game] =
|
||||
pgn.Reader.withSans(game.pgn, _.init) map (_.game) mapFail failInfo(game)
|
||||
private def rewind(game: DbGame, pgnString: String): Valid[Game] =
|
||||
pgn.Reader.withSans(pgnString, _.init) map (_.game) mapFail failInfo(game)
|
||||
|
||||
private def fen(game: Game): String = Forsyth >> game takeWhile (_ != ' ')
|
||||
|
||||
|
|
|
@ -5,13 +5,14 @@ import akka.actor._
|
|||
import play.api.libs.concurrent._
|
||||
import play.api.Application
|
||||
|
||||
import game.GameRepo
|
||||
import game.{ GameRepo, PgnRepo }
|
||||
import core.Settings
|
||||
|
||||
final class SiteEnv(
|
||||
app: Application,
|
||||
settings: Settings,
|
||||
gameRepo: GameRepo) {
|
||||
gameRepo: GameRepo,
|
||||
pgnRepo: PgnRepo) {
|
||||
|
||||
implicit val ctx = app
|
||||
import settings._
|
||||
|
@ -19,7 +20,7 @@ final class SiteEnv(
|
|||
lazy val hub = Akka.system.actorOf(
|
||||
Props(new Hub(timeout = SiteUidTimeout)), name = ActorSiteHub)
|
||||
|
||||
lazy val socket = new Socket(hub = hub)
|
||||
lazy val socket = new Socket(hub)
|
||||
|
||||
lazy val captcha = new Captcha(gameRepo = gameRepo)
|
||||
lazy val captcha = new Captcha(gameRepo, pgnRepo)
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@ var gamesToMigrate = db.game2.find();
|
|||
var max = gamesToMigrate.count();
|
||||
var batchSize = 10000;
|
||||
var collection = db.game3;
|
||||
var pgnCollection = db.pgn;
|
||||
|
||||
print("Migrating " + max + " games");
|
||||
|
||||
collection.drop();
|
||||
pgnCollection.drop();
|
||||
|
||||
function rename(arr, from, to) {
|
||||
if (typeof arr[from] !== 'undefined') {
|
||||
|
@ -41,7 +43,7 @@ function compactPs(ps) { return ps.replace(/ /g, ''); }
|
|||
|
||||
function finishedOrAborted(game) { return game.status >= 25; }
|
||||
|
||||
var c, z;
|
||||
var c, z, pgn;
|
||||
var it = 0;
|
||||
var dat = new Date().getTime() / 1000;
|
||||
var finishedPlayerFieldsToRemove = ['previousMoveTs', 'lastDrawOffer', 'isOfferingDraw', 'isProposingTakeback'];
|
||||
|
@ -76,7 +78,6 @@ gamesToMigrate.forEach(function(g) {
|
|||
if (finishedOrAborted(g)) {
|
||||
delete g.positionHashes
|
||||
delete g.lmt;
|
||||
clean(g, 'pgn');
|
||||
} else {
|
||||
cleanOrRename(g, 'positionHashes', 'ph');
|
||||
}
|
||||
|
@ -108,7 +109,12 @@ gamesToMigrate.forEach(function(g) {
|
|||
delete p.isAi;
|
||||
rename(p, 'aiLevel', 'ai');
|
||||
});
|
||||
pgn = g.pgn;
|
||||
delete g.pgn;
|
||||
collection.insert(g);
|
||||
if (pgn !== null && pgn !== "") {
|
||||
pgnCollection.insert({_id: g.id, p: pgn});
|
||||
}
|
||||
++it;
|
||||
if (it % batchSize == 0) {
|
||||
var percent = Math.round((it / max) * 100);
|
||||
|
|
Loading…
Reference in New Issue