study flat tree WIP
parent
29f506a41e
commit
b45b6b2c4a
|
@ -82,7 +82,7 @@ final private class RelaySync(
|
|||
toMainline = true
|
||||
)(who) >> chapterRepo.setRelayPath(chapter.id, path)
|
||||
} >> newNode.?? { node =>
|
||||
lila.common.Future.fold(node.mainline)(Position(chapter, path).ref) { case (position, n) =>
|
||||
lila.common.Future.fold(node.mainline.toList)(Position(chapter, path).ref) { case (position, n) =>
|
||||
studyApi.addNode(
|
||||
studyId = study.id,
|
||||
position = position,
|
||||
|
|
|
@ -213,22 +213,25 @@ object BSONHandlers {
|
|||
score = r.getO[Score]("e"),
|
||||
clock = r.getO[Centis]("l"),
|
||||
crazyData = r.getO[Crazyhouse.Data]("z"),
|
||||
children = StudyFlatTree.rootChildren(fullReader.doc)
|
||||
children = StudyFlatTree.reader.rootChildren(fullReader.doc)
|
||||
)
|
||||
}
|
||||
def writes(w: Writer, s: Root) = ???
|
||||
// $doc(
|
||||
// "p" -> s.ply,
|
||||
// "f" -> s.fen,
|
||||
// "c" -> w.boolO(s.check),
|
||||
// "h" -> s.shapes.value.nonEmpty.option(s.shapes),
|
||||
// "co" -> s.comments.value.nonEmpty.option(s.comments),
|
||||
// "ga" -> s.gamebook,
|
||||
// "g" -> s.glyphs.nonEmpty,
|
||||
// "e" -> s.score,
|
||||
// "l" -> s.clock,
|
||||
// "z" -> s.crazyData
|
||||
// )
|
||||
def writes(w: Writer, r: Root) = $doc(
|
||||
StudyFlatTree.writer.rootChildren(r) appended {
|
||||
"" -> $doc(
|
||||
"p" -> r.ply,
|
||||
"f" -> r.fen,
|
||||
"c" -> r.check.some.filter(identity),
|
||||
"h" -> r.shapes.value.nonEmpty.option(r.shapes),
|
||||
"co" -> r.comments.value.nonEmpty.option(r.comments),
|
||||
"ga" -> r.gamebook,
|
||||
"g" -> r.glyphs.nonEmpty,
|
||||
"e" -> r.score,
|
||||
"l" -> r.clock,
|
||||
"z" -> r.crazyData
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
implicit val PathBSONHandler = BSONStringHandler.as[Path](Path.apply, _.toString)
|
||||
|
|
|
@ -28,7 +28,7 @@ final class GifExport(
|
|||
chapter.tags(_.Black),
|
||||
chapter.tags(_.BlackElo).map(elo => s"($elo)")
|
||||
).flatten.mkString(" "),
|
||||
"frames" -> framesRec(chapter.root :: chapter.root.mainline, Json.arr())
|
||||
"frames" -> framesRec(chapter.root +: chapter.root.mainline, Json.arr())
|
||||
)
|
||||
)
|
||||
.stream() flatMap {
|
||||
|
@ -39,10 +39,10 @@ final class GifExport(
|
|||
}
|
||||
|
||||
@annotation.tailrec
|
||||
private def framesRec(nodes: List[RootOrNode], arr: JsArray): JsArray =
|
||||
private def framesRec(nodes: Vector[RootOrNode], arr: JsArray): JsArray =
|
||||
nodes match {
|
||||
case Nil => arr
|
||||
case node :: tail =>
|
||||
case node +: tail =>
|
||||
framesRec(
|
||||
tail,
|
||||
arr :+ Json
|
||||
|
|
|
@ -22,7 +22,7 @@ sealed trait RootOrNode {
|
|||
val score: Option[Score]
|
||||
def addChild(node: Node): RootOrNode
|
||||
def fullMoveNumber = 1 + ply / 2
|
||||
def mainline: List[Node]
|
||||
def mainline: Vector[Node]
|
||||
def color = chess.Color.fromPly(ply)
|
||||
def moveOption: Option[Uci.WithSan]
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ case class Node(
|
|||
|
||||
def toggleGlyph(glyph: Glyph) = copy(glyphs = glyphs toggle glyph)
|
||||
|
||||
def mainline: List[Node] = this :: children.first.??(_.mainline)
|
||||
def mainline: Vector[Node] = this +: children.first.??(_.mainline)
|
||||
|
||||
def updateMainlineLast(f: Node => Node): Node =
|
||||
children.first.fold(f(this)) { main =>
|
||||
|
@ -120,14 +120,14 @@ object Node {
|
|||
case (head, tail) => get(head) flatMap (_.children nodeAt tail)
|
||||
}
|
||||
|
||||
def nodesOn(path: Path): List[(Node, Path)] =
|
||||
path.split ?? { case (head, tail) =>
|
||||
get(head) ?? { first =>
|
||||
(first, Path(List(head))) :: first.children.nodesOn(tail).map { case (n, p) =>
|
||||
(n, p prepend head)
|
||||
}
|
||||
}
|
||||
}
|
||||
// def nodesOn(path: Path): List[(Node, Path)] =
|
||||
// path.split ?? { case (head, tail) =>
|
||||
// get(head) ?? { first =>
|
||||
// (first, Path(List(head))) :: first.children.nodesOn(tail).map { case (n, p) =>
|
||||
// (n, p prepend head)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
def addNodeAt(node: Node, path: Path): Option[Children] =
|
||||
path.split match {
|
||||
|
@ -302,7 +302,7 @@ object Node {
|
|||
copy(children = children.update(main updateMainlineLast f))
|
||||
}
|
||||
|
||||
lazy val mainline: List[Node] = children.first.??(_.mainline)
|
||||
lazy val mainline: Vector[Node] = children.first.??(_.mainline)
|
||||
|
||||
def lastMainlinePly = Chapter.Ply(mainline.lastOption.??(_.ply))
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila.study
|
|||
|
||||
import chess.format.UciCharPair
|
||||
|
||||
case class Path(ids: List[UciCharPair]) extends AnyVal {
|
||||
case class Path(ids: Vector[UciCharPair]) extends AnyVal {
|
||||
|
||||
def head: Option[UciCharPair] = ids.headOption
|
||||
|
||||
|
@ -14,10 +14,11 @@ case class Path(ids: List[UciCharPair]) extends AnyVal {
|
|||
|
||||
def isEmpty = ids.isEmpty
|
||||
|
||||
def +(node: Node): Path = Path(ids :+ node.id)
|
||||
def +(more: Path): Path = Path(ids ::: more.ids)
|
||||
def +(id: UciCharPair): Path = Path(ids appended id)
|
||||
def +(node: Node): Path = Path(ids appended node.id)
|
||||
def +(more: Path): Path = Path(ids appendedAll more.ids)
|
||||
|
||||
def prepend(id: UciCharPair) = Path(id :: ids)
|
||||
def prepend(id: UciCharPair) = Path(ids prepended id)
|
||||
|
||||
def intersect(other: Path): Path =
|
||||
Path {
|
||||
|
@ -40,7 +41,7 @@ object Path {
|
|||
UciCharPair(p(0), b)
|
||||
}
|
||||
}
|
||||
.toList
|
||||
.toVector
|
||||
}
|
||||
|
||||
val root = Path("")
|
||||
|
|
|
@ -26,7 +26,7 @@ final class PgnDump(
|
|||
def ofChapter(study: Study, flags: WithFlags)(chapter: Chapter) =
|
||||
Pgn(
|
||||
tags = makeTags(study, chapter),
|
||||
turns = toTurns(chapter.root)(flags),
|
||||
turns = toTurns(chapter.root)(flags).toList,
|
||||
initial = Initial(
|
||||
chapter.root.comments.list.map(_.text.value) ::: shapeComment(chapter.root.shapes).toList
|
||||
)
|
||||
|
@ -105,7 +105,7 @@ object PgnDump {
|
|||
result = none,
|
||||
variations = flags.variations ?? {
|
||||
variations.view.map { child =>
|
||||
toTurns(child.mainline, noVariations)
|
||||
toTurns(child.mainline, noVariations).toList
|
||||
}.toList
|
||||
},
|
||||
secondsLeft = flags.clocks ?? node.clock.map(_.roundSeconds)
|
||||
|
@ -138,34 +138,37 @@ object PgnDump {
|
|||
black = second map { node2move(_, first.children.variations) }
|
||||
)
|
||||
|
||||
def toTurns(root: Node.Root)(implicit flags: WithFlags): List[chessPgn.Turn] =
|
||||
def toTurns(root: Node.Root)(implicit flags: WithFlags): Vector[chessPgn.Turn] =
|
||||
toTurns(root.mainline, root.children.variations)
|
||||
|
||||
def toTurns(line: List[Node], variations: Variations)(implicit flags: WithFlags): List[chessPgn.Turn] = {
|
||||
def toTurns(
|
||||
line: Vector[Node],
|
||||
variations: Variations
|
||||
)(implicit flags: WithFlags): Vector[chessPgn.Turn] = {
|
||||
line match {
|
||||
case Nil => Nil
|
||||
case first :: rest if first.ply % 2 == 0 =>
|
||||
case Vector() => Vector()
|
||||
case first +: rest if first.ply % 2 == 0 =>
|
||||
chessPgn.Turn(
|
||||
number = 1 + (first.ply - 1) / 2,
|
||||
white = none,
|
||||
black = node2move(first, variations).some
|
||||
) :: toTurnsFromWhite(rest, first.children.variations)
|
||||
) +: toTurnsFromWhite(rest, first.children.variations)
|
||||
case l => toTurnsFromWhite(l, variations)
|
||||
}
|
||||
}.filterNot(_.isEmpty)
|
||||
|
||||
def toTurnsFromWhite(line: List[Node], variations: Variations)(implicit
|
||||
def toTurnsFromWhite(line: Vector[Node], variations: Variations)(implicit
|
||||
flags: WithFlags
|
||||
): List[chessPgn.Turn] =
|
||||
): Vector[chessPgn.Turn] =
|
||||
line
|
||||
.grouped(2)
|
||||
.foldLeft(variations -> List.empty[chessPgn.Turn]) { case variations ~ turns ~ pair =>
|
||||
.foldLeft(variations -> Vector.empty[chessPgn.Turn]) { case variations ~ turns ~ pair =>
|
||||
pair.headOption.fold(variations -> turns) { first =>
|
||||
pair
|
||||
.lift(1)
|
||||
.getOrElse(first)
|
||||
.children
|
||||
.variations -> (toTurn(first, pair lift 1, variations) :: turns)
|
||||
.variations -> (toTurn(first, pair lift 1, variations) +: turns)
|
||||
}
|
||||
}
|
||||
._2
|
||||
|
|
|
@ -57,7 +57,7 @@ object ServerEval {
|
|||
case Study.WithChapter(_, chapter) =>
|
||||
(complete ?? chapterRepo.completeServerEval(chapter)) >> {
|
||||
lila.common.Future
|
||||
.fold(chapter.root.mainline zip analysis.infoAdvices)(Path.root) {
|
||||
.fold(chapter.root.mainline.zip(analysis.infoAdvices).toList)(Path.root) {
|
||||
case (path, (node, (info, advOpt))) =>
|
||||
info.eval.score
|
||||
.ifTrue {
|
||||
|
|
|
@ -296,34 +296,35 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def promote(studyId: Study.Id, position: Position.Ref, toMainline: Boolean)(who: Who) =
|
||||
sequenceStudyWithChapter(studyId, position.chapterId) { case Study.WithChapter(study, chapter) =>
|
||||
Contribute(who.u, study) {
|
||||
chapter.updateRoot { root =>
|
||||
root.withChildren { children =>
|
||||
if (toMainline) children.promoteToMainlineAt(position.path)
|
||||
else children.promoteUpAt(position.path).map(_._1)
|
||||
}
|
||||
} match {
|
||||
case Some(newChapter) =>
|
||||
chapterRepo.update(newChapter) >>-
|
||||
sendTo(study.id)(_.promote(position, toMainline, who)) >>
|
||||
newChapter.root.children
|
||||
.nodesOn {
|
||||
newChapter.root.mainlinePath.intersect(position.path)
|
||||
}
|
||||
.collect {
|
||||
case (node, path) if node.forceVariation =>
|
||||
doForceVariation(Study.WithChapter(study, newChapter), path, force = false, who)
|
||||
}
|
||||
.sequenceFu
|
||||
.void
|
||||
case None =>
|
||||
fufail(s"Invalid promoteToMainline $studyId $position") >>-
|
||||
reloadSriBecauseOf(study, who.sri, chapter.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
def promote(studyId: Study.Id, position: Position.Ref, toMainline: Boolean)(who: Who): Funit =
|
||||
??? // TODO
|
||||
// sequenceStudyWithChapter(studyId, position.chapterId) { case Study.WithChapter(study, chapter) =>
|
||||
// Contribute(who.u, study) {
|
||||
// chapter.updateRoot { root =>
|
||||
// root.withChildren { children =>
|
||||
// if (toMainline) children.promoteToMainlineAt(position.path)
|
||||
// else children.promoteUpAt(position.path).map(_._1)
|
||||
// }
|
||||
// } match {
|
||||
// case Some(newChapter) =>
|
||||
// chapterRepo.update(newChapter) >>-
|
||||
// sendTo(study.id)(_.promote(position, toMainline, who)) >>
|
||||
// newChapter.root.children
|
||||
// .nodesOn {
|
||||
// newChapter.root.mainlinePath.intersect(position.path)
|
||||
// }
|
||||
// .collect {
|
||||
// case (node, path) if node.forceVariation =>
|
||||
// doForceVariation(Study.WithChapter(study, newChapter), path, force = false, who)
|
||||
// }
|
||||
// .sequenceFu
|
||||
// .void
|
||||
// case None =>
|
||||
// fufail(s"Invalid promoteToMainline $studyId $position") >>-
|
||||
// reloadSriBecauseOf(study, who.sri, chapter.id)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
def forceVariation(studyId: Study.Id, position: Position.Ref, force: Boolean)(who: Who): Funit =
|
||||
sequenceStudyWithChapter(studyId, position.chapterId) { sc =>
|
||||
|
|
|
@ -17,27 +17,43 @@ private object StudyFlatTree {
|
|||
}.copy(children = children | Node.emptyChildren)
|
||||
}
|
||||
|
||||
def rootChildren(flatTree: Bdoc): Children = traverse {
|
||||
flatTree.elements.toList
|
||||
.collect {
|
||||
case el if el.name.nonEmpty => FlatNode(Path(el.name), el.value.asOpt[Bdoc].get)
|
||||
object reader {
|
||||
|
||||
def rootChildren(flatTree: Bdoc): Children = traverse {
|
||||
flatTree.elements.toList
|
||||
.collect {
|
||||
case el if el.name.nonEmpty => FlatNode(Path(el.name), el.value.asOpt[Bdoc].get)
|
||||
}
|
||||
.sortBy(-_.depth)
|
||||
}
|
||||
|
||||
private def traverse(children: List[FlatNode]): Children =
|
||||
children
|
||||
.foldLeft(Map.empty[Path, Children]) { case (allChildren, flat) =>
|
||||
update(allChildren, flat)
|
||||
}
|
||||
.get(Path.root) | Node.emptyChildren
|
||||
|
||||
// assumes that node has a greater depth than roots (sort beforehand)
|
||||
private def update(roots: Map[Path, Children], flat: FlatNode): Map[Path, Children] = {
|
||||
val node = flat.toNodeWithChildren(roots get flat.path)
|
||||
roots.removed(flat.path).updatedWith(flat.path.init) {
|
||||
case None => Children(Vector(node)).some
|
||||
case Some(siblings) => siblings.addNode(node).some
|
||||
}
|
||||
.sortBy(-_.depth)
|
||||
}
|
||||
}
|
||||
|
||||
private def traverse(children: List[FlatNode]): Children =
|
||||
children
|
||||
.foldLeft(Map.empty[Path, Children]) { case (allChildren, flat) =>
|
||||
update(allChildren, flat)
|
||||
}
|
||||
.get(Path.root) | Node.emptyChildren
|
||||
object writer {
|
||||
|
||||
// assumes that node has a greater depth than roots (sort beforehand)
|
||||
private def update(roots: Map[Path, Children], flat: FlatNode): Map[Path, Children] = {
|
||||
val node = flat.toNodeWithChildren(roots get flat.path)
|
||||
roots.removed(flat.path).updatedWith(flat.path.init) {
|
||||
case None => Children(Vector(node)).some
|
||||
case Some(siblings) => siblings.addNode(node).some
|
||||
def rootChildren(root: Node.Root): Vector[(String, Bdoc)] =
|
||||
root.children.nodes.flatMap { traverse(_, Path.root) }
|
||||
|
||||
def traverse(node: Node, parentPath: Path): Vector[(String, Bdoc)] = {
|
||||
val path = parentPath + node.id
|
||||
node.children.nodes.flatMap {
|
||||
traverse(_, path)
|
||||
} appended (path.toString -> writeNode(node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ object Dependencies {
|
|||
val scalalib = "com.github.ornicar" %% "scalalib" % "7.0.2"
|
||||
val hasher = "com.roundeights" %% "hasher" % "1.2.1"
|
||||
val jodaTime = "joda-time" % "joda-time" % "2.10.9"
|
||||
val chess = "org.lichess" %% "scalachess" % "10.1.7"
|
||||
val chess = "org.lichess" %% "scalachess" % "10.2.0"
|
||||
val compression = "org.lichess" %% "compression" % "1.6"
|
||||
val maxmind = "com.sanoma.cda" %% "maxmind-geoip2-scala" % "1.3.1-THIB"
|
||||
val prismic = "io.prismic" %% "scala-kit" % "1.2.19-THIB213"
|
||||
|
|
Loading…
Reference in New Issue