Skip to content

Commit fac736a

Browse files
committed
cache division better, fetch initialFen less often
1 parent 6836802 commit fac736a

File tree

11 files changed

+99
-107
lines changed

11 files changed

+99
-107
lines changed

app/controllers/Analyse.scala

+20-18
Original file line numberDiff line numberDiff line change
@@ -52,23 +52,25 @@ object Analyse extends LilaController {
5252
}
5353

5454
def replay(pov: Pov)(implicit ctx: Context) =
55-
Env.game.pgnDump(pov.game) zip
55+
GameRepo initialFen pov.game.id flatMap { initialFen =>
5656
(env.analyser get pov.game.id) zip
57-
(pov.game.tournamentId ?? lila.tournament.TournamentRepo.byId) zip
58-
lila.game.Divider(pov.game) zip
59-
Env.game.crosstableApi(pov.game) flatMap {
60-
case ((((pgn, analysis), tour), division), crosstable) =>
61-
Env.api.roundApi.watcher(pov, Env.api.version, tv = none, analysis.map(pgn -> _)) map { data =>
62-
Ok(html.analyse.replay(
63-
pov,
64-
data,
65-
Env.analyse.annotator(pgn, analysis, pov.game.opening, pov.game.winnerColor, pov.game.status, pov.game.clock).toString,
66-
analysis,
67-
analysis filter (_.done) map { a => AdvantageChart(a.infoAdvices, pov.game.pgnMoves) },
68-
tour,
69-
new TimeChart(pov.game, pov.game.pgnMoves),
70-
crosstable,
71-
division))
72-
}
73-
}
57+
(pov.game.tournamentId ?? lila.tournament.TournamentRepo.byId) zip
58+
Env.game.crosstableApi(pov.game) flatMap {
59+
case ((analysis, tour), crosstable) =>
60+
val division = Env.game.cached.Divider(pov.game, initialFen)
61+
val pgn = Env.game.pgnDump(pov.game, initialFen)
62+
Env.api.roundApi.watcher(pov, Env.api.version, tv = none, analysis.map(pgn -> _), initialFen = initialFen.some) map { data =>
63+
Ok(html.analyse.replay(
64+
pov,
65+
data,
66+
Env.analyse.annotator(pgn, analysis, pov.game.opening, pov.game.winnerColor, pov.game.status, pov.game.clock).toString,
67+
analysis,
68+
analysis filter (_.done) map { a => AdvantageChart(a.infoAdvices, pov.game.pgnMoves) },
69+
tour,
70+
new TimeChart(pov.game, pov.game.pgnMoves),
71+
crosstable,
72+
division))
73+
}
74+
}
75+
}
7476
}

