swiss: no longer store player pairing

this is all completely nonsensical. I hate swiss.
pull/6639/head
Thibault Duplessis 2020-05-15 15:49:44 -06:00
parent 54f95afe0a
commit 095caad281
18 changed files with 131 additions and 140 deletions

View File

@ -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});
});

View File

@ -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,

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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),

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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

View File

@ -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]()
}

View File

@ -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(

View File

@ -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
)
)

View File

@ -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),

View File

@ -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 {

View File

@ -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
}

View File

@ -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]
}