Skip to content

Commit

Permalink
move eval implementation to scalachess to share with lila-ws
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Dec 22, 2024
1 parent 0170950 commit 0a7a690
Show file tree
Hide file tree
Showing 25 changed files with 66 additions and 156 deletions.
26 changes: 15 additions & 11 deletions modules/analyse/src/main/AccuracyPercent.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package lila.analyse

import chess.{ ByColor, Color }
import chess.eval.WinPercent
import chess.eval.Eval.{ Cp, Mate }
import scalalib.Maths
import scalalib.model.Percent

import lila.core.data.Percent
import lila.core.game.SideAndStart
import lila.tree.{ Analysis, Eval, WinPercent }
import lila.tree.{ Analysis, Eval }

// Quality of a move, based on previous and next WinPercent
opaque type AccuracyPercent = Double
Expand Down Expand Up @@ -55,15 +57,17 @@ for x in xs:
def fromEvalsAndPov(pov: SideAndStart, evals: List[Eval]): List[AccuracyPercent] =
val subjectiveEvals = pov.color.fold(evals, evals.map(_.invert))
val alignedEvals =
if pov.color == pov.startColor then Eval.initial :: subjectiveEvals else subjectiveEvals
if pov.color == pov.startColor
then lila.tree.evals.initial :: subjectiveEvals
else subjectiveEvals
alignedEvals
.grouped(2)
.collect { case List(e1, e2) =>
for
before <- WinPercent.fromEval(e1)
after <- WinPercent.fromEval(e2)
yield AccuracyPercent.fromWinPercents(before, after)
}
.collect:
case List(e1, e2) =>
for
before <- e1.score.map(WinPercent.fromScore)
after <- e2.score.map(WinPercent.fromScore)
yield AccuracyPercent.fromWinPercents(before, after)
.flatten
.toList

Expand All @@ -74,8 +78,8 @@ for x in xs:
gameAccuracy(startColor, analysis.infos.map(_.eval).flatMap(_.forceAsCp))

// a mean of volatility-weighted mean and harmonic mean
def gameAccuracy(startColor: Color, cps: List[Eval.Cp]): Option[ByColor[AccuracyPercent]] =
val allWinPercents = (Eval.Cp.initial :: cps).map(WinPercent.fromCentiPawns)
def gameAccuracy(startColor: Color, cps: List[Cp]): Option[ByColor[AccuracyPercent]] =
val allWinPercents = (Cp.initial :: cps).map(WinPercent.fromCentiPawns)
val windowSize = (cps.size / 10).atLeast(2).atMost(8)
val allWinPercentValues = WinPercent.raw(allWinPercents)
val windows =
Expand Down
2 changes: 1 addition & 1 deletion modules/analyse/src/main/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package lila.analyse

export lila.core.lilaism.Lilaism.{ *, given }
export lila.common.extensions.*
export lila.tree.{ Advice, Analysis, Info, WinPercent }
export lila.tree.{ Advice, Analysis, Info }
3 changes: 1 addition & 2 deletions modules/analyse/src/test/AccuracyPercentTest.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package lila.analyse

import chess.{ ByColor, Color }
import chess.eval.Eval.Cp
import scalalib.Maths.isCloseTo

import lila.tree.Eval.Cp

class AccuracyPercentTest extends munit.FunSuite:

import AccuracyPercent.*
Expand Down
9 changes: 0 additions & 9 deletions modules/core/src/main/data.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@ object data:

trait OpaqueInstant[A](using A =:= Instant) extends TotalWrapper[A, Instant]

trait Percent[A]:
def value(a: A): Double
def apply(a: Double): A
object Percent:
def of[A](w: TotalWrapper[A, Double]): Percent[A] = new:
def apply(a: Double): A = w(a)
def value(a: A): Double = w.value(a)
def toInt[A](a: A)(using p: Percent[A]): Int = Math.round(p.value(a)).toInt // round to closest

