-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
Swiss.scala
130 lines (103 loc) · 3.84 KB
/
Swiss.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package lila.swiss
import reactivemongo.api.bson.Macros.Annotations.Key
import chess.Clock.Config as ClockConfig
import chess.format.Fen
import scalalib.ThreadLocalRandom
import lila.rating.PerfType
import lila.core.swiss.IdName
case class Swiss(
@Key("_id") id: SwissId,
name: String,
clock: ClockConfig,
variant: chess.variant.Variant,
round: SwissRoundNumber, // ongoing round
nbPlayers: Int,
nbOngoing: Int,
createdAt: Instant,
createdBy: UserId,
teamId: TeamId,
startsAt: Instant,
settings: Swiss.Settings,
nextRoundAt: Option[Instant],
finishedAt: Option[Instant],
winnerId: Option[UserId] = None
):
def isCreated = round.value == 0
def isStarted = !isCreated && !isFinished
def isFinished = finishedAt.isDefined
def isNotFinished = !isFinished
def isNowOrSoon = startsAt.isBefore(nowInstant.plusMinutes(15)) && !isFinished
def finishedSinceSeconds = finishedAt.map(nowSeconds - _.toSeconds)
def isRecentlyFinished = finishedSinceSeconds.exists(_ < 30 * 60)
def isEnterable =
isNotFinished && round.value <= settings.nbRounds / 2 && nbPlayers < Swiss.maxPlayers
def allRounds: List[SwissRoundNumber] = SwissRoundNumber.from((1 to round.value).toList)
def startRound =
copy(
round = SwissRoundNumber(round.value + 1),
nextRoundAt = none
)
def speed = chess.Speed(clock)
def perfType: PerfType = lila.rating.PerfType(variant, speed)
def estimatedDuration: FiniteDuration = {
(clock.limit.toSeconds + clock.increment.toSeconds * 80 + 10) * settings.nbRounds
}.toInt.seconds
def estimatedDurationString =
val minutes = estimatedDuration.toMinutes
if minutes < 60 then s"${minutes}m"
else s"${minutes / 60}h" + (if minutes % 60 != 0 then s" ${minutes % 60}m" else "")
def roundInfo = Swiss.RoundInfo(teamId, settings.chatFor)
def withConditions(conditions: SwissCondition.All) = copy(
settings = settings.copy(conditions = conditions)
)
def unrealisticSettings =
!settings.manualRounds &&
settings.dailyInterval.isEmpty &&
clock.estimateTotalSeconds * 2 * settings.nbRounds > 3600 * 8
def idName = IdName(id, name)
lazy val looksLikePrize = lila.gathering.looksLikePrize(s"$name ${~settings.description}")
object Swiss:
val maxPlayers = 4000
val maxForbiddenPairings = 1000
val maxManualPairings = 10_000
opaque type Round = Int
object Round extends OpaqueInt[Round]
opaque type TieBreak = Double
object TieBreak extends OpaqueDouble[TieBreak]
opaque type Performance = Float
object Performance extends OpaqueFloat[Performance]
opaque type Score = Int
object Score extends OpaqueInt[Score]
case class Settings(
nbRounds: Int,
rated: Boolean,
description: Option[String] = None,
position: Option[Fen.Full],
chatFor: ChatFor = ChatFor.default,
password: Option[String] = None,
conditions: SwissCondition.All,
roundInterval: FiniteDuration,
forbiddenPairings: String,
manualPairings: String
):
lazy val intervalSeconds = roundInterval.toSeconds.toInt
def manualRounds = intervalSeconds == Swiss.RoundInterval.manual
def dailyInterval = (!manualRounds && intervalSeconds >= 24 * 3600).option(intervalSeconds / 3600 / 24)
type ChatFor = Int
object ChatFor:
val NONE = 0
val LEADERS = 10
val MEMBERS = 20
val ALL = 30
val default = MEMBERS
object RoundInterval:
val auto = -1
val manual = 99999999
def makeScore(points: SwissPoints, tieBreak: TieBreak, perf: Performance) =
Score((points.value * 10000000 + tieBreak * 10000 + perf).toInt)
def makeId = SwissId(ThreadLocalRandom.nextString(8))
case class PastAndNext(past: List[Swiss], next: List[Swiss])
case class RoundInfo(
teamId: TeamId,
chatFor: ChatFor
)