Skip to content

Commit

Permalink
tournament/swiss conditions refactoring WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Apr 27, 2023
1 parent be78538 commit 311c217
Show file tree
Hide file tree
Showing 20 changed files with 134 additions and 308 deletions.
11 changes: 6 additions & 5 deletions app/views/swiss/form.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import play.api.data.Form
import lila.api.Context
import lila.app.templating.Environment.{ given, * }
import lila.app.ui.ScalatagsTemplate.{ *, given }
import lila.swiss.{ Swiss, SwissCondition, SwissForm }
import lila.swiss.{ Swiss, SwissForm }
import lila.tournament.TournamentForm
import lila.gathering.{ ConditionForm, GatheringClock }

object form:

Expand Down Expand Up @@ -92,7 +93,7 @@ object form:
frag(
form3.split(
form3.group(form("conditions.nbRatedGame.nb"), trans.minimumRatedGames(), half = true)(
form3.select(_, SwissCondition.DataForm.nbRatedGameChoices)
form3.select(_, ConditionForm.nbRatedGameChoices)
),
(ctx.me.exists(_.hasTitle) || isGranted(_.ManageTournament)) ?? {
form3.checkbox(
Expand All @@ -105,10 +106,10 @@ object form:
),
form3.split(
form3.group(form("conditions.minRating.rating"), trans.minimumRating(), half = true)(
form3.select(_, SwissCondition.DataForm.minRatingChoices)
form3.select(_, ConditionForm.minRatingChoices)
),
form3.group(form("conditions.maxRating.rating"), trans.maximumWeeklyRating(), half = true)(
form3.select(_, SwissCondition.DataForm.maxRatingChoices)
form3.select(_, ConditionForm.maxRatingChoices)
)
)
)
Expand Down Expand Up @@ -164,7 +165,7 @@ final private class SwissFields(form: Form[SwissForm.SwissData], swiss: Option[S
form3.select(_, SwissForm.clockLimitChoices, disabled = disabledAfterStart)
),
form3.group(form("clock.increment"), trans.clockIncrement(), half = true)(
form3.select(_, TournamentForm.clockIncrementChoices, disabled = disabledAfterStart)
form3.select(_, GatheringClock.incrementChoices, disabled = disabledAfterStart)
)
)
def roundInterval =
Expand Down
5 changes: 3 additions & 2 deletions app/views/swiss/show.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@ import lila.api.Context
import lila.app.templating.Environment.{ given, * }
import lila.app.ui.ScalatagsTemplate.{ *, given }
import lila.common.String.html.safeJsonValue
import lila.swiss.{ Swiss, SwissCondition }
import lila.swiss.Swiss
import lila.swiss.SwissRoundNumber
import lila.common.paginator.Paginator
import lila.swiss.SwissPairing
import lila.gathering.Condition.WithVerdicts

object show:

private def fullName(s: Swiss) = s"${s.name} by ${teamIdToName(s.teamId)}"

def apply(
s: Swiss,
verdicts: SwissCondition.All.WithVerdicts,
verdicts: WithVerdicts,
data: play.api.libs.json.JsObject,
chatOption: Option[lila.chat.UserChat.Mine],
streamers: List[UserId],
Expand Down
15 changes: 7 additions & 8 deletions app/views/swiss/side.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ import lila.api.Context
import lila.app.templating.Environment.{ given, * }
import lila.app.ui.ScalatagsTemplate.{ *, given }
import lila.common.String.html.markdownLinksOrRichText
import lila.swiss.{ Swiss, SwissCondition }
import lila.swiss.Swiss
import lila.gathering.Condition
import lila.gathering.Condition.WithVerdicts

object side:

private val separator = ""

def apply(
s: Swiss,
verdicts: SwissCondition.All.WithVerdicts,
verdicts: WithVerdicts,
streamers: List[UserId],
chat: Boolean
)(using
ctx: Context
) =
)(using ctx: Context) =
frag(
div(cls := "swiss__meta")(
st.section(dataIcon := s.perfType.iconChar.toString)(
Expand Down Expand Up @@ -61,8 +61,7 @@ object side:
teamLink(s.teamId),
if (verdicts.relevant)
st.section(
dataIcon := (if (ctx.isAuth && verdicts.accepted) ""
else ""),
dataIcon := (if (ctx.isAuth && verdicts.accepted) "" else ""),
cls := List(
"conditions" -> true,
"accepted" -> (ctx.isAuth && verdicts.accepted),
Expand All @@ -80,7 +79,7 @@ object side:
),
title := v.verdict.reason.map(_(ctx.lang))
)(v.verdict match {
case SwissCondition.RefusedUntil(until) =>
case Condition.RefusedUntil(until) =>
frag(
"Because you missed your last swiss game, you cannot enter a new swiss tournament until ",
absClientInstant(until),
Expand Down
5 changes: 3 additions & 2 deletions app/views/tournament/crud.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import lila.app.ui.ScalatagsTemplate.{ *, given }
import lila.common.paginator.Paginator
import lila.tournament.crud.CrudForm
import lila.tournament.{ Tournament, TournamentForm }
import lila.gathering.GatheringClock

object crud:

Expand Down Expand Up @@ -117,10 +118,10 @@ object crud:
),
form3.split(
form3.group(form("clockTime"), raw("Clock time"), half = true)(
form3.select(_, TournamentForm.clockTimeChoices)
form3.select(_, GatheringClock.timeChoices)
),
form3.group(form("clockIncrement"), raw("Clock increment"), half = true)(
form3.select(_, TournamentForm.clockIncrementChoices)
form3.select(_, GatheringClock.incrementChoices)
)
),
form3.split(
Expand Down
6 changes: 3 additions & 3 deletions app/views/tournament/form.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import lila.app.templating.Environment.{ given, * }
import lila.app.ui.ScalatagsTemplate.{ *, given }
import lila.hub.LeaderTeam
import lila.tournament.{ Tournament, TournamentForm }
import lila.gathering.{ Condition, ConditionForm }
import lila.gathering.{ Condition, ConditionForm, GatheringClock }

object form:

Expand Down Expand Up @@ -238,10 +238,10 @@ final private class TourFields(form: Form[?], tour: Option[Tournament])(using Co
def clock =
form3.split(
form3.group(form("clockTime"), trans.clockInitialTime(), half = true)(
form3.select(_, TournamentForm.clockTimeChoices, disabled = disabledAfterStart)
form3.select(_, GatheringClock.timeChoices, disabled = disabledAfterStart)
),
form3.group(form("clockIncrement"), trans.clockIncrement(), half = true)(
form3.select(_, TournamentForm.clockIncrementChoices, disabled = disabledAfterStart)
form3.select(_, GatheringClock.incrementChoices, disabled = disabledAfterStart)
)
)
def minutes =
Expand Down
2 changes: 2 additions & 0 deletions modules/db/src/main/Handlers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ trait Handlers:
def typedMapHandlerIso[K, V: BSONHandler](using keyIso: StringIso[K]) =
stringMapHandler[V].as[Map[K, V]](_.mapKeys(keyIso.from), _.mapKeys(keyIso.to))

def ifPresentHandler[A](a: A) = quickHandler({ case _: BSONValue => a }, _ => BSONBoolean(true))

given [T: BSONHandler]: BSONHandler[NonEmptyList[T]] =
def listWriter = BSONWriter.collectionWriter[T, List[T]]
def listReader = collectionReader[List, T]
Expand Down
10 changes: 6 additions & 4 deletions modules/gathering/src/main/Condition.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import lila.rating.PerfType
import lila.user.{ Title, User }
import lila.hub.LightTeam.TeamName

sealed trait Condition:
trait Condition:

def name(perf: PerfType)(using Lang): String

Expand All @@ -22,9 +22,11 @@ object Condition:
type GetMaxRating = PerfType => Fu[IntRating]
type GetUserTeamIds = User => Fu[List[TeamId]]

sealed abstract class Verdict(val accepted: Boolean, val reason: Option[Lang => String])
case object Accepted extends Verdict(true, none)
case class Refused(because: Lang => String) extends Verdict(false, because.some)
enum Verdict(val accepted: Boolean, val reason: Option[Lang => String]):
case Accepted extends Verdict(true, none)
case Refused(because: Lang => String) extends Verdict(false, because.some)
case RefusedUntil(until: Instant) extends Verdict(false, none)
export Verdict.*

case class WithVerdict(condition: Condition, verdict: Verdict)

Expand Down
17 changes: 10 additions & 7 deletions modules/gathering/src/main/ConditionHandlers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package lila.gathering
import lila.gathering.Condition.*
import play.api.i18n.Lang
import lila.rating.PerfType
import java.time.format.{ DateTimeFormatter, FormatStyle }

object ConditionHandlers:

Expand All @@ -14,12 +15,9 @@ object ConditionHandlers:
given BSONDocumentHandler[NbRatedGame] = Macros.handler
given BSONDocumentHandler[MaxRating] = Macros.handler
given BSONDocumentHandler[MinRating] = Macros.handler
given BSONHandler[Titled.type] = quickHandler(
{ case _: BSONValue => Titled },
_ => BSONBoolean(true)
)
given BSONDocumentHandler[TeamMember] = Macros.handler
given BSONDocumentHandler[AllowList] = Macros.handler
given BSONHandler[Titled.type] = ifPresentHandler(Titled)
given BSONDocumentHandler[TeamMember] = Macros.handler
given BSONDocumentHandler[AllowList] = Macros.handler

object JSONHandlers:
import lila.common.Json.given
Expand All @@ -31,8 +29,13 @@ object ConditionHandlers:
Json.obj(
"condition" -> cond.name(pt),
"verdict" -> verd.match
case Refused(reason) => reason(lang)
case Accepted => JsString("ok")
case Refused(reason) => reason(lang)
case RefusedUntil(until) =>
val date = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT)
.withLocale(lang.toLocale)
s"Because you missed your last swiss game, you cannot enter a new swiss tournament until $date"
)
},
"accepted" -> verdicts.accepted
Expand Down
11 changes: 11 additions & 0 deletions modules/gathering/src/main/ConditionList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import lila.gathering.Condition.*

abstract class ConditionList(options: List[Option[Condition]]):

def maxRating: Option[MaxRating]
def minRating: Option[MinRating]

def sameMaxRating(other: ConditionList) = maxRating.map(_.rating) == other.maxRating.map(_.rating)
def sameMinRating(other: ConditionList) = minRating.map(_.rating) == other.minRating.map(_.rating)
def sameRatings(other: ConditionList) = sameMaxRating(other) && sameMinRating(other)

lazy val list: List[Condition] = options.flatten

def relevant = list.nonEmpty
Expand All @@ -16,3 +23,7 @@ abstract class ConditionList(options: List[Option[Condition]]):
case _: MaxRating => true
case _: MinRating => true
case _ => false

def validRatings = (minRating, maxRating) match
case (Some(min), Some(max)) => min.rating < max.rating
case _ => true
23 changes: 23 additions & 0 deletions modules/gathering/src/main/GatheringClock.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package lila.gathering

import chess.Clock
import chess.Clock.{ LimitSeconds, IncrementSeconds }
import lila.common.Form.*

object GatheringClock:

val times: Seq[Double] = Seq(0d, 1 / 4d, 1 / 2d, 3 / 4d, 1d, 3 / 2d) ++ {
(2 to 8 by 1) ++ (10 to 30 by 5) ++ (40 to 60 by 10)
}.map(_.toDouble)
val timeDefault = 2d
private def formatLimit(l: Double) =
Clock.Config(LimitSeconds((l * 60).toInt), IncrementSeconds(0)).limitString + {
if (l <= 1) " minute" else " minutes"
}
val timeChoices = optionsDouble(times, formatLimit)

val increments = IncrementSeconds from {
(0 to 2 by 1) ++ (3 to 7) ++ (10 to 30 by 5) ++ (40 to 60 by 10)
}
val incrementDefault = IncrementSeconds(0)
val incrementChoices = options(IncrementSeconds raw increments, "%d second{s}")
6 changes: 3 additions & 3 deletions modules/swiss/src/main/BsonHandlers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ object BsonHandlers:
isForfeit -> w.boolO(o.isForfeit)
)

import SwissCondition.BSONHandlers.given
import SwissCondition.bsonHandler

given BSON[Swiss.Settings] with
def reads(r: BSON.Reader) =
Expand All @@ -93,7 +93,7 @@ object BsonHandlers:
chatFor = r.intO("c") | Swiss.ChatFor.default,
roundInterval = (r.intO("i") | 60).seconds,
password = r.strO("p"),
conditions = r.getO[SwissCondition.All]("o") getOrElse SwissCondition.All.empty,
conditions = r.getD[SwissCondition.All]("o"),
forbiddenPairings = r.getD[String]("fp"),
manualPairings = r.getD[String]("mp")
)
Expand All @@ -106,7 +106,7 @@ object BsonHandlers:
"c" -> (s.chatFor != Swiss.ChatFor.default).option(s.chatFor),
"i" -> s.roundInterval.toSeconds.toInt,
"p" -> s.password,
"o" -> s.conditions.ifNonEmpty,
"o" -> s.conditions.relevant.option(s.conditions),
"fp" -> s.forbiddenPairings.some.filter(_.nonEmpty),
"mp" -> s.manualPairings.some.filter(_.nonEmpty)
)
Expand Down
12 changes: 6 additions & 6 deletions modules/swiss/src/main/SwissApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import lila.game.{ Game, Pov }
import lila.round.actorApi.round.QuietFlag
import lila.user.{ User, UserRepo }
import lila.common.config.Max
import lila.gathering.Condition.WithVerdicts

final class SwissApi(
mongo: SwissMongo,
Expand Down Expand Up @@ -74,7 +75,7 @@ final class SwissApi(
chatFor = data.realChatFor,
roundInterval = data.realRoundInterval,
password = data.password,
conditions = data.conditions.all,
conditions = data.conditions,
forbiddenPairings = ~data.forbiddenPairings,
manualPairings = ~data.manualPairings
)
Expand Down Expand Up @@ -106,7 +107,7 @@ final class SwissApi(
if (data.roundInterval.isDefined) data.realRoundInterval
else old.settings.roundInterval,
password = data.password,
conditions = data.conditions.all,
conditions = data.conditions,
forbiddenPairings = ~data.forbiddenPairings,
manualPairings = ~data.manualPairings
)
Expand Down Expand Up @@ -162,10 +163,9 @@ final class SwissApi(
socket.reload(swiss.id)
}

def verdicts(swiss: Swiss, me: Option[User]): Fu[SwissCondition.All.WithVerdicts] =
me match
case None => fuccess(swiss.settings.conditions.accepted)
case Some(user) => verify(swiss, user)
def verdicts(swiss: Swiss, me: Option[User]): Fu[WithVerdicts] = me match
case None => fuccess(swiss.settings.conditions.accepted)
case Some(user) => verify(swiss, user)

def join(id: SwissId, me: User, isInTeam: TeamId => Boolean, password: Option[String]): Fu[Boolean] =
Sequencing(id)(cache.swissCache.notFinishedById) { swiss =>
Expand Down
Loading

0 comments on commit 311c217

Please sign in to comment.