store, precompute and display user variant elos
parent
b7bb597954
commit
24ea61025a
|
@ -47,6 +47,15 @@
|
|||
</div>
|
||||
}
|
||||
}
|
||||
@u.variantElos.toMap.map {
|
||||
case (variant, x) => {
|
||||
<div class="speed_elos">
|
||||
<h3>@variant.toString</h3>
|
||||
<strong>@x.elo</strong> ELO<br/>
|
||||
<strong>@x.nb</strong> @trans.games()
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@ object $enumerate {
|
|||
)
|
||||
}
|
||||
|
||||
def fold[A: BSONDocumentReader, B: Zero](query: QueryBuilder)(f: (B, A) ⇒ B): Fu[B] =
|
||||
query.cursor[A].enumerate |>>> Iteratee.fold(∅[B])(f)
|
||||
def fold[A: BSONDocumentReader, B](query: QueryBuilder)(zero: B)(f: (B, A) ⇒ B): Fu[B] =
|
||||
query.cursor[A].enumerate |>>> Iteratee.fold(zero)(f)
|
||||
|
||||
def foldZero[A: BSONDocumentReader, B: Zero](query: QueryBuilder)(f: (B, A) ⇒ B): Fu[B] =
|
||||
fold(query)(∅[B])(f)
|
||||
}
|
||||
|
|
|
@ -30,23 +30,39 @@ private[game] final class ComputeElos(system: ActorSystem) {
|
|||
funit
|
||||
}
|
||||
|
||||
def apply(user: User): Funit = $enumerate.fold[Option[Game], SpeedElos](gamesQuery(user)) {
|
||||
case (elos, gameOption) ⇒ (for {
|
||||
def apply(user: User): Funit = $enumerate.fold[Option[Game], User](gamesQuery(user))(user) {
|
||||
case (user, gameOption) ⇒ (for {
|
||||
game ← gameOption
|
||||
player ← game player user
|
||||
opponentElo ← game.opponent(player).elo
|
||||
} yield {
|
||||
val speed = Speed(game.clock)
|
||||
val speedElo = elos(speed)
|
||||
val opponentSpeedElo = SubElo(0, opponentElo)
|
||||
val (white, black) = player.color.fold[(eloCalculator.User, eloCalculator.User)](
|
||||
speedElo -> opponentSpeedElo,
|
||||
opponentSpeedElo -> speedElo)
|
||||
val newElos = eloCalculator.calculate(white, black, game.winnerColor)
|
||||
val newElo = player.color.fold(newElos._1, newElos._2)
|
||||
elos.addGame(speed, newElo)
|
||||
}) | elos
|
||||
} flatMap UserRepo.setSpeedElos(user.id)
|
||||
} yield user.copy(
|
||||
speedElos = {
|
||||
val speed = Speed(game.clock)
|
||||
val speedElo = user.speedElos(speed)
|
||||
val opponentSpeedElo = SubElo(0, opponentElo)
|
||||
val (white, black) = player.color.fold[(eloCalculator.User, eloCalculator.User)](
|
||||
speedElo -> opponentSpeedElo,
|
||||
opponentSpeedElo -> speedElo)
|
||||
val newElos = eloCalculator.calculate(white, black, game.winnerColor)
|
||||
val newElo = player.color.fold(newElos._1, newElos._2)
|
||||
user.speedElos.addGame(speed, newElo)
|
||||
},
|
||||
variantElos = {
|
||||
val variantElo = user.variantElos(game.variant)
|
||||
val opponentVariantElo = SubElo(0, opponentElo)
|
||||
val (white, black) = player.color.fold[(eloCalculator.User, eloCalculator.User)](
|
||||
variantElo -> opponentVariantElo,
|
||||
opponentVariantElo -> variantElo)
|
||||
val newElos = eloCalculator.calculate(white, black, game.winnerColor)
|
||||
val newElo = player.color.fold(newElos._1, newElos._2)
|
||||
user.variantElos.addGame(game.variant, newElo)
|
||||
}
|
||||
)
|
||||
) | user
|
||||
} flatMap { user ⇒
|
||||
UserRepo.setSpeedElos(user.id, user.speedElos) >>
|
||||
UserRepo.setVariantElos(user.id, user.variantElos)
|
||||
}
|
||||
|
||||
private def usersQuery = $query.apply[User](
|
||||
Json.obj(
|
||||
|
|
|
@ -9,6 +9,7 @@ case class User(
|
|||
username: String,
|
||||
elo: Int,
|
||||
speedElos: SpeedElos,
|
||||
variantElos: VariantElos,
|
||||
count: Count,
|
||||
troll: Boolean = false,
|
||||
ipBan: Boolean = false,
|
||||
|
@ -57,6 +58,7 @@ object User {
|
|||
|
||||
private implicit def countTube = Count.tube
|
||||
private implicit def speedElosTube = SpeedElos.tube
|
||||
private implicit def variantElosTube = VariantElos.tube
|
||||
|
||||
private[user] lazy val tube = Tube[User](
|
||||
(__.json update (
|
||||
|
@ -69,6 +71,7 @@ object User {
|
|||
|
||||
private def defaults = Json.obj(
|
||||
"speedElos" -> SpeedElos.default,
|
||||
"variantElos" -> VariantElos.default,
|
||||
"troll" -> false,
|
||||
"ipBan" -> false,
|
||||
"settings" -> Json.obj(),
|
||||
|
|
|
@ -146,11 +146,16 @@ object UserRepo {
|
|||
|
||||
def setBio(id: ID, bio: String) = $update.field(id, "bio", bio)
|
||||
|
||||
def setSpeedElos(id: ID)(ses: SpeedElos) = {
|
||||
def setSpeedElos(id: ID, ses: SpeedElos) = {
|
||||
import tube.speedElosTube
|
||||
$update.field(id, "speedElos", ses)
|
||||
}
|
||||
|
||||
def setVariantElos(id: ID, ses: VariantElos) = {
|
||||
import tube.variantElosTube
|
||||
$update.field(id, "variantElos", ses)
|
||||
}
|
||||
|
||||
def enable(id: ID) = $update.field(id, "enabled", true)
|
||||
|
||||
def disable(id: ID) = $update.field(id, "enabled", false)
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package lila.user
|
||||
|
||||
import chess.Variant
|
||||
import lila.db.Tube
|
||||
import play.api.libs.json._
|
||||
import Tube.Helpers._
|
||||
|
||||
case class VariantElos(
|
||||
standard: SubElo,
|
||||
chess960: SubElo) {
|
||||
|
||||
def apply(variant: Variant) = variant match {
|
||||
case Variant.Chess960 ⇒ chess960
|
||||
case _ ⇒ standard
|
||||
}
|
||||
|
||||
def toMap = Map(
|
||||
Variant.Standard -> standard,
|
||||
Variant.Chess960 -> chess960)
|
||||
|
||||
def addGame(variant: Variant, newElo: Int) = variant match {
|
||||
case Variant.Chess960 ⇒ copy(chess960 = chess960 addGame newElo)
|
||||
case Variant.Standard ⇒ copy(standard = standard addGame newElo)
|
||||
case _ ⇒ this
|
||||
}
|
||||
|
||||
def adjustTo(to: Int) = {
|
||||
val nb = toMap.values.map(_.nb).sum
|
||||
if (nb == 0) this else {
|
||||
val median = (toMap.values map {
|
||||
case SubElo(nb, elo) ⇒ nb * elo
|
||||
}).sum / nb
|
||||
val diff = to - median
|
||||
def amortize(se: SubElo) = se withElo (se.elo + (diff * se.nb / nb))
|
||||
VariantElos(
|
||||
standard = amortize(standard),
|
||||
chess960 = amortize(chess960))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VariantElos {
|
||||
|
||||
val default = VariantElos(SubElo.default, SubElo.default)
|
||||
|
||||
private implicit def subEloTube = SubElo.tube
|
||||
|
||||
private[user] lazy val tube = Tube[VariantElos](
|
||||
__.json update merge(defaults) andThen Json.reads[VariantElos],
|
||||
Json.writes[VariantElos])
|
||||
|
||||
private def defaults = Json.obj(
|
||||
"standard" -> SubElo.default,
|
||||
"chess960" -> SubElo.default)
|
||||
}
|
|
@ -10,6 +10,7 @@ package object user extends PackageObject with WithPlay {
|
|||
implicit lazy val userTube = User.tube inColl Env.current.userColl
|
||||
|
||||
private[user] implicit lazy val speedElosTube = SpeedElos.tube
|
||||
private[user] implicit lazy val variantElosTube = VariantElos.tube
|
||||
|
||||
private[user] implicit lazy val historyTube =
|
||||
Tube.json inColl Env.current.historyColl
|
||||
|
|
3
todo
3
todo
|
@ -70,8 +70,7 @@ customize piece images
|
|||
opera bug http://postimg.org/image/zcv8hse8n/full/
|
||||
customize sound notifications http://imgur.com/70WVyb5
|
||||
show friend game results on timeline
|
||||
profile tweaks proposed by clarkey http://i.imgur.com/C57qoRC.jpg
|
||||
bullet < 3 min
|
||||
opera issue http://en.lichess.org/forum/lichess-feedback/new-game-wont-show-on-games-list-opera#1
|
||||
|
||||
---
|
||||
|
||||
|
|
Loading…
Reference in New Issue