study server eval variations
parent
b58325d20d
commit
1e366b76a7
|
@ -156,7 +156,7 @@ object BSONHandlers {
|
|||
}
|
||||
}
|
||||
|
||||
private implicit def NodeBSONHandler: BSON[Node] = new BSON[Node] {
|
||||
implicit def NodeBSONHandler: BSON[Node] = new BSON[Node] {
|
||||
def reads(r: Reader) = Node(
|
||||
id = r.get[UciCharPair]("i"),
|
||||
ply = r int "p",
|
||||
|
|
|
@ -113,6 +113,16 @@ final class ChapterRepo(coll: Coll) {
|
|||
s"root.n.${indexes.mkString(".n.")}.$subField"
|
||||
}
|
||||
|
||||
private[study] def setChild(chapter: Chapter, path: Path, child: Node): Funit =
|
||||
pathToField(chapter, path, "n") ?? { parentChildrenPath =>
|
||||
coll.update(
|
||||
$id(chapter.id) ++ $doc(s"$parentChildrenPath.i" -> child.id),
|
||||
$set(s"$parentChildrenPath.$$" -> child)
|
||||
) flatMap { res =>
|
||||
(res.n == 0) ?? coll.update($id(chapter.id), $push(parentChildrenPath -> child)).void
|
||||
}
|
||||
}
|
||||
|
||||
private[study] def idNamesByStudyIds(studyIds: Seq[Study.Id]): Fu[Map[Study.Id, Vector[Chapter.IdName]]] =
|
||||
coll.find(
|
||||
$doc("studyId" $in studyIds),
|
||||
|
|
|
@ -20,6 +20,7 @@ sealed trait RootOrNode {
|
|||
val gamebook: Option[Gamebook]
|
||||
val glyphs: Glyphs
|
||||
val score: Option[Score]
|
||||
def addChild(node: Node): RootOrNode
|
||||
def fullMoveNumber = 1 + ply / 2
|
||||
def mainline: List[Node]
|
||||
def color = chess.Color(ply % 2 == 0)
|
||||
|
@ -50,6 +51,8 @@ case class Node(
|
|||
|
||||
def withoutChildren = copy(children = Node.emptyChildren)
|
||||
|
||||
def addChild(child: Node) = copy(children = children addNode child)
|
||||
|
||||
def withClock(centis: Option[Centis]) = copy(clock = centis)
|
||||
|
||||
def isCommented = comments.value.nonEmpty
|
||||
|
@ -78,7 +81,20 @@ case class Node(
|
|||
score = none
|
||||
)
|
||||
|
||||
override def toString = s"$id:${move.san} $score"
|
||||
def merge(n: Node): Node = copy(
|
||||
shapes = shapes ++ n.shapes,
|
||||
comments = comments ++ n.comments,
|
||||
gamebook = n.gamebook orElse gamebook,
|
||||
glyphs = glyphs merge n.glyphs,
|
||||
score = n.score orElse score,
|
||||
clock = n.clock orElse clock,
|
||||
crazyData = n.crazyData orElse crazyData,
|
||||
children = n.children.nodes.foldLeft(children) {
|
||||
case (cs, c) => children addNode c
|
||||
}
|
||||
)
|
||||
|
||||
override def toString = s"$ply.${move.san} ${children.nodes}"
|
||||
}
|
||||
|
||||
object Node {
|
||||
|
@ -97,11 +113,14 @@ object Node {
|
|||
}
|
||||
|
||||
def addNodeAt(node: Node, path: Path): Option[Children] = path.split match {
|
||||
case None if has(node.id) => this.some
|
||||
case None => Children(nodes :+ node).some
|
||||
case None => addNode(node).some
|
||||
case Some((head, tail)) => updateChildren(head, _.addNodeAt(node, tail))
|
||||
}
|
||||
|
||||
def addNode(node: Node): Children = get(node.id).fold(Children(nodes :+ node)) { prev =>
|
||||
Children(nodes.filterNot(_.id == node.id) :+ prev.merge(node))
|
||||
}
|
||||
|
||||
def deleteNodeAt(path: Path): Option[Children] = path.split match {
|
||||
case None => none
|
||||
case Some((head, Path(Nil))) if has(head) => Children(nodes.filterNot(_.id == head)).some
|
||||
|
@ -203,6 +222,8 @@ object Node {
|
|||
|
||||
def withoutChildren = copy(children = Node.emptyChildren)
|
||||
|
||||
def addChild(child: Node) = copy(children = children addNode child)
|
||||
|
||||
def nodeAt(path: Path): Option[RootOrNode] =
|
||||
if (path.isEmpty) this.some else children nodeAt path
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@ package lila.study
|
|||
|
||||
import play.api.libs.json._
|
||||
|
||||
import lila.analyse.Analysis
|
||||
import chess.format.{ Forsyth, FEN, Uci, UciCharPair }
|
||||
import lila.analyse.{ Analysis, Info }
|
||||
import lila.hub.actorApi.fishnet.StudyChapterRequest
|
||||
import lila.hub.actorApi.map.Tell
|
||||
import lila.socket.Socket.Uid
|
||||
import lila.tree._
|
||||
import lila.tree.Node.Comment
|
||||
import lila.user.User
|
||||
|
@ -44,30 +46,64 @@ object ServerEval {
|
|||
sequencer.sequenceStudyWithChapter(Study.Id(studyId), Chapter.Id(analysis.id)) {
|
||||
case Study.WithChapter(study, chapter) =>
|
||||
(complete ?? chapterRepo.setAnalysed(chapter.id, true.some)) >> {
|
||||
val allInfoAdvices = analysis.infos.headOption.map(_ -> none).toList ::: analysis.infoAdvices
|
||||
lila.common.Future.fold(chapter.root.mainline zip allInfoAdvices)(Path.root) {
|
||||
lila.common.Future.fold(chapter.root.mainline zip analysis.infoAdvices)(Path.root) {
|
||||
case (path, (node, (info, advOpt))) => info.eval.score.ifTrue(node.score.isEmpty).?? { score =>
|
||||
chapterRepo.setScore(chapter, path + node, score.some) >>
|
||||
advOpt.?? { adv =>
|
||||
chapterRepo.setComments(chapter, path, node.comments + Comment(
|
||||
chapterRepo.setComments(chapter, path + node, node.comments + Comment(
|
||||
Comment.Id.make,
|
||||
Comment.Text(adv.makeComment(false, true)),
|
||||
Comment.Author.Lichess
|
||||
))
|
||||
)) >> {
|
||||
chapter.root.nodeAt(path).flatMap { parent =>
|
||||
analysisLine(parent, chapter.setup.variant, info) flatMap { child =>
|
||||
parent.addChild(child).children.get(child.id)
|
||||
}
|
||||
} ?? { chapterRepo.setChild(chapter, path, _) }
|
||||
}
|
||||
}
|
||||
} inject path + node
|
||||
} void
|
||||
} >>- chapterRepo.byId(Chapter.Id(analysis.id)).foreach {
|
||||
_ ?? { chapter =>
|
||||
socketHub ! Tell(studyId, ServerEval.Progress(
|
||||
chapterId = chapter.id,
|
||||
tree = lila.study.TreeBuilder(chapter.root, chapter.setup.variant),
|
||||
analysis = toJson(chapter, analysis)
|
||||
))
|
||||
} >>- {
|
||||
chapterRepo.byId(Chapter.Id(analysis.id)).foreach {
|
||||
_ ?? { chapter =>
|
||||
socketHub ! Tell(studyId, ServerEval.Progress(
|
||||
chapterId = chapter.id,
|
||||
tree = lila.study.TreeBuilder(chapter.root, chapter.setup.variant),
|
||||
analysis = toJson(chapter, analysis)
|
||||
))
|
||||
}
|
||||
}
|
||||
} logFailure logger
|
||||
}
|
||||
}
|
||||
|
||||
private def analysisLine(root: RootOrNode, variant: chess.variant.Variant, info: Info): Option[Node] = {
|
||||
chess.Replay.gameMoveWhileValid(info.variation take 20, root.fen.value, variant) match {
|
||||
case (init, games, error) =>
|
||||
error foreach { logger.info(_) }
|
||||
games.reverse match {
|
||||
case Nil => none
|
||||
case (g, m) :: rest => rest.foldLeft(makeBranch(g, m)) {
|
||||
case (node, (g, m)) => makeBranch(g, m) addChild node
|
||||
} some
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def makeBranch(g: chess.Game, m: Uci.WithSan) = {
|
||||
val fen = FEN(Forsyth >> g)
|
||||
Node(
|
||||
id = UciCharPair(m.uci),
|
||||
ply = g.turns,
|
||||
move = m,
|
||||
fen = fen,
|
||||
check = g.situation.check,
|
||||
crazyData = g.situation.board.crazyData,
|
||||
clock = none,
|
||||
children = Node.emptyChildren
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
case class Progress(chapterId: Chapter.Id, tree: Root, analysis: JsObject)
|
||||
|
|
|
@ -18,7 +18,6 @@ lichess.advantageChart = function(data, trans, el) {
|
|||
|
||||
var color = node.ply & 1, cp;
|
||||
|
||||
console.log(node.eval);
|
||||
if (node.eval && node.eval.mate) {
|
||||
cp = node.eval.mate > 0 ? Infinity : -Infinity;
|
||||
} else if (node.san.indexOf('#') > 0) {
|
||||
|
|
|
@ -29,7 +29,7 @@ export function ctrl(root: AnalyseCtrl, chapterId: () => string): ServerEvalCtrl
|
|||
if (!li.advantageChart || lastPly() === mainlinePly) return;
|
||||
const lp = lastPly(typeof mainlinePly === 'undefined' ? lastPly() : mainlinePly),
|
||||
el = chartEl();
|
||||
if (el) {
|
||||
if (el && window.HighCharts) {
|
||||
const $chart = $(el);
|
||||
if ($chart.length) {
|
||||
const chart = $chart.highcharts();
|
||||
|
|
Loading…
Reference in New Issue