opaque type RichText = String
object RichText extends OpaqueString[RichText]

Expand Down
3 changes: 1 addition & 2 deletions modules/db/src/main/Handlers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ package lila.db
import chess.variant.Variant
import reactivemongo.api.bson.*
import reactivemongo.api.bson.exceptions.TypeDoesNotMatchException

import scala.util.{ Failure, NotGiven, Success, Try }
import scalalib.model.Percent

import lila.common.Iso.{ *, given }
import lila.core.data.Percent
import lila.core.net.IpAddress
import lila.core.game.Blurs

Expand Down
3 changes: 2 additions & 1 deletion modules/evalCache/src/main/BSONHandlers.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package lila.evalCache

import chess.format.{ BinaryFen, Uci }
import chess.eval.*
import reactivemongo.api.bson.*

import scala.util.{ Success, Try }

import lila.db.dsl.{ *, given }
import lila.tree.{ CloudEval, Moves, Pv, Score }
import lila.tree.{ CloudEval, Moves, Pv }

private object BSONHandlers:

Expand Down
2 changes: 1 addition & 1 deletion modules/fishnet/src/main/AnalysisBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,5 @@ final private class AnalysisBuilder(evalCache: IFishnetEvalCache)(using Executor
variation = variation.map(uci => SanStr(uci.uci)) // temporary, for UciToSan
)
if info.ply.isOdd then info.invert else info
case ((_, _), index) => Info(startedAtPly + index + 1, Eval.empty, Nil)
case ((_, _), index) => Info(startedAtPly + index + 1, lila.tree.evals.empty, Nil)
}
2 changes: 1 addition & 1 deletion modules/fishnet/src/main/JsonApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package lila.fishnet

import chess.format.{ Fen, Uci }
import chess.variant.Variant
import chess.eval.Eval.{ Cp, Mate }
import play.api.libs.json.*

import lila.common.Json.{ *, given }
import lila.core.chess.Depth
import lila.core.net.IpAddress
import lila.fishnet.Work as W
import lila.tree.Eval.{ Cp, Mate }

object JsonApi:

Expand Down
3 changes: 2 additions & 1 deletion modules/fishnet/src/test/UciToSanTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package lila.fishnet

import chess.format.pgn.{ Reader, SanStr }
import chess.{ Ply, Replay }
import chess.eval.*
import chess.eval.Eval.*

import scala.language.implicitConversions

import lila.analyse.{ Analysis, Info }
import lila.tree.Eval
import lila.tree.Eval.*

final class UciToSanTest extends munit.FunSuite:

Expand Down
3 changes: 2 additions & 1 deletion modules/insight/src/main/BSONHandlers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package lila.insight

import chess.{ Color, Role }
import chess.IntRating
import chess.eval.WinPercent
import chess.rating.IntRatingDiff
import reactivemongo.api.bson.*

import lila.analyse.{ AccuracyPercent, WinPercent }
import lila.analyse.AccuracyPercent
import lila.common.SimpleOpening
import lila.db.BSON
import lila.db.dsl.{ *, given }
Expand Down
3 changes: 2 additions & 1 deletion modules/insight/src/main/InsightDimension.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package lila.insight

import chess.Role
import chess.eval.WinPercent
import play.api.libs.json.*
import reactivemongo.api.bson.*

import lila.analyse.{ AccuracyPercent, WinPercent }
import lila.analyse.AccuracyPercent
import lila.common.Json.given
import lila.common.{ LilaOpeningFamily, SimpleOpening }
import lila.core.i18n.Translate
Expand Down
5 changes: 3 additions & 2 deletions modules/insight/src/main/PovToEntry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package lila.insight
import chess.format.pgn.SanStr
import chess.opening.OpeningDb
import chess.{ Centis, Clock, Ply, Role, Situation, Stats }
import chess.eval.WinPercent

import lila.analyse.{ AccuracyCP, AccuracyPercent, Advice, Analysis, WinPercent }
import lila.analyse.{ AccuracyCP, AccuracyPercent, Advice, Analysis }
import lila.common.SimpleOpening
import lila.game.Blurs.booleans

Expand Down Expand Up @@ -137,7 +138,7 @@ final private class PovToEntry(
role = role,
eval = prevInfo.flatMap(_.eval.forceAsCp).map(_.ceiled.centipawns),
cpl = cpDiffs.lift(i).flatten,
winPercent = prevInfo.map(_.eval).flatMap(WinPercent.fromEval),
winPercent = prevInfo.map(_.eval).flatMap(_.score).map(WinPercent.fromScore),
accuracyPercent = accuracyPercent,
material = situation.board.materialImbalance * from.pov.color.fold(1, -1),
awareness = awareness,
Expand Down
3 changes: 2 additions & 1 deletion modules/insight/src/main/TutorRange.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package lila.insight

import chess.IntRating
import chess.rating.IntRatingDiff
import chess.eval.WinPercent

import lila.analyse.{ AccuracyPercent, WinPercent }
import lila.analyse.AccuracyPercent

enum RelativeStrength(val id: Int, val name: String):
case MuchWeaker extends RelativeStrength(10, "Much weaker")
Expand Down
5 changes: 3 additions & 2 deletions modules/insight/src/main/model.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package lila.insight

import scalalib.model.Percent
import chess.format.pgn.SanStr
import chess.{ Centis, Clock, Ply, Role }
import chess.eval.WinPercent

import lila.analyse.{ AccuracyPercent, WinPercent }
import lila.analyse.AccuracyPercent
import lila.common.{ LilaOpeningFamily, SimpleOpening }
import lila.core.data.Percent

case class InsightUser(
count: Int, // nb insight entries
Expand Down
11 changes: 6 additions & 5 deletions modules/study/src/main/BSONHandlers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import chess.format.pgn.{ Glyph, Glyphs, SanStr, Tag, Tags }
import chess.format.{ Fen, Uci, UciCharPair, UciPath }
import chess.variant.{ Crazyhouse, Variant }
import chess.{ ByColor, Centis, Check, FideId, Ply, PromotableRole, Role, Square }
import chess.eval.*
import reactivemongo.api.bson.*

import scala.util.Success
Expand All @@ -12,7 +13,7 @@ import lila.db.BSON
import lila.db.BSON.{ Reader, Writer }
import lila.db.dsl.{ *, given }
import lila.tree.Node.{ Comment, Comments, Gamebook, Shape, Shapes }
import lila.tree.{ Branch, Branches, Metas, NewBranch, NewRoot, Root, Score }
import lila.tree.{ Branch, Branches, Metas, NewBranch, NewRoot, Root }

object BSONHandlers:

Expand Down Expand Up @@ -135,7 +136,7 @@ object BSONHandlers:
comments = doc.getAsOpt[Comments](F.comments).getOrElse(Comments.empty)
gamebook = doc.getAsOpt[Gamebook](F.gamebook)
glyphs = doc.getAsOpt[Glyphs](F.glyphs).getOrElse(Glyphs.empty)
eval = doc.getAsOpt[Score](F.score).map(_.eval)
eval = doc.getAsOpt[Score](F.score).map(lila.tree.evals.fromScore)
clock = doc.getAsOpt[Centis](F.clock)
crazyData = doc.getAsOpt[Crazyhouse.Data](F.crazy)
forceVariation = ~doc.getAsOpt[Boolean](F.forceVariation)
Expand Down Expand Up @@ -170,7 +171,7 @@ object BSONHandlers:
comments = doc.getAsOpt[Comments](F.comments).getOrElse(Comments.empty)
gamebook = doc.getAsOpt[Gamebook](F.gamebook)
glyphs = doc.getAsOpt[Glyphs](F.glyphs).getOrElse(Glyphs.empty)
eval = doc.getAsOpt[Score](F.score).map(_.eval)
eval = doc.getAsOpt[Score](F.score).map(lila.tree.evals.fromScore)
clock = doc.getAsOpt[Centis](F.clock)
crazyData = doc.getAsOpt[Crazyhouse.Data](F.crazy)
forceVariation = ~doc.getAsOpt[Boolean](F.forceVariation)
Expand Down Expand Up @@ -244,7 +245,7 @@ object BSONHandlers:
comments = r.getO[Comments](F.comments) | Comments.empty,
gamebook = r.getO[Gamebook](F.gamebook),
glyphs = r.getO[Glyphs](F.glyphs) | Glyphs.empty,
eval = r.getO[Score](F.score).map(_.eval),
eval = r.getO[Score](F.score).map(lila.tree.evals.fromScore),
clock = r.getO[Centis](F.clock),
crazyData = r.getO[Crazyhouse.Data](F.crazy),
children = StudyFlatTree.reader.rootChildren(fullReader.doc)
Expand Down Expand Up @@ -280,7 +281,7 @@ object BSONHandlers:
comments = r.getO[Comments](F.comments) | Comments.empty,
gamebook = r.getO[Gamebook](F.gamebook),
glyphs = r.getO[Glyphs](F.glyphs) | Glyphs.empty,
eval = r.getO[Score](F.score).map(_.eval),
eval = r.getO[Score](F.score).map(lila.tree.evals.fromScore),
clock = r.getO[Centis](F.clock),
crazyData = r.getO[Crazyhouse.Data](F.crazy)
),
Expand Down
3 changes: 1 addition & 2 deletions modules/tree/src/main/Advice.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package lila.tree

import chess.format.pgn.{ Comment, Glyph }

import lila.tree.Eval.*
import chess.eval.*

sealed trait Advice:
val judgment: Advice.Judgement
Expand Down
10 changes: 5 additions & 5 deletions modules/tree/src/main/Info.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package lila.tree
import chess.Ply
import chess.format.Uci
import chess.format.pgn.{ Comment, SanStr }
import chess.eval.WinPercent

// ply AFTER the move was played
case class Info(ply: Ply, eval: Eval, variation: List[SanStr]):
Expand Down Expand Up @@ -30,9 +31,8 @@ case class Info(ply: Ply, eval: Eval, variation: List[SanStr]):

def cpComment: Option[String] = cp.map(_.showPawns)
def mateComment: Option[String] =
mate.map { m =>
mate.map: m =>
s"Mate in ${math.abs(m.value)}"
}
// advise comment
def evalComment: Option[String] = cpComment.orElse(mateComment)

Expand All @@ -51,21 +51,21 @@ case class Info(ply: Ply, eval: Eval, variation: List[SanStr]):

object Info:

import Eval.{ Cp, Mate }
import chess.eval.Eval.{ Cp, Mate }

val LineMaxPlies = 12

private val separator = ","
private val listSeparator = ";"

def start(ply: Ply) = Info(ply, Eval.initial, Nil)
def start(ply: Ply) = Info(ply, evals.initial, Nil)

private def strCp(s: String) = Cp.from(s.toIntOption)
private def strMate(s: String) = Mate.from(s.toIntOption)

private def decode(ply: Ply, str: String): Option[Info] =
str.split(separator) match
case Array() => Info(ply, Eval.empty, Nil).some
case Array() => Info(ply, evals.empty, Nil).some
case Array(cp) => Info(ply, Eval(strCp(cp), None, None), Nil).some
case Array(cp, ma) => Info(ply, Eval(strCp(cp), strMate(ma), None), Nil).some
case Array(cp, ma, va) =>
Expand Down
30 changes: 0 additions & 30 deletions modules/tree/src/main/WinPercent.scala

This file was deleted.

Loading

0 comments on commit 0a7a690

Please sign in to comment.