app/controllers/Export.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ object Export extends LilaController {
1717
(game.pgnImport.ifTrue(~get("as") == "imported") match {
1818
case Some(i) => fuccess(i.pgn)
1919
case None => for {
20-
pgn Env.game.pgnDump(game)
20+
initialFen <- GameRepo initialFen game
21+
pgn = Env.game.pgnDump(game, initialFen)
2122
analysis (~get("as") != "raw") ?? (Env.analyse.analyser getDone game.id)
22-
} yield Env.analyse.annotator(pgn, analysis, game.opening, game.winnerColor, game.status, game.clock).toString
23+
} yield Env.analyse.annotator(pgn, analysis, gameOpening(game), game.winnerColor, game.status, game.clock).toString
2324
}) map { content =>
2425
Ok(content).withHeaders(
2526
CONTENT_TYPE -> ContentTypes.TEXT,

app/views/analyse/replay.scala.html

+5-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@(pov: Pov, data: play.api.libs.json.JsObject, pgn: String, analysis: Option[lila.analyse.Analysis], advantageChart: Option[String], tour: Option[lila.tournament.Tournament], timeChart: lila.analyse.TimeChart, cross: Option[lila.game.Crosstable], division: lila.game.Divider.Division)(implicit ctx: Context)
1+
@(pov: Pov, data: play.api.libs.json.JsObject, pgn: String, analysis: Option[lila.analyse.Analysis], advantageChart: Option[String], tour: Option[lila.tournament.Tournament], timeChart: lila.analyse.TimeChart, cross: Option[lila.game.Crosstable], division: chess.Division)(implicit ctx: Context)
22

33
@import pov._
44

@@ -80,13 +80,9 @@
8080
@analysis.map { a =>
8181
@advantageChart.map { chart =>
8282
<div
83-
@division match {
84-
case (mid, end) => {
85-
data-division-mid="@mid.getOrElse("null")"
86-
data-division-end="@end.getOrElse("null")"
87-
}
88-
}
8983
id="adv_chart"
84+
data-division-mid="@division.mid.getOrElse("null")"
85+
data-division-end="@division.end.getOrElse("null")"
9086
data-title="Advantage (up: white, down: black)"
9187
data-max="@lila.analyse.AdvantageChart.max"
9288
data-rows="@chart"></div>
@@ -126,13 +122,9 @@
126122
</div>
127123
<div class="panel move_times">
128124
<div
129-
@division match {
130-
case (mid, end) => {
131-
data-division-mid="@mid.getOrElse("null")"
132-
data-division-end="@end.getOrElse("null")"
133-
}
134-
}
135125
id="movetimes_chart"
126+
data-division-mid="@division.mid.getOrElse("null")"
127+
data-division-end="@division.end.getOrElse("null")"
136128
data-series="@timeChart.series"
137129
data-max="@timeChart.maxTime"></div>
138130
</div>

modules/api/src/main/GameApi.scala

+16-17
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,19 @@ private[api] final class GameApi(
6666
withFens: Boolean,
6767
token: Option[String])(games: List[Game]): Fu[List[JsObject]] =
6868
AnalysisRepo doneByIds games.map(_.id) flatMap { analysisOptions =>
69-
(games map { g => withAnalysis ?? (pgnDump(g) map (_.some)) }).sequenceFu flatMap { pgns =>
70-
(games map GameRepo.initialFen).sequenceFu map { initialFens =>
71-
val validToken = check(token)
72-
games zip analysisOptions zip pgns zip initialFens map {
73-
case (((g, analysisOption), pgnOption), initialFenOption) =>
74-
gameToJson(g, makeUrl(g), analysisOption, pgnOption, initialFenOption,
75-
withAnalysis = withAnalysis,
76-
withMoves = withMoves,
77-
withOpening = withOpening,
78-
withFens = withFens,
79-
withBlurs = validToken,
80-
withHold = validToken,
81-
withMoveTimes = validToken)
82-
}
69+
(games map GameRepo.initialFen).sequenceFu map { initialFens =>
70+
val validToken = check(token)
71+
games zip analysisOptions zip initialFens map {
72+
case ((g, analysisOption), initialFen) =>
73+
val pgnOption = withAnalysis option pgnDump(g, initialFen)
74+
gameToJson(g, makeUrl(g), analysisOption, pgnOption, initialFen,
75+
withAnalysis = withAnalysis,
76+
withMoves = withMoves,
77+
withOpening = withOpening,
78+
withFens = withFens,
79+
withBlurs = validToken,
80+
withHold = validToken,
81+
withMoveTimes = validToken)
8382
}
8483
}
8584
}
@@ -91,7 +90,7 @@ private[api] final class GameApi(
9190
url: String,
9291
analysisOption: Option[Analysis],
9392
pgnOption: Option[Pgn],
94-
initialFenOption: Option[String],
93+
initialFen: Option[String],
9594
withAnalysis: Boolean,
9695
withMoves: Boolean,
9796
withOpening: Boolean,
@@ -100,7 +99,7 @@ private[api] final class GameApi(
10099
withHold: Boolean = false,
101100
withMoveTimes: Boolean = false) = Json.obj(
102101
"id" -> g.id,
103-
"initialFen" -> initialFenOption,
102+
"initialFen" -> initialFen,
104103
"rated" -> g.rated,
105104
"variant" -> g.variant.key,
106105
"speed" -> g.speed.key,
@@ -143,7 +142,7 @@ private[api] final class GameApi(
143142
}
144143
},
145144
"fens" -> withFens ?? {
146-
chess.Replay(g.pgnMoves mkString " ", initialFenOption, g.variant).toOption map { replay =>
145+
chess.Replay(g.pgnMoves mkString " ", initialFen, g.variant).toOption map { replay =>
147146
JsArray(replay.chronoMoves map { move =>
148147
chess.format.Forsyth exportBoard move.after
149148
} map JsString.apply)

modules/api/src/main/RoundApi.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ private[api] final class RoundApi(
2626
}
2727
}
2828

29-
def watcher(pov: Pov, apiVersion: Int, tv: Option[Boolean], analysis: Option[(Pgn, Analysis)] = None)(implicit ctx: Context): Fu[JsObject] =
29+
def watcher(pov: Pov, apiVersion: Int, tv: Option[Boolean],
30+
analysis: Option[(Pgn, Analysis)] = None,
31+
initialFen: Option[Option[String]] = None)(implicit ctx: Context): Fu[JsObject] =
3032
jsonView.watcherJson(pov, ctx.pref, apiVersion, ctx.me, tv,
31-
withBlurs = ctx.me ?? Granter(_.ViewBlurs)) zip
33+
withBlurs = ctx.me ?? Granter(_.ViewBlurs), initialFen = initialFen) zip
3234
(pov.game.tournamentId ?? TournamentRepo.byId) map {
3335
case (json, tourOption) => blindMode {
3436
withTournament(tourOption) {

modules/chess

Submodule chess updated from d679b7b to f2a454c

modules/game/src/main/Cached.scala

+19-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import org.joda.time.DateTime
66
import play.api.libs.json.JsObject
77

88
import lila.db.api.$count
9-
import lila.memo.{ AsyncCache, ExpireSetMemo }
9+
import lila.memo.{ AsyncCache, ExpireSetMemo, Builder }
1010
import tube.gameTube
1111

1212
final class Cached(ttl: Duration) {
@@ -28,4 +28,22 @@ final class Cached(ttl: Duration) {
2828
timeToLive = 6 hours)
2929

3030
private val count = AsyncCache((o: JsObject) => $count(o), timeToLive = ttl)
31+
32+
object Divider {
33+
34+
private val cache = Builder.size[String, chess.Division](5000)
35+
private val empty = chess.Division(none[Int], none[Int])
36+
37+
def apply(game: Game, initialFen: Option[String]): chess.Division = {
38+
Option(cache getIfPresent game.id) | {
39+
val div = chess.Replay(
40+
pgn = game.pgnMoves mkString " ",
41+
initialFen = initialFen,
42+
variant = game.variant
43+
).toOption.fold(empty)(chess.Divider.apply)
44+
cache.put(game.id, div)
45+
div
46+
}
47+
}
48+
}
3149
}

modules/game/src/main/Divider.scala

-22
This file was deleted.

modules/game/src/main/PgnDump.scala

+25-28
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ final class PgnDump(
1818

1919
import PgnDump._
2020

21-
def apply(game: Game): Fu[Pgn] =
22-
tags(game) map { ts =>
23-
val fenSituation = ts find (_.name == Tag.FEN) flatMap { case Tag(_, fen) => Forsyth <<< fen }
24-
val moves2 = (~fenSituation.map(_.situation.color.black)).fold(".." :: game.pgnMoves, game.pgnMoves)
25-
Pgn(ts, turns(moves2, fenSituation.map(_.fullMoveNumber) | 1))
26-
}
21+
def apply(game: Game, initialFen: Option[String]): Pgn = {
22+
val ts = tags(game, initialFen)
23+
val fenSituation = ts find (_.name == Tag.FEN) flatMap { case Tag(_, fen) => Forsyth <<< fen }
24+
val moves2 = (~fenSituation.map(_.situation.color.black)).fold(".." :: game.pgnMoves, game.pgnMoves)
25+
Pgn(ts, turns(moves2, fenSituation.map(_.fullMoveNumber) | 1))
26+
}
2727

2828
def filename(game: Game): String = gameLightUsers(game) match {
2929
case (wu, bu) => "lichess_pgn_%s_%s_vs_%s.%s.pgn".format(
@@ -47,28 +47,25 @@ final class PgnDump(
4747

4848
private val customStartPosition: Set[Variant] = Set(Variant.Chess960, Variant.FromPosition)
4949

50-
private def tags(game: Game): Fu[List[Tag]] = gameLightUsers(game) match {
51-
case (wu, bu) =>
52-
(game.variant.standard.fold(fuccess(none), GameRepo initialFen game.id)) map { initialFen =>
53-
List(
54-
Tag(_.Event, game.rated.fold("Rated game", "Casual game")),
55-
Tag(_.Site, gameUrl(game.id)),
56-
Tag(_.Date, dateFormat.print(game.createdAt)),
57-
Tag(_.White, player(game.whitePlayer, wu)),
58-
Tag(_.Black, player(game.blackPlayer, bu)),
59-
Tag(_.Result, result(game)),
60-
Tag("WhiteElo", rating(game.whitePlayer)),
61-
Tag("BlackElo", rating(game.blackPlayer)),
62-
Tag("PlyCount", game.turns),
63-
Tag(_.Variant, game.variant.name.capitalize),
64-
Tag(_.TimeControl, game.clock.fold("-") { c => s"${c.limit}+${c.increment}" }),
65-
Tag(_.ECO, game.opening.fold("?")(_.code)),
66-
Tag(_.Opening, game.opening.fold("?")(_.name))
67-
) ::: customStartPosition(game.variant).??(List(
68-
Tag(_.FEN, initialFen | "?"),
69-
Tag("SetUp", "1")
70-
))
71-
}
50+
private def tags(game: Game, initialFen: Option[String]): List[Tag] = gameLightUsers(game) match {
51+
case (wu, bu) => List(
52+
Tag(_.Event, game.rated.fold("Rated game", "Casual game")),
53+
Tag(_.Site, gameUrl(game.id)),
54+
Tag(_.Date, dateFormat.print(game.createdAt)),
55+
Tag(_.White, player(game.whitePlayer, wu)),
56+
Tag(_.Black, player(game.blackPlayer, bu)),
57+
Tag(_.Result, result(game)),
58+
Tag("WhiteElo", rating(game.whitePlayer)),
59+
Tag("BlackElo", rating(game.blackPlayer)),
60+
Tag("PlyCount", game.turns),
61+
Tag(_.Variant, game.variant.name.capitalize),
62+
Tag(_.TimeControl, game.clock.fold("-") { c => s"${c.limit}+${c.increment}" }),
63+
Tag(_.ECO, game.opening.fold("?")(_.code)),
64+
Tag(_.Opening, game.opening.fold("?")(_.name))
65+
) ::: customStartPosition(game.variant).??(List(
66+
Tag(_.FEN, initialFen | "?"),
67+
Tag("SetUp", "1")
68+
))
7269
}
7370

7471
private def turns(moves: List[String], from: Int): List[chessPgn.Turn] =

modules/game/src/main/PgnExport.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ private[game] final class PgnExport(pgnDump: lila.game.PgnDump) {
1111
def apply(userId: String): Enumerator[String] = {
1212
val query = pimpQB($query(Query user userId)) sort Query.sortCreated
1313
val toPgn = Enumeratee.mapM[Game].apply[String] { game =>
14-
pgnDump(game) map (_.toString + "\n\n\n")
14+
GameRepo initialFen game map { initialFen =>
15+
pgnDump(game, initialFen).toString + "\n\n\n"
16+
}
1517
}
1618
query.cursor[Game].enumerate() &> toPgn
1719
}

modules/round/src/main/JsonView.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,9 @@ final class JsonView(
129129
apiVersion: Int,
130130
user: Option[User],
131131
tv: Option[Boolean],
132-
withBlurs: Boolean) =
133-
GameRepo.initialFen(pov.game) zip
132+
withBlurs: Boolean,
133+
initialFen: Option[Option[String]] = None) =
134+
initialFen.fold(GameRepo initialFen pov.game)(fuccess) zip
134135
getSocketStatus(pov.game.id) zip
135136
getWatcherChat(pov.game, user) zip
136137
UserRepo.pair(pov.player.userId, pov.opponent.userId) map {

0 commit comments

Comments
 (0)