load analysis move destinations on demand
This commit is contained in:
parent
ee686d74dc
commit
1893d47fc3
|
@ -64,8 +64,7 @@ object Analyse extends LilaController {
|
|||
tv = none,
|
||||
analysis.map(pgn -> _),
|
||||
initialFenO = initialFen.some,
|
||||
withMoveTimes = true,
|
||||
withPossibleMoves = true) map { data =>
|
||||
withMoveTimes = true) map { data =>
|
||||
Ok(html.analyse.replay(
|
||||
pov,
|
||||
data,
|
||||
|
|
|
@ -33,7 +33,7 @@ private[api] final class RoundApi(
|
|||
blindMode _ compose
|
||||
withTournament(pov, tourOption)_ compose
|
||||
withSimul(pov, simulOption)_ compose
|
||||
withSteps(pov, none, initialFen, false)_ compose
|
||||
withSteps(pov, none, initialFen)_ compose
|
||||
withNote(note)_
|
||||
)(json)
|
||||
}
|
||||
|
@ -42,8 +42,7 @@ private[api] final class RoundApi(
|
|||
def watcher(pov: Pov, apiVersion: Int, tv: Option[Boolean],
|
||||
analysis: Option[(Pgn, Analysis)] = None,
|
||||
initialFenO: Option[Option[String]] = None,
|
||||
withMoveTimes: Boolean = false,
|
||||
withPossibleMoves: Boolean = false)(implicit ctx: Context): Fu[JsObject] =
|
||||
withMoveTimes: Boolean = false)(implicit ctx: Context): Fu[JsObject] =
|
||||
initialFenO.fold(GameRepo initialFen pov.game)(fuccess) flatMap { initialFen =>
|
||||
jsonView.watcherJson(pov, ctx.pref, apiVersion, ctx.me, tv,
|
||||
withBlurs = ctx.me ?? Granter(_.ViewBlurs),
|
||||
|
@ -57,17 +56,17 @@ private[api] final class RoundApi(
|
|||
withTournament(pov, tourOption)_ compose
|
||||
withSimul(pov, simulOption)_ compose
|
||||
withNote(note)_ compose
|
||||
withSteps(pov, analysis, initialFen, withPossibleMoves)_ compose
|
||||
withSteps(pov, analysis, initialFen)_ compose
|
||||
withAnalysis(analysis)_
|
||||
)(json)
|
||||
}
|
||||
}
|
||||
|
||||
def userAnalysisJson(pov: Pov, pref: Pref, initialFen: Option[String]) =
|
||||
jsonView.userAnalysisJson(pov, pref) map withSteps(pov, none, initialFen, true)_
|
||||
jsonView.userAnalysisJson(pov, pref) map withSteps(pov, none, initialFen)_
|
||||
|
||||
private def withSteps(pov: Pov, a: Option[(Pgn, Analysis)], initialFen: Option[String], possibleMoves: Boolean)(obj: JsObject) =
|
||||
obj + ("steps" -> lila.round.StepBuilder(pov.game.id, pov.game.pgnMoves, pov.game.variant, a, initialFen, possibleMoves))
|
||||
private def withSteps(pov: Pov, a: Option[(Pgn, Analysis)], initialFen: Option[String])(obj: JsObject) =
|
||||
obj + ("steps" -> lila.round.StepBuilder(pov.game.id, pov.game.pgnMoves, pov.game.variant, a, initialFen))
|
||||
|
||||
private def withNote(note: String)(json: JsObject) =
|
||||
if (note.isEmpty) json else json + ("note" -> JsString(note))
|
||||
|
|
|
@ -15,14 +15,12 @@ object StepBuilder {
|
|||
pgnMoves: List[String],
|
||||
variant: Variant,
|
||||
a: Option[(Pgn, Analysis)],
|
||||
initialFen: Option[String],
|
||||
possibleMoves: Boolean): JsArray = {
|
||||
initialFen: Option[String]): JsArray = {
|
||||
chess.Replay.gameWhileValid(pgnMoves, initialFen, variant) match {
|
||||
case (games, error) =>
|
||||
error foreach logChessError(id)
|
||||
val lastPly = games.lastOption.??(_.turns)
|
||||
val steps = games.map { g =>
|
||||
val withDests = possibleMoves && !(lastPly == g.turns && g.situation.end)
|
||||
Step(
|
||||
ply = g.turns,
|
||||
move = for {
|
||||
|
@ -31,13 +29,13 @@ object StepBuilder {
|
|||
} yield Step.Move(pos._1, pos._2, san),
|
||||
fen = Forsyth >> g,
|
||||
check = g.situation.check,
|
||||
dests = withDests ?? g.situation.destinations)
|
||||
dests = None)
|
||||
}
|
||||
JsArray(a.fold[Seq[Step]](steps) {
|
||||
case (pgn, analysis) => applyAnalysisAdvices(
|
||||
id,
|
||||
applyAnalysisEvals(steps.toList, analysis),
|
||||
pgn, analysis, variant, possibleMoves)
|
||||
applyAnalysisEvals(steps, analysis),
|
||||
pgn, analysis, variant)
|
||||
}.map(_.toJson))
|
||||
}
|
||||
}
|
||||
|
@ -57,8 +55,7 @@ object StepBuilder {
|
|||
steps: List[Step],
|
||||
pgn: Pgn,
|
||||
analysis: Analysis,
|
||||
variant: Variant,
|
||||
possibleMoves: Boolean): List[Step] =
|
||||
variant: Variant): List[Step] =
|
||||
analysis.advices.foldLeft(steps) {
|
||||
case (steps, ad) => (for {
|
||||
before <- steps lift (ad.ply - 1)
|
||||
|
@ -67,17 +64,16 @@ object StepBuilder {
|
|||
nag = ad.nag.symbol.some,
|
||||
comments = ad.makeComment(false, true) :: after.comments,
|
||||
variations = if (ad.info.variation.isEmpty) after.variations
|
||||
else makeVariation(gameId, before, ad.info, variant, possibleMoves).toList :: after.variations))
|
||||
else makeVariation(gameId, before, ad.info, variant).toList :: after.variations))
|
||||
) | steps
|
||||
}
|
||||
|
||||
private def makeVariation(gameId: String, fromStep: Step, info: Info, variant: Variant, possibleMoves: Boolean): List[Step] = {
|
||||
private def makeVariation(gameId: String, fromStep: Step, info: Info, variant: Variant): List[Step] = {
|
||||
chess.Replay.gameWhileValid(info.variation take 20, fromStep.fen.some, variant) match {
|
||||
case (games, error) =>
|
||||
error foreach logChessError(gameId)
|
||||
val lastPly = games.lastOption.??(_.turns)
|
||||
games.drop(1).map { g =>
|
||||
val withDests = possibleMoves && !(lastPly == g.turns && g.situation.end)
|
||||
Step(
|
||||
ply = g.turns,
|
||||
move = for {
|
||||
|
@ -87,7 +83,7 @@ object StepBuilder {
|
|||
} yield Step.Move(orig, dest, san),
|
||||
fen = Forsyth >> g,
|
||||
check = g.situation.check,
|
||||
dests = withDests ?? g.situation.destinations)
|
||||
dests = None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ class StepBuilderPerfTest extends Specification {
|
|||
// val iterations = 1
|
||||
|
||||
def runOne(moves: List[String]) =
|
||||
StepBuilder("abcd1234", moves, chess.variant.Standard, None, None, true)
|
||||
StepBuilder("abcd1234", moves, chess.variant.Standard, None, None)
|
||||
def run { gameMoves foreach runOne }
|
||||
|
||||
"playing a game" should {
|
||||
|
|
27
modules/socket/src/main/AnaDests.scala
Normal file
27
modules/socket/src/main/AnaDests.scala
Normal file
|
@ -0,0 +1,27 @@
|
|||
package lila.socket
|
||||
|
||||
import lila.common.PimpedJson._
|
||||
import play.api.libs.json.JsObject
|
||||
|
||||
case class AnaDests(
|
||||
variant: chess.variant.Variant,
|
||||
fen: String,
|
||||
path: String) {
|
||||
|
||||
def dests: String = chess.Game(variant.some, fen.some).situation.destinations map {
|
||||
case (orig, dests) => s"${orig.piotr}${dests.map(_.piotr).mkString}"
|
||||
} mkString " "
|
||||
}
|
||||
|
||||
object AnaDests {
|
||||
|
||||
def parse(o: JsObject) = for {
|
||||
d ← o obj "d"
|
||||
variant = chess.variant.Variant orDefault ~d.str("variant")
|
||||
fen ← d str "fen"
|
||||
path ← d str "path"
|
||||
} yield AnaDests(
|
||||
variant = variant,
|
||||
fen = fen,
|
||||
path = path)
|
||||
}
|
|
@ -20,7 +20,7 @@ case class AnaMove(
|
|||
},
|
||||
fen = chess.format.Forsyth >> game,
|
||||
check = game.situation.check,
|
||||
dests = !game.situation.end ?? game.situation.destinations)
|
||||
dests = Some(!game.situation.end ?? game.situation.destinations))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ object AnaMove {
|
|||
d ← o obj "d"
|
||||
orig ← d str "orig" flatMap chess.Pos.posAt
|
||||
dest ← d str "dest" flatMap chess.Pos.posAt
|
||||
variant ← d str "variant" map chess.variant.Variant.orDefault
|
||||
variant = chess.variant.Variant orDefault ~d.str("variant")
|
||||
fen ← d str "fen"
|
||||
path ← d str "path"
|
||||
prom = d str "promotion" flatMap chess.Role.promotable
|
||||
|
|
|
@ -39,6 +39,16 @@ object Handler {
|
|||
member push lila.socket.Socket.makeMessage("stepFailure", err.toString)
|
||||
}
|
||||
}
|
||||
case ("anaDests", o) =>
|
||||
AnaDests parse o match {
|
||||
case Some(req) =>
|
||||
member push lila.socket.Socket.makeMessage("dests", Json.obj(
|
||||
"dests" -> req.dests,
|
||||
"path" -> req.path
|
||||
))
|
||||
case None =>
|
||||
member push lila.socket.Socket.makeMessage("destsFailure", "Bad dests request")
|
||||
}
|
||||
case _ => // logwarn("Unhandled msg: " + msg)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ case class Step(
|
|||
move: Option[Step.Move],
|
||||
fen: String,
|
||||
check: Boolean,
|
||||
dests: Map[Pos, List[Pos]],
|
||||
// None when not computed yet
|
||||
dests: Option[Map[Pos, List[Pos]]],
|
||||
eval: Option[Int] = None,
|
||||
mate: Option[Int] = None,
|
||||
nag: Option[String] = None,
|
||||
|
@ -36,15 +37,17 @@ object Step {
|
|||
add("mate", mate) _ compose
|
||||
add("nag", nag) _ compose
|
||||
add("comments", comments, comments.nonEmpty) _ compose
|
||||
add("variations", variations, variations.nonEmpty) _
|
||||
add("variations", variations, variations.nonEmpty) _ compose
|
||||
add("dests", dests.map {
|
||||
_.map {
|
||||
case (orig, dests) => s"${orig.piotr}${dests.map(_.piotr).mkString}"
|
||||
}.mkString(" ")
|
||||
})
|
||||
)(Json.obj(
|
||||
"ply" -> ply,
|
||||
"uci" -> move.map(_.uci),
|
||||
"san" -> move.map(_.san),
|
||||
"fen" -> fen,
|
||||
"dests" -> dests.map {
|
||||
case (orig, dests) => s"${orig.piotr}${dests.map(_.piotr).mkString}"
|
||||
}.mkString(" ")))
|
||||
"fen" -> fen))
|
||||
}
|
||||
|
||||
private def add[A](k: String, v: A, cond: Boolean)(o: JsObject)(implicit writes: Writes[A]): JsObject =
|
||||
|
|
|
@ -53,4 +53,21 @@ module.exports = function(steps, analysis) {
|
|||
tree.push(step);
|
||||
return nextPath;
|
||||
}.bind(this);
|
||||
|
||||
this.addDests = function(dests, path) {
|
||||
var tree = this.tree;
|
||||
for (var j in path) {
|
||||
var p = path[j];
|
||||
for (var i = 0, nb = tree.length; i < nb; i++) {
|
||||
if (p.ply === tree[i].ply) {
|
||||
if (p.variation) {
|
||||
tree = tree[i].variations[p.variation - 1];
|
||||
break;
|
||||
}
|
||||
tree[i].dests = dests;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ var autoplay = require('./autoplay');
|
|||
var control = require('./control');
|
||||
var promotion = require('./promotion');
|
||||
var readDests = require('./util').readDests;
|
||||
var debounce = require('./util').debounce;
|
||||
var socket = require('./socket');
|
||||
var m = require('mithril');
|
||||
|
||||
|
@ -63,8 +64,8 @@ module.exports = function(opts) {
|
|||
fen: s.fen,
|
||||
turnColor: color,
|
||||
movable: {
|
||||
color: Object.keys(dests).length === 0 ? null : color,
|
||||
dests: dests
|
||||
color: dests && Object.keys(dests).length > 0 ? color : null,
|
||||
dests: dests || {}
|
||||
},
|
||||
check: s.check,
|
||||
lastMove: s.uci ? [s.uci.substr(0, 2), s.uci.substr(2, 2)] : null,
|
||||
|
@ -75,8 +76,18 @@ module.exports = function(opts) {
|
|||
this.chessground = ground.make(this.data, config, userMove);
|
||||
this.chessground.set(config);
|
||||
if (opts.onChange) opts.onChange(config.fen, this.vm.path);
|
||||
if (!dests) getDests();
|
||||
}.bind(this);
|
||||
|
||||
var getDests = debounce(function() {
|
||||
if (this.vm.step.dests) return;
|
||||
this.socket.sendAnaDests({
|
||||
variant: this.data.game.variant.key,
|
||||
fen: this.vm.step.fen,
|
||||
path: this.vm.pathStr
|
||||
});
|
||||
}.bind(this), 200, false);
|
||||
|
||||
this.jump = function(path) {
|
||||
this.vm.path = path;
|
||||
this.vm.pathStr = treePath.write(path);
|
||||
|
@ -132,6 +143,11 @@ module.exports = function(opts) {
|
|||
this.chessground.playPremove();
|
||||
}.bind(this);
|
||||
|
||||
this.addDests = function(dests, path) {
|
||||
this.analyse.addDests(dests, treePath.read(path));
|
||||
if (path === this.vm.pathStr) showGround();
|
||||
}.bind(this);
|
||||
|
||||
this.reset = function() {
|
||||
this.chessground.set(this.vm.situation);
|
||||
m.redraw();
|
||||
|
|
|
@ -3,6 +3,7 @@ module.exports = function(send, ctrl) {
|
|||
this.send = send;
|
||||
|
||||
var anaMoveTimeout;
|
||||
var anaDestsTimeout;
|
||||
|
||||
var handlers = {
|
||||
step: function(data) {
|
||||
|
@ -13,6 +14,14 @@ module.exports = function(send, ctrl) {
|
|||
console.log(data);
|
||||
clearTimeout(anaMoveTimeout);
|
||||
ctrl.reset();
|
||||
},
|
||||
dests: function(data) {
|
||||
ctrl.addDests(data.dests, data.path);
|
||||
clearTimeout(anaDestsTimeout);
|
||||
},
|
||||
destsFailure: function(data) {
|
||||
console.log(data);
|
||||
clearTimeout(anaDestsTimeout);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -24,8 +33,19 @@ module.exports = function(send, ctrl) {
|
|||
return false;
|
||||
}.bind(this);
|
||||
|
||||
this.sendAnaMove = function(move) {
|
||||
this.send('anaMove', move);
|
||||
anaMoveTimeout = setTimeout(this.sendAnaMove.bind(this, move), 3000);
|
||||
this.sendAnaMove = function(req) {
|
||||
withoutStandardVariant(req);
|
||||
this.send('anaMove', req);
|
||||
anaMoveTimeout = setTimeout(this.sendAnaMove.bind(this, req), 3000);
|
||||
}.bind(this);
|
||||
|
||||
this.sendAnaDests = function(req) {
|
||||
withoutStandardVariant(req);
|
||||
this.send('anaDests', req);
|
||||
anaDestsTimeout = setTimeout(this.sendAnaDests.bind(this, req), 3000);
|
||||
}.bind(this);
|
||||
|
||||
var withoutStandardVariant = function(obj) {
|
||||
if (obj.variant === 'standard') delete obj.variant;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ var piotr2key = require('game').piotr.piotr2key;
|
|||
|
||||
module.exports = {
|
||||
readDests: function(lines) {
|
||||
if (typeof lines === 'undefined') return null;
|
||||
var dests = {};
|
||||
if (lines) lines.split(' ').forEach(function(line) {
|
||||
dests[piotr2key[line[0]]] = line.split('').slice(1).map(function(c) {
|
||||
|
@ -15,5 +16,24 @@ module.exports = {
|
|||
},
|
||||
empty: function(a) {
|
||||
return !a || a.length === 0;
|
||||
},
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds. If `immediate` is passed, trigger the function on the
|
||||
// leading edge, instead of the trailing.
|
||||
debounce: function(func, wait, immediate) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this,
|
||||
args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
var callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue