swiss: no longer store player pairing
this is all completely nonsensical. I hate swiss.pull/6639/head
parent
54f95afe0a
commit
095caad281
|
@ -0,0 +1,19 @@
|
|||
db.swiss_pairing.dropIndex('s_1_u_1');
|
||||
db.swiss_pairing.dropIndex('s_1_n_1');
|
||||
|
||||
db.swiss.find().forEach(swiss => {
|
||||
|
||||
let index = {};
|
||||
db.swiss_player.find({s:swiss._id}).forEach(p => {
|
||||
index[p.n] = p.u;
|
||||
});
|
||||
|
||||
db.swiss_pairing.find({s:swiss._id}).forEach(p => {
|
||||
db.swiss_pairing.update({_id:p._id},{$set:{
|
||||
p:[index[p.p[0]],index[p.p[1]]],
|
||||
t: p.t == p.p[0] ? NumberInt(0) : (p.t == p.p[1] ? NumberInt(1) : p.t)
|
||||
}});
|
||||
});
|
||||
|
||||
db.swiss_player.update({s:swiss._id},{$unset:{n:1}},{multi:1});
|
||||
});
|
|
@ -4,9 +4,10 @@ import scala.concurrent.duration._
|
|||
|
||||
import chess.Clock.{ Config => ClockConfig }
|
||||
import chess.variant.Variant
|
||||
import chess.StartingPosition
|
||||
import chess.{ Color, StartingPosition }
|
||||
import lila.db.BSON
|
||||
import lila.db.dsl._
|
||||
import lila.user.User
|
||||
import reactivemongo.api.bson._
|
||||
|
||||
private object BsonHandlers {
|
||||
|
@ -46,11 +47,10 @@ private object BsonHandlers {
|
|||
implicit val swissTieBreakHandler = doubleAnyValHandler[Swiss.TieBreak](_.value, Swiss.TieBreak.apply)
|
||||
implicit val swissPerformanceHandler =
|
||||
floatAnyValHandler[Swiss.Performance](_.value, Swiss.Performance.apply)
|
||||
implicit val swissScoreHandler = intAnyValHandler[Swiss.Score](_.value, Swiss.Score.apply)
|
||||
implicit val playerNumberHandler = intAnyValHandler[SwissPlayer.Number](_.value, SwissPlayer.Number.apply)
|
||||
implicit val roundNumberHandler = intAnyValHandler[SwissRound.Number](_.value, SwissRound.Number.apply)
|
||||
implicit val swissIdHandler = stringAnyValHandler[Swiss.Id](_.value, Swiss.Id.apply)
|
||||
implicit val playerIdHandler = stringAnyValHandler[SwissPlayer.Id](_.value, SwissPlayer.Id.apply)
|
||||
implicit val swissScoreHandler = intAnyValHandler[Swiss.Score](_.value, Swiss.Score.apply)
|
||||
implicit val roundNumberHandler = intAnyValHandler[SwissRound.Number](_.value, SwissRound.Number.apply)
|
||||
implicit val swissIdHandler = stringAnyValHandler[Swiss.Id](_.value, Swiss.Id.apply)
|
||||
implicit val playerIdHandler = stringAnyValHandler[SwissPlayer.Id](_.value, SwissPlayer.Id.apply)
|
||||
|
||||
implicit val playerHandler = new BSON[SwissPlayer] {
|
||||
import SwissPlayer.Fields._
|
||||
|
@ -58,7 +58,6 @@ private object BsonHandlers {
|
|||
SwissPlayer(
|
||||
id = r.get[SwissPlayer.Id](id),
|
||||
swissId = r.get[Swiss.Id](swissId),
|
||||
number = r.get[SwissPlayer.Number](number),
|
||||
userId = r str userId,
|
||||
rating = r int rating,
|
||||
provisional = r boolD provisional,
|
||||
|
@ -73,7 +72,6 @@ private object BsonHandlers {
|
|||
$doc(
|
||||
id -> o.id,
|
||||
swissId -> o.swissId,
|
||||
number -> o.number,
|
||||
userId -> o.userId,
|
||||
rating -> o.rating,
|
||||
provisional -> w.boolO(o.provisional),
|
||||
|
@ -88,20 +86,20 @@ private object BsonHandlers {
|
|||
|
||||
implicit val pairingStatusHandler = lila.db.dsl.quickHandler[SwissPairing.Status](
|
||||
{
|
||||
case BSONInteger(n) => Right(SwissPlayer.Number(n).some)
|
||||
case BSONBoolean(true) => Left(SwissPairing.Ongoing)
|
||||
case _ => Right(none)
|
||||
case BSONBoolean(true) => Left(SwissPairing.Ongoing)
|
||||
case BSONInteger(index) => Right(Color(index == 0).some)
|
||||
case _ => Right(none)
|
||||
},
|
||||
{
|
||||
case Left(_) => BSONBoolean(true)
|
||||
case Right(Some(n)) => BSONInteger(n.value)
|
||||
case Right(Some(c)) => BSONInteger(c.fold(0, 1))
|
||||
case _ => BSONNull
|
||||
}
|
||||
)
|
||||
implicit val pairingHandler = new BSON[SwissPairing] {
|
||||
import SwissPairing.Fields._
|
||||
def reads(r: BSON.Reader) =
|
||||
r.get[List[SwissPlayer.Number]](players) match {
|
||||
r.get[List[User.ID]](players) match {
|
||||
case List(w, b) =>
|
||||
SwissPairing(
|
||||
id = r str id,
|
||||
|
|
|
@ -35,9 +35,11 @@ final class Env(
|
|||
|
||||
private val sheetApi = wire[SwissSheetApi]
|
||||
|
||||
private lazy val rankingApi: SwissRankingApi = wire[SwissRankingApi]
|
||||
|
||||
val trf: SwissTrf = wire[SwissTrf]
|
||||
|
||||
private val pairingSystem = new PairingSystem(trf, appConfig.get[String]("swiss.bbpairing"))
|
||||
private val pairingSystem = new PairingSystem(trf, rankingApi, appConfig.get[String]("swiss.bbpairing"))
|
||||
|
||||
private val scoring = wire[SwissScoring]
|
||||
|
||||
|
@ -56,8 +58,6 @@ final class Env(
|
|||
|
||||
lazy val standingApi = wire[SwissStandingApi]
|
||||
|
||||
private lazy val rankingApi = wire[SwissRankingApi]
|
||||
|
||||
lazy val json = wire[SwissJson]
|
||||
|
||||
lazy val forms = wire[SwissForm]
|
||||
|
|
|
@ -6,13 +6,17 @@ import java.io.File
|
|||
import scala.concurrent.blocking
|
||||
import scala.sys.process._
|
||||
|
||||
final private class PairingSystem(trf: SwissTrf, executable: String)(implicit
|
||||
import lila.user.User
|
||||
|
||||
final private class PairingSystem(trf: SwissTrf, rankingApi: SwissRankingApi, executable: String)(implicit
|
||||
ec: scala.concurrent.ExecutionContext,
|
||||
mat: akka.stream.Materializer
|
||||
) {
|
||||
|
||||
def apply(swiss: Swiss): Fu[List[SwissPairing.ByeOrPending]] =
|
||||
invoke(swiss, trf(swiss)) map reader
|
||||
rankingApi(swiss) flatMap { ranking =>
|
||||
invoke(swiss, trf(swiss, ranking)) map reader(ranking.map(_.swap))
|
||||
}
|
||||
|
||||
private def invoke(swiss: Swiss, input: Source[String, _]): Fu[List[String]] =
|
||||
withTempFile(input) { file =>
|
||||
|
@ -31,20 +35,20 @@ final private class PairingSystem(trf: SwissTrf, executable: String)(implicit
|
|||
} else stdout.toList
|
||||
}
|
||||
|
||||
private def reader(output: List[String]): List[SwissPairing.ByeOrPending] =
|
||||
private def reader(rankingSwap: Map[Int, User.ID])(output: List[String]): List[SwissPairing.ByeOrPending] =
|
||||
output
|
||||
.drop(1) // first line is the number of pairings
|
||||
.map(_ split ' ')
|
||||
.collect {
|
||||
case Array(p, "0") =>
|
||||
p.toIntOption map { p =>
|
||||
Left(SwissPairing.Bye(SwissPlayer.Number(p)))
|
||||
p.toIntOption flatMap rankingSwap.get map { userId =>
|
||||
Left(SwissPairing.Bye(userId))
|
||||
}
|
||||
case Array(w, b) =>
|
||||
for {
|
||||
white <- w.toIntOption
|
||||
black <- b.toIntOption
|
||||
} yield Right(SwissPairing.Pending(SwissPlayer.Number(white), SwissPlayer.Number(black)))
|
||||
white <- w.toIntOption flatMap rankingSwap.get
|
||||
black <- b.toIntOption flatMap rankingSwap.get
|
||||
} yield Right(SwissPairing.Pending(white, black))
|
||||
}
|
||||
.flatten
|
||||
|
||||
|
|
|
@ -134,8 +134,7 @@ final class SwissApi(
|
|||
.flatMap { rejoin =>
|
||||
fuccess(rejoin.n == 1) >>| { // if the match failed (not the update!), try a join
|
||||
(swiss.isEnterable && isInTeam(swiss.teamId)) ?? {
|
||||
val number = SwissPlayer.Number(swiss.nbPlayers + 1)
|
||||
colls.player.insert.one(SwissPlayer.make(swiss.id, number, me, swiss.perfLens)) zip
|
||||
colls.player.insert.one(SwissPlayer.make(swiss.id, me, swiss.perfLens)) zip
|
||||
colls.swiss.update.one($id(swiss.id), $inc("nbPlayers" -> 1)) inject true
|
||||
}
|
||||
}
|
||||
|
@ -150,16 +149,8 @@ final class SwissApi(
|
|||
if (swiss.isStarted)
|
||||
colls.player.updateField($id(SwissPlayer.makeId(swiss.id, me.id)), f.absent, true)
|
||||
else
|
||||
colls.player.ext.findAndRemove[SwissPlayer]($id(SwissPlayer.makeId(swiss.id, me.id))) flatMap {
|
||||
_ ?? { player =>
|
||||
colls.player.update
|
||||
.one(
|
||||
$doc(f.swissId -> id, f.number $gt player.number),
|
||||
$inc(f.number -> -1),
|
||||
multi = true
|
||||
) zip
|
||||
colls.swiss.update.one($id(swiss.id), $inc("nbPlayers" -> -1)) void
|
||||
}
|
||||
colls.player.delete.one($id(SwissPlayer.makeId(swiss.id, me.id))) flatMap { res =>
|
||||
(res.n == 1) ?? colls.swiss.update.one($id(swiss.id), $inc("nbPlayers" -> -1)).void
|
||||
}
|
||||
}.void >>- recomputeAndUpdateAll(id)
|
||||
}
|
||||
|
@ -194,7 +185,7 @@ final class SwissApi(
|
|||
_ ?? { player =>
|
||||
SwissPairing.fields { f =>
|
||||
colls.pairing.ext
|
||||
.find($doc(f.swissId -> swiss.id, f.players -> player.number))
|
||||
.find($doc(f.swissId -> swiss.id, f.players -> player.userId))
|
||||
.sort($sort asc f.round)
|
||||
.list[SwissPairing]()
|
||||
} flatMap {
|
||||
|
@ -226,7 +217,7 @@ final class SwissApi(
|
|||
pairings.headOption ?? { first =>
|
||||
SwissPlayer.fields { f =>
|
||||
colls.player.ext
|
||||
.find($doc(f.swissId -> first.swissId, f.number $in pairings.map(_ opponentOf player.number)))
|
||||
.find($inIds(pairings.map(_ opponentOf player.userId).map { SwissPlayer.makeId(first.swissId, _) }))
|
||||
.list[SwissPlayer]()
|
||||
} flatMap { opponents =>
|
||||
lightUserApi asyncMany opponents.map(_.userId) map { users =>
|
||||
|
@ -235,7 +226,7 @@ final class SwissApi(
|
|||
}
|
||||
} map { opponents =>
|
||||
pairings flatMap { pairing =>
|
||||
opponents.find(_.player.number == pairing.opponentOf(player.number)) map {
|
||||
opponents.find(_.player.userId == pairing.opponentOf(player.userId)) map {
|
||||
SwissPairing.View(pairing, _)
|
||||
}
|
||||
}
|
||||
|
@ -257,16 +248,9 @@ final class SwissApi(
|
|||
}
|
||||
|
||||
def pageOf(swiss: Swiss, userId: User.ID): Fu[Option[Int]] =
|
||||
colls.player.primitiveOne[SwissPlayer.Number](
|
||||
$id(SwissPlayer.makeId(swiss.id, userId)),
|
||||
SwissPlayer.Fields.number
|
||||
) flatMap {
|
||||
_ ?? { number =>
|
||||
rankingApi(swiss) map {
|
||||
_ get number map { rank =>
|
||||
(Math.floor(rank / 10) + 1).toInt
|
||||
}
|
||||
}
|
||||
rankingApi(swiss) map {
|
||||
_ get userId map { rank =>
|
||||
(Math.floor(rank / 10) + 1).toInt
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,10 +263,15 @@ final class SwissApi(
|
|||
} else
|
||||
colls.pairing.byId[SwissPairing](game.id).dmap(_.filter(_.isOngoing)) flatMap {
|
||||
_ ?? { pairing =>
|
||||
val winner = game.winnerColor
|
||||
.map(_.fold(pairing.white, pairing.black))
|
||||
.flatMap(playerNumberHandler.writeOpt)
|
||||
colls.pairing.updateField($id(game.id), SwissPairing.Fields.status, winner | BSONNull).void >> {
|
||||
colls.pairing
|
||||
.updateField(
|
||||
$id(game.id),
|
||||
SwissPairing.Fields.status,
|
||||
game.winnerColor
|
||||
.map(_.fold(pairing.white, pairing.black))
|
||||
.fold[BSONValue](BSONNull)(BSONString.apply)
|
||||
)
|
||||
.void >> {
|
||||
if (swiss.nbOngoing > 0)
|
||||
colls.swiss.update.one($id(swiss.id), $inc("nbOngoing" -> -1))
|
||||
else
|
||||
|
|
|
@ -52,7 +52,7 @@ final private class SwissBoardApi(
|
|||
case (player, _) if player.present => player
|
||||
}
|
||||
.flatMap { player =>
|
||||
pairings get player.number flatMap {
|
||||
pairings get player.userId flatMap {
|
||||
_ get swiss.round
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +65,8 @@ final private class SwissBoardApi(
|
|||
p2 <- playerMap get pairing.black
|
||||
u1 <- lightUserApi sync p1.userId
|
||||
u2 <- lightUserApi sync p2.userId
|
||||
r1 <- ranks get p1.number
|
||||
r2 <- ranks get p2.number
|
||||
r1 <- ranks get p1.userId
|
||||
r2 <- ranks get p2.userId
|
||||
} yield SwissBoard(
|
||||
pairing.gameId,
|
||||
white = SwissBoard.Player(u1, r1 + 1, p1.rating),
|
||||
|
|
|
@ -6,6 +6,7 @@ import scala.util.chaining._
|
|||
|
||||
import lila.db.dsl._
|
||||
import lila.game.Game
|
||||
import lila.user.User
|
||||
|
||||
final private class SwissDirector(
|
||||
colls: SwissColls,
|
||||
|
@ -30,7 +31,6 @@ final private class SwissDirector(
|
|||
players <- SwissPlayer.fields { f =>
|
||||
colls.player.ext
|
||||
.find($doc(f.swissId -> swiss.id))
|
||||
.sort($sort asc f.number)
|
||||
.list[SwissPlayer]()
|
||||
}
|
||||
ids <- idGenerator.games(pendingPairings.size)
|
||||
|
@ -59,7 +59,7 @@ final private class SwissDirector(
|
|||
byes = pendings.collect { case Left(bye) => bye.player }
|
||||
_ <- SwissPlayer.fields { f =>
|
||||
colls.player.update
|
||||
.one($doc(f.number $in byes, f.swissId -> swiss.id), $addToSet(f.byes -> swiss.round))
|
||||
.one($doc(f.userId $in byes, f.swissId -> swiss.id), $addToSet(f.byes -> swiss.round))
|
||||
.void
|
||||
}
|
||||
_ <- colls.pairing.insert.many(pairings).void
|
||||
|
@ -78,7 +78,7 @@ final private class SwissDirector(
|
|||
}
|
||||
.monSuccess(_.swiss.startRound)
|
||||
|
||||
private def makeGame(swiss: Swiss, players: Map[SwissPlayer.Number, SwissPlayer])(
|
||||
private def makeGame(swiss: Swiss, players: Map[User.ID, SwissPlayer])(
|
||||
pairing: SwissPairing
|
||||
): Game =
|
||||
Game
|
||||
|
|
|
@ -74,7 +74,7 @@ final class SwissJson(
|
|||
.?? {
|
||||
colls.pairing
|
||||
.find(
|
||||
$doc(f.swissId -> swiss.id, f.players -> player.number, f.status -> SwissPairing.ongoing),
|
||||
$doc(f.swissId -> swiss.id, f.players -> player.userId, f.status -> SwissPairing.ongoing),
|
||||
$doc(f.id -> true).some
|
||||
)
|
||||
.sort($sort desc f.round)
|
||||
|
@ -110,7 +110,7 @@ final class SwissJson(
|
|||
// guess its rank based on other players scores in the DB
|
||||
private def getOrGuessRank(swiss: Swiss, player: SwissPlayer): Fu[Int] =
|
||||
rankingApi(swiss) flatMap {
|
||||
_ get player.number match {
|
||||
_ get player.userId match {
|
||||
case Some(rank) => fuccess(rank)
|
||||
case None =>
|
||||
SwissPlayer.fields { f =>
|
||||
|
@ -241,7 +241,7 @@ object SwissJson {
|
|||
private def pairingJsonMin(player: SwissPlayer, pairing: SwissPairing): String = {
|
||||
val status =
|
||||
if (pairing.isOngoing) "o"
|
||||
else pairing.resultFor(player.number).fold("d") { r => if (r) "w" else "l" }
|
||||
else pairing.resultFor(player.userId).fold("d") { r => if (r) "w" else "l" }
|
||||
s"${pairing.gameId}$status"
|
||||
}
|
||||
|
||||
|
@ -251,8 +251,8 @@ object SwissJson {
|
|||
"g" -> pairing.gameId
|
||||
)
|
||||
.add("o" -> pairing.isOngoing)
|
||||
.add("w" -> pairing.resultFor(player.number))
|
||||
.add("c" -> (pairing.white == player.number))
|
||||
.add("w" -> pairing.resultFor(player.userId))
|
||||
.add("c" -> (pairing.white == player.userId))
|
||||
|
||||
private def pairingJsonOrOutcome(
|
||||
player: SwissPlayer
|
||||
|
|
|
@ -1,45 +1,48 @@
|
|||
package lila.swiss
|
||||
|
||||
import chess.Color
|
||||
import lila.game.Game
|
||||
import lila.user.User
|
||||
|
||||
case class SwissPairing(
|
||||
id: Game.ID,
|
||||
swissId: Swiss.Id,
|
||||
round: SwissRound.Number,
|
||||
white: SwissPlayer.Number,
|
||||
black: SwissPlayer.Number,
|
||||
white: User.ID,
|
||||
black: User.ID,
|
||||
status: SwissPairing.Status
|
||||
) {
|
||||
def gameId = id
|
||||
def players = List(white, black)
|
||||
def has(number: SwissPlayer.Number) = white == number || black == number
|
||||
def colorOf(number: SwissPlayer.Number) = chess.Color(white == number)
|
||||
def opponentOf(number: SwissPlayer.Number) = if (white == number) black else white
|
||||
def winner: Option[SwissPlayer.Number] = ~status.toOption
|
||||
def isOngoing = status.isLeft
|
||||
def resultFor(number: SwissPlayer.Number) = winner.map(number.==)
|
||||
def whiteWins = status == Right(Some(white))
|
||||
def blackWins = status == Right(Some(black))
|
||||
def isDraw = status == Right(None)
|
||||
def apply(c: Color) = c.fold(white, black)
|
||||
def gameId = id
|
||||
def players = List(white, black)
|
||||
def has(userId: User.ID) = white == userId || black == userId
|
||||
def colorOf(userId: User.ID) = chess.Color(white == userId)
|
||||
def opponentOf(userId: User.ID) = if (white == userId) black else white
|
||||
def winner: Option[User.ID] = (~status.toOption).map(apply)
|
||||
def isOngoing = status.isLeft
|
||||
def resultFor(userId: User.ID) = winner.map(userId.==)
|
||||
def whiteWins = status == Right(Some(white))
|
||||
def blackWins = status == Right(Some(black))
|
||||
def isDraw = status == Right(None)
|
||||
}
|
||||
|
||||
object SwissPairing {
|
||||
|
||||
sealed trait Ongoing
|
||||
case object Ongoing extends Ongoing
|
||||
type Status = Either[Ongoing, Option[SwissPlayer.Number]]
|
||||
type Status = Either[Ongoing, Option[Color]]
|
||||
|
||||
val ongoing: Status = Left(Ongoing)
|
||||
|
||||
case class Pending(
|
||||
white: SwissPlayer.Number,
|
||||
black: SwissPlayer.Number
|
||||
white: User.ID,
|
||||
black: User.ID
|
||||
)
|
||||
case class Bye(player: SwissPlayer.Number)
|
||||
case class Bye(player: User.ID)
|
||||
|
||||
type ByeOrPending = Either[Bye, Pending]
|
||||
|
||||
type PairingMap = Map[SwissPlayer.Number, Map[SwissRound.Number, SwissPairing]]
|
||||
type PairingMap = Map[User.ID, Map[SwissRound.Number, SwissPairing]]
|
||||
|
||||
case class View(pairing: SwissPairing, player: SwissPlayer.WithUser)
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import lila.user.{ Perfs, User }
|
|||
case class SwissPlayer(
|
||||
id: SwissPlayer.Id, // random
|
||||
swissId: Swiss.Id,
|
||||
number: SwissPlayer.Number,
|
||||
userId: User.ID,
|
||||
rating: Int,
|
||||
provisional: Boolean,
|
||||
|
@ -37,14 +36,12 @@ object SwissPlayer {
|
|||
|
||||
private[swiss] def make(
|
||||
swissId: Swiss.Id,
|
||||
number: SwissPlayer.Number,
|
||||
user: User,
|
||||
perfLens: Perfs => Perf
|
||||
): SwissPlayer =
|
||||
new SwissPlayer(
|
||||
id = makeId(swissId, user.id),
|
||||
swissId = swissId,
|
||||
number = number,
|
||||
userId = user.id,
|
||||
rating = perfLens(user.perfs).intRating,
|
||||
provisional = perfLens(user.perfs).provisional,
|
||||
|
@ -56,8 +53,6 @@ object SwissPlayer {
|
|||
byes = Set.empty
|
||||
).recomputeScore
|
||||
|
||||
case class Number(value: Int) extends AnyVal with IntValue
|
||||
|
||||
case class Ranked(rank: Int, player: SwissPlayer) {
|
||||
def is(other: Ranked) = player is other.player
|
||||
override def toString = s"$rank. ${player.userId}[${player.rating}]"
|
||||
|
@ -88,20 +83,14 @@ object SwissPlayer {
|
|||
sheet: SwissSheet
|
||||
) extends Viewish
|
||||
|
||||
type PlayerMap = Map[SwissPlayer.Number, SwissPlayer]
|
||||
type PlayerMap = Map[User.ID, SwissPlayer]
|
||||
|
||||
def toMap(players: List[SwissPlayer]): PlayerMap =
|
||||
players.view.map(p => p.number -> p).toMap
|
||||
|
||||
// def ranked(ranking: Ranking)(player: SwissPlayer): Option[Ranked] =
|
||||
// ranking get player.userId map { rank =>
|
||||
// Ranked(rank + 1, player)
|
||||
// }
|
||||
players.view.map(p => p.userId -> p).toMap
|
||||
|
||||
object Fields {
|
||||
val id = "_id"
|
||||
val swissId = "s"
|
||||
val number = "n"
|
||||
val userId = "u"
|
||||
val rating = "r"
|
||||
val provisional = "pr"
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package lila.swiss
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import reactivemongo.api.bson._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.db.dsl._
|
||||
import lila.memo.CacheApi
|
||||
import lila.user.User
|
||||
|
||||
final private class SwissRankingApi(
|
||||
colls: SwissColls,
|
||||
|
@ -21,7 +22,7 @@ final private class SwissRankingApi(
|
|||
scoreCache.put(
|
||||
res.swiss.id,
|
||||
res.leaderboard.zipWithIndex.map {
|
||||
case ((p, _), i) => p.number -> (i + 1)
|
||||
case ((p, _), i) => p.userId -> (i + 1)
|
||||
}.toMap
|
||||
)
|
||||
|
||||
|
@ -42,7 +43,7 @@ final private class SwissRankingApi(
|
|||
import framework._
|
||||
Match($doc(f.swissId -> id)) -> List(
|
||||
Sort(Descending(f.score)),
|
||||
Group(BSONNull)("players" -> PushField(f.number))
|
||||
Group(BSONNull)("players" -> PushField(f.userId))
|
||||
)
|
||||
}
|
||||
.headOption map {
|
||||
|
@ -50,10 +51,10 @@ final private class SwissRankingApi(
|
|||
_ get "players" match {
|
||||
case Some(BSONArray(players)) =>
|
||||
// mutable optimized implementation
|
||||
val b = Map.newBuilder[SwissPlayer.Number, Int]
|
||||
val b = Map.newBuilder[User.ID, Int]
|
||||
var r = 0
|
||||
for (u <- players) {
|
||||
b += (SwissPlayer.Number(u.asInstanceOf[BSONInteger].value) -> r)
|
||||
b += (u.asInstanceOf[BSONString].value -> r)
|
||||
r = r + 1
|
||||
}
|
||||
b.result
|
||||
|
|
|
@ -32,12 +32,12 @@ final private class SwissScoring(
|
|||
}
|
||||
playerMap = SwissPlayer.toMap(withPoints)
|
||||
players = withPoints.map { p =>
|
||||
val playerPairings = (~pairingMap.get(p.number)).values
|
||||
val playerPairings = (~pairingMap.get(p.userId)).values
|
||||
val (tieBreak, perfSum) = playerPairings.foldLeft(0f -> 0f) {
|
||||
case ((tieBreak, perfSum), pairing) =>
|
||||
val opponent = playerMap.get(pairing opponentOf p.number)
|
||||
val opponent = playerMap.get(pairing opponentOf p.userId)
|
||||
val opponentPoints = opponent.??(_.points.value)
|
||||
val result = pairing.resultFor(p.number)
|
||||
val result = pairing.resultFor(p.userId)
|
||||
val newTieBreak = tieBreak + result.fold(opponentPoints / 2) { _ ?? opponentPoints }
|
||||
val newPerf = perfSum + opponent.??(_.rating) + result.?? { win =>
|
||||
if (win) 500 else -500
|
||||
|
@ -85,7 +85,7 @@ final private class SwissScoring(
|
|||
SwissPlayer.fields { f =>
|
||||
colls.player.ext
|
||||
.find($doc(f.swissId -> swiss.id))
|
||||
.sort($sort asc f.number)
|
||||
.sort($sort asc f.score)
|
||||
.list[SwissPlayer]()
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ private object SwissSheet {
|
|||
pairingMap: SwissPairing.PairingMap
|
||||
): List[SwissSheet] =
|
||||
players.map { player =>
|
||||
one(swiss, ~pairingMap.get(player.number), player)
|
||||
one(swiss, ~pairingMap.get(player.userId), player)
|
||||
}
|
||||
|
||||
def one(
|
||||
|
@ -46,10 +46,9 @@ private object SwissSheet {
|
|||
pairingMap get round match {
|
||||
case Some(pairing) =>
|
||||
pairing.status match {
|
||||
case Left(_) => Ongoing
|
||||
case Right(None) => Draw
|
||||
case Right(Some(n)) if n == player.number => Win
|
||||
case Right(_) => Loss
|
||||
case Left(_) => Ongoing
|
||||
case Right(None) => Draw
|
||||
case Right(Some(color)) => if (pairing(color) == player.userId) Win else Loss
|
||||
}
|
||||
case None if player.byes(round) => Bye
|
||||
case None if round.value == 1 => Late
|
||||
|
@ -81,7 +80,7 @@ final private class SwissSheetApi(colls: SwissColls)(implicit
|
|||
.aggregateWith[Bdoc](readPreference = readPreference) { implicit framework =>
|
||||
import framework._
|
||||
Match($doc(f.swissId -> swiss.id)) -> List(
|
||||
Sort(Ascending(f.number)),
|
||||
Sort(Descending(f.score)),
|
||||
PipelineOperator(
|
||||
$doc(
|
||||
"$lookup" -> $doc(
|
||||
|
|
|
@ -54,7 +54,7 @@ final class SwissStandingApi(
|
|||
player = player,
|
||||
rank = r + 1,
|
||||
user = user | LightUser.fallback(player.userId),
|
||||
~res.pairings.get(player.number),
|
||||
~res.pairings.get(player.userId),
|
||||
sheet
|
||||
)
|
||||
)
|
||||
|
@ -77,7 +77,7 @@ final class SwissStandingApi(
|
|||
rankedPlayers <- bestWithRankByPage(swiss.id, 10, page atLeast 1)
|
||||
pairings <- !swiss.isCreated ?? SwissPairing.fields { f =>
|
||||
colls.pairing.ext
|
||||
.find($doc(f.swissId -> swiss.id, f.players $in rankedPlayers.map(_.player.number)))
|
||||
.find($doc(f.swissId -> swiss.id, f.players $in rankedPlayers.map(_.player.userId)))
|
||||
.sort($sort asc f.round)
|
||||
.list[SwissPairing]()
|
||||
.map(SwissPairing.toMap)
|
||||
|
@ -97,7 +97,7 @@ final class SwissStandingApi(
|
|||
player,
|
||||
rank,
|
||||
user | LightUser.fallback(player.userId),
|
||||
~pairings.get(player.number),
|
||||
~pairings.get(player.userId),
|
||||
sheet
|
||||
)
|
||||
)
|
||||
|
|
|
@ -87,7 +87,7 @@ final class SwissStatsApi(
|
|||
case (stats, (player, pairings, sheet)) =>
|
||||
pairings.foldLeft((0, 0, 0, 0)) {
|
||||
case ((games, whiteWins, blackWins, draws), pairing) =>
|
||||
val counts = pairing.white == player.number
|
||||
val counts = pairing.white == player.userId
|
||||
(
|
||||
games + counts.??(1),
|
||||
whiteWins + (counts && pairing.whiteWins).??(1),
|
||||
|
|
|
@ -6,15 +6,21 @@ import akka.stream.scaladsl._
|
|||
final class SwissTrf(
|
||||
colls: SwissColls,
|
||||
sheetApi: SwissSheetApi,
|
||||
rankingApi: SwissRankingApi,
|
||||
baseUrl: lila.common.config.BaseUrl
|
||||
) {
|
||||
)(implicit ec: scala.concurrent.ExecutionContext) {
|
||||
|
||||
private type Bits = List[(Int, String)]
|
||||
|
||||
def apply(swiss: Swiss): Source[String, _] =
|
||||
Source futureSource {
|
||||
rankingApi(swiss) map { apply(swiss, _) }
|
||||
}
|
||||
|
||||
def apply(swiss: Swiss, ranking: Ranking): Source[String, _] =
|
||||
tournamentLines(swiss) concat sheetApi
|
||||
.source(swiss)
|
||||
.map((playerLine(swiss) _).tupled)
|
||||
.map((playerLine(swiss, ranking) _).tupled)
|
||||
.map(formatLine)
|
||||
|
||||
private def tournamentLines(swiss: Swiss) =
|
||||
|
@ -29,16 +35,17 @@ final class SwissTrf(
|
|||
s"092 Individual: Swiss-System",
|
||||
s"102 ${baseUrl}/swiss",
|
||||
s"XXR ${swiss.settings.nbRounds}",
|
||||
s"XXC ${chess.Color(scala.util.Random.nextBoolean).name}1"
|
||||
s"XXC ${chess.Color(swiss.id.value(0).toInt % 2 == 0).name}1"
|
||||
)
|
||||
)
|
||||
|
||||
private def playerLine(
|
||||
swiss: Swiss
|
||||
swiss: Swiss,
|
||||
ranking: Ranking
|
||||
)(p: SwissPlayer, pairings: Map[SwissRound.Number, SwissPairing], sheet: SwissSheet): Bits =
|
||||
List(
|
||||
3 -> "001",
|
||||
8 -> p.number.toString,
|
||||
8 -> ranking.getOrElse(p.userId, 0).toString,
|
||||
47 -> p.userId,
|
||||
84 -> f"${sheet.points.value}%1.1f"
|
||||
) ::: {
|
||||
|
@ -46,8 +53,8 @@ final class SwissTrf(
|
|||
case (rn, outcome) =>
|
||||
val pairing = pairings get rn
|
||||
List(
|
||||
95 -> pairing.map(_ opponentOf p.number).??(_.toString),
|
||||
97 -> pairing.map(_ colorOf p.number).??(_.fold("w", "b")),
|
||||
95 -> pairing.map(_ opponentOf p.userId).??(_.toString),
|
||||
97 -> pairing.map(_ colorOf p.userId).??(_.fold("w", "b")),
|
||||
99 -> {
|
||||
import SwissSheet._
|
||||
outcome match {
|
||||
|
|
|
@ -8,24 +8,6 @@ object SwissRound {
|
|||
case class Number(value: Int) extends AnyVal with IntValue
|
||||
}
|
||||
|
||||
case class SwissBye(
|
||||
round: SwissRound.Number,
|
||||
player: SwissPlayer.Number
|
||||
)
|
||||
|
||||
// case class LeaderboardPlayer(
|
||||
// player: SwissPlayer,
|
||||
// pairings: Map[SwissRound.Number, SwissPairing]
|
||||
// )
|
||||
// object LeaderboardPlayer {
|
||||
// def make(swiss: Swiss, player: SwissPlayer, pairings: List[SwissPairing]) = LeaderboardPlayer(
|
||||
// player = player,
|
||||
// pairings = pairings.view.map { p =>
|
||||
// p.round -> p
|
||||
// }.toMap
|
||||
// )
|
||||
// }
|
||||
|
||||
case class MyInfo(rank: Int, gameId: Option[Game.ID], user: User, player: SwissPlayer) {
|
||||
def page = { math.floor((rank - 1) / 10) + 1 }.toInt
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@ package object swiss extends PackageObject {
|
|||
|
||||
private[swiss] val logger = lila.log("swiss")
|
||||
|
||||
private[swiss] type Ranking = Map[SwissPlayer.Number, Int]
|
||||
private[swiss] type Ranking = Map[lila.user.User.ID, Int]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue