move pgn to a distinct mongodb collection - wip

pull/83/head
Thibault Duplessis 2012-10-12 22:37:34 +02:00
parent 0f05de9582
commit 4d920e2ac6
27 changed files with 197 additions and 119 deletions

View File

@ -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]]
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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)

View File

@ -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(

View File

@ -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, _)

View File

@ -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(_) })
}
}

View File

@ -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(

View File

@ -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))

View File

@ -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")

View File

@ -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),

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}
}
}

View File

@ -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)

View File

@ -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 {

View File

@ -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(

View File

@ -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) =

View File

@ -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

View File

@ -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
}

View File

@ -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(

View File

@ -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

View File

@ -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)

View File

@ -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 (_ != ' ')

View File

@ -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)
}

View File

@ -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);