Implement game info
This commit is contained in:
parent
fcc7403e54
commit
c065d513e7
|
@ -21,7 +21,8 @@ final class AppApi(
|
|||
gameSocket: game.Socket,
|
||||
messenger: Messenger,
|
||||
starter: Starter,
|
||||
eloUpdater: EloUpdater) {
|
||||
eloUpdater: EloUpdater,
|
||||
gameInfo: DbGame ⇒ IO[GameInfo]) {
|
||||
|
||||
private implicit val timeout = Timeout(300 millis)
|
||||
private implicit val executor = Akka.system.dispatcher
|
||||
|
@ -131,6 +132,11 @@ final class AppApi(
|
|||
|
||||
def gameVersion(gameId: String): Future[Int] = futureVersion(gameId)
|
||||
|
||||
def gameInfo(gameId: String): IO[Option[GameInfo]] = for {
|
||||
gameOption ← gameRepo game gameId
|
||||
gameInfo ← gameOption.fold(gameInfo(_) map some, io(none))
|
||||
} yield gameInfo
|
||||
|
||||
def isConnected(gameId: String, colorName: String): Future[Boolean] =
|
||||
Color(colorName).fold(
|
||||
c ⇒ gameSocket.hubMaster ? IsConnectedOnGame(gameId, c) mapTo manifest[Boolean],
|
||||
|
|
37
app/GameInfo.scala
Normal file
37
app/GameInfo.scala
Normal file
|
@ -0,0 +1,37 @@
|
|||
package lila
|
||||
|
||||
import model.DbGame
|
||||
import chess.Eco
|
||||
import chess.format.Forsyth
|
||||
|
||||
import scalaz.effects.IO
|
||||
|
||||
final class GameInfo private (
|
||||
val game: DbGame,
|
||||
val pgn: String,
|
||||
val fen: String,
|
||||
val opening: Option[Eco.Opening]) {
|
||||
|
||||
def toMap = Map(
|
||||
"pgn" -> pgn,
|
||||
"fen" -> fen,
|
||||
"opening" -> (opening map { o ⇒
|
||||
Map(
|
||||
"code" -> o.code,
|
||||
"name" -> o.name
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
object GameInfo {
|
||||
|
||||
def apply(pgnDump: PgnDump)(game: DbGame): IO[GameInfo] =
|
||||
pgnDump >> game map { pgn ⇒
|
||||
new GameInfo(
|
||||
game = game,
|
||||
pgn = pgn,
|
||||
fen = Forsyth >> game.toChess,
|
||||
opening = Eco openingOf game.pgn)
|
||||
}
|
||||
}
|
60
app/PgnDump.scala
Normal file
60
app/PgnDump.scala
Normal file
|
@ -0,0 +1,60 @@
|
|||
package lila
|
||||
|
||||
import chess.format.Forsyth
|
||||
import db.{ GameRepo, UserRepo }
|
||||
import model.{ DbGame, DbPlayer, User }
|
||||
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
import scalaz.effects._
|
||||
|
||||
final class PgnDump(gameRepo: GameRepo, userRepo: UserRepo) {
|
||||
|
||||
val dateFormat = DateTimeFormat forPattern "yyyy-MM-dd";
|
||||
|
||||
def >>(game: DbGame): IO[String] =
|
||||
header(game) map { headers ⇒
|
||||
"%s\n\n%s %s".format(headers, moves(game), result(game))
|
||||
}
|
||||
|
||||
def header(game: DbGame): IO[String] = for {
|
||||
whiteUser ← user(game.whitePlayer)
|
||||
blackUser ← user(game.blackPlayer)
|
||||
initialFen ← game.variant.standard.fold(io(none), gameRepo initialFen game.id)
|
||||
} yield List(
|
||||
"Event" -> game.rated.fold("Rated game", "Casual game"),
|
||||
"Site" -> ("http://lichess.org/" + game.id),
|
||||
"Date" -> game.createdAt.fold(dateFormat.print, "?"),
|
||||
"White" -> player(game.whitePlayer, whiteUser),
|
||||
"Black" -> player(game.blackPlayer, blackUser),
|
||||
"WhiteElo" -> elo(game.whitePlayer),
|
||||
"BlackElo" -> elo(game.blackPlayer),
|
||||
"Result" -> result(game),
|
||||
"PlayCount" -> game.turns,
|
||||
"Variant" -> game.variant.name
|
||||
) ++ game.variant.standard.fold(Map.empty, Map(
|
||||
"FEN" -> (initialFen | "?"),
|
||||
"SetUp" -> "1"
|
||||
)) map {
|
||||
case (name, value) ⇒ """[%s "%s"]""".format(name, value)
|
||||
} mkString "\n"
|
||||
|
||||
def elo(p: DbPlayer) = p.elo.fold(_.toString, "?")
|
||||
|
||||
def user(p: DbPlayer): IO[Option[User]] = p.userId.fold(
|
||||
userRepo.user,
|
||||
io(none))
|
||||
|
||||
def player(p: DbPlayer, u: Option[User]) = p.isAi.fold(
|
||||
"Crafty level " + p.aiLevel,
|
||||
u.fold(_.username, "Anonymous"))
|
||||
|
||||
def moves(game: DbGame) = (game.pgnList grouped 2).zipWithIndex map {
|
||||
case (moves, turn) ⇒ "%d. %s".format((turn + 1), moves.mkString(" "))
|
||||
} mkString " "
|
||||
|
||||
def result(game: DbGame) = game.finished.fold(
|
||||
game.winnerColor.fold(
|
||||
color ⇒ color.white.fold("1-0", "0-1"),
|
||||
"1/2-1/2"),
|
||||
"*")
|
||||
}
|
|
@ -18,6 +18,12 @@ final class SystemEnv(application: Application) {
|
|||
implicit val app = application
|
||||
val config = app.configuration.underlying
|
||||
|
||||
lazy val pgnDump = new PgnDump(
|
||||
userRepo = userRepo,
|
||||
gameRepo = gameRepo)
|
||||
|
||||
lazy val gameInfo = GameInfo(pgnDump) _
|
||||
|
||||
lazy val reporting = Akka.system.actorOf(
|
||||
Props(new report.Reporting), name = "reporting")
|
||||
|
||||
|
@ -82,7 +88,8 @@ final class SystemEnv(application: Application) {
|
|||
gameSocket = gameSocket,
|
||||
messenger = messenger,
|
||||
starter = starter,
|
||||
eloUpdater = eloUpdater)
|
||||
eloUpdater = eloUpdater,
|
||||
gameInfo = gameInfo)
|
||||
|
||||
lazy val lobbyApi = new lobby.Api(
|
||||
hookRepo = hookRepo,
|
||||
|
|
|
@ -46,6 +46,13 @@ object AppApiC extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def gameInfo(gameId: String) = Action {
|
||||
(api gameInfo gameId).unsafePerformIO.fold(
|
||||
info ⇒ JsonOk(info.toMap),
|
||||
BadRequest("No such game")
|
||||
)
|
||||
}
|
||||
|
||||
def rematchAccept(gameId: String, color: String, newGameId: String) = Action { implicit request ⇒
|
||||
FormValidIOk[RematchData](rematchForm)(r ⇒
|
||||
api.rematchAccept(gameId, newGameId, color, r._1, r._2, r._3, r._4))
|
||||
|
@ -59,7 +66,7 @@ object AppApiC extends LilaController {
|
|||
env.captcha.create.unsafePerformIO.fold(
|
||||
err ⇒ BadRequest(err.shows),
|
||||
data ⇒ JsonOk(Map(
|
||||
"id" -> data._1,
|
||||
"id" -> data._1,
|
||||
"fen" -> data._2,
|
||||
"color" -> data._3.toString
|
||||
))
|
||||
|
|
|
@ -107,6 +107,10 @@ class GameRepo(collection: MongoCollection)
|
|||
)
|
||||
}
|
||||
|
||||
def initialFen(gameId: String): IO[Option[String]] = io {
|
||||
primitiveProjection[String](DBObject("_id" -> gameId), "initialFen")
|
||||
}
|
||||
|
||||
def cleanupUnplayed: IO[Unit] = io {
|
||||
remove(("turns" $lt 2) ++ ("createdAt" $lt (DateTime.now - 2.day)))
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ package model
|
|||
import chess._
|
||||
import Pos.{ posAt, piotr }
|
||||
import Role.forsyth
|
||||
import org.joda.time.DateTime
|
||||
|
||||
case class DbGame(
|
||||
id: String,
|
||||
|
@ -20,7 +21,8 @@ case class DbGame(
|
|||
castles: String = "KQkq",
|
||||
isRated: Boolean = false,
|
||||
variant: Variant = Standard,
|
||||
lastMoveTime: Option[Int] = None) {
|
||||
lastMoveTime: Option[Int] = None,
|
||||
createdAt: Option[DateTime] = None) {
|
||||
|
||||
val players = List(whitePlayer, blackPlayer)
|
||||
|
||||
|
@ -215,6 +217,8 @@ case class DbGame(
|
|||
def creator = player(creatorColor)
|
||||
|
||||
def invited = player(!creatorColor)
|
||||
|
||||
def pgnList = pgn.split(' ').toList
|
||||
}
|
||||
|
||||
object DbGame {
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.novus.salat.annotations._
|
|||
import chess._
|
||||
import Pos.{ posAt, piotr }
|
||||
import Role.forsyth
|
||||
import org.joda.time.DateTime
|
||||
|
||||
case class RawDbGame(
|
||||
@Key("_id") id: String,
|
||||
|
@ -21,7 +22,8 @@ case class RawDbGame(
|
|||
castles: String = "KQkq",
|
||||
isRated: Boolean = false,
|
||||
v: Int = 1,
|
||||
lmt: Option[Int] = None) {
|
||||
lmt: Option[Int] = None,
|
||||
createdAt: Option[DateTime]) {
|
||||
|
||||
def decode: Option[DbGame] = for {
|
||||
whitePlayer ← players find (_.c == "white") flatMap (_.decode)
|
||||
|
@ -46,7 +48,8 @@ case class RawDbGame(
|
|||
castles = castles,
|
||||
isRated = isRated,
|
||||
variant = trueVariant,
|
||||
lastMoveTime = lmt
|
||||
lastMoveTime = lmt,
|
||||
createdAt = createdAt
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -68,7 +71,8 @@ object RawDbGame {
|
|||
castles = castles,
|
||||
isRated = isRated,
|
||||
v = variant.id,
|
||||
lmt = lastMoveTime
|
||||
lmt = lastMoveTime,
|
||||
createdAt = createdAt
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ package model
|
|||
sealed abstract class Variant(val id: Int) {
|
||||
|
||||
lazy val name = toString.toLowerCase
|
||||
|
||||
def standard = this == Standard
|
||||
}
|
||||
|
||||
case object Standard extends Variant(1)
|
||||
|
|
|
@ -20,6 +20,9 @@ sealed trait Color {
|
|||
def queen = this - Queen
|
||||
def king = this - King
|
||||
|
||||
def white = this == White
|
||||
def black = this == Black
|
||||
|
||||
override def toString = name
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
28
chess/src/main/scala/GameInfo.scala
Normal file
28
chess/src/main/scala/GameInfo.scala
Normal file
|
@ -0,0 +1,28 @@
|
|||
package lila.chess
|
||||
|
||||
import format.PgnDump
|
||||
import Eco.Opening
|
||||
|
||||
final class GameInfo private (
|
||||
val game: Game,
|
||||
val pgn: String,
|
||||
val opening: Option[Opening]) {
|
||||
|
||||
def toMap = Map(
|
||||
"pgn" -> pgn,
|
||||
"opening" -> (opening map { o ⇒
|
||||
Map(
|
||||
"code" -> o.code,
|
||||
"name" -> o.name
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
object GameInfo {
|
||||
|
||||
def apply(game: Game) = new GameInfo(
|
||||
game = game,
|
||||
pgn = game.pgnMoves,
|
||||
opening = Eco openingOf game.pgnMoves)
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
package lila.chess
|
||||
|
||||
|
||||
class EcoTest extends ChessTest {
|
||||
|
||||
"Complete game" in {
|
||||
val game = "d4 Nf6 e4 Nxe4 f3 Nd6 g3"
|
||||
Eco nameOf game must beSome("Queen's Pawn Game")
|
||||
Eco openingOf game must beSome.like {
|
||||
case Eco.Opening(code, name) ⇒ name must_== "Queen's Pawn Game"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ POST /api/rematch-accept/:gameId/:color/:newGameId lila.controllers.AppApiC.re
|
|||
POST /api/adjust/:username lila.controllers.AppApiC.adjust(username: String)
|
||||
GET /api/activity/:gameId/:color lila.controllers.AppApiC.activity(gameId: String, color: String)
|
||||
GET /api/game-version/:gameId lila.controllers.AppApiC.gameVersion(gameId: String)
|
||||
GET /api/game-info/:gameId lila.controllers.AppApiC.gameInfo(gameId: String)
|
||||
|
||||
GET /api/captcha/create lila.controllers.CaptchaC.create
|
||||
GET /api/captcha/solve/:gameId lila.controllers.CaptchaC.solve(gameId: String)
|
||||
|
|
Loading…
Reference in a new issue