lila/modules/swiss/src/main/SwissForm.scala

193 lines
6.0 KiB
Scala
Raw Normal View History

2020-04-29 10:31:34 -06:00
package lila.swiss
import chess.Clock.{ Config => ClockConfig }
2021-08-06 01:56:39 -06:00
import chess.Speed
import chess.format.FEN
2020-04-29 10:31:34 -06:00
import chess.variant.Variant
import org.joda.time.DateTime
import play.api.data._
import play.api.data.Forms._
2021-06-01 07:34:46 -06:00
import play.api.data.validation
import play.api.data.validation.Constraint
2020-10-05 06:39:25 -06:00
import play.api.Mode
2020-05-05 19:26:39 -06:00
import scala.concurrent.duration._
2020-04-29 10:31:34 -06:00
import lila.common.Form._
2021-06-01 07:34:46 -06:00
import lila.user.User
2020-04-29 10:31:34 -06:00
2020-05-14 08:26:37 -06:00
final class SwissForm(implicit mode: Mode) {
2020-04-29 10:31:34 -06:00
import SwissForm._
2021-06-01 07:34:46 -06:00
def form(user: User, minRounds: Int = 3) =
2020-05-16 15:15:22 -06:00
Form(
mapping(
"name" -> optional(eventName(2, 30, user.isVerifiedOrAdmin)),
2020-05-16 15:15:22 -06:00
"clock" -> mapping(
"limit" -> number.verifying(clockLimits.contains _),
"increment" -> number(min = 0, max = 120)
2020-05-16 15:15:22 -06:00
)(ClockConfig.apply)(ClockConfig.unapply)
.verifying("Invalid clock", _.estimateTotalSeconds > 0),
"startsAt" -> optional(inTheFuture(ISODateTimeOrTimestamp.isoDateTimeOrTimestamp)),
"variant" -> optional(nonEmptyText.verifying(v => Variant(v).isDefined)),
"rated" -> optional(boolean),
"nbRounds" -> number(min = minRounds, max = 100),
"description" -> optional(cleanNonEmptyText),
"position" -> optional(lila.common.Form.fen.playableStrict),
"chatFor" -> optional(numberIn(chatForChoices.map(_._1))),
"roundInterval" -> optional(numberIn(roundIntervals)),
"password" -> optional(cleanNonEmptyText),
"conditions" -> SwissCondition.DataForm.all,
"forbiddenPairings" -> optional(
cleanNonEmptyText.verifying(
s"Maximum forbidden pairings: ${Swiss.maxForbiddenPairings}",
str => str.linesIterator.size <= Swiss.maxForbiddenPairings
)
)
2020-05-16 15:15:22 -06:00
)(SwissData.apply)(SwissData.unapply)
.verifying("15s and 0+1 variant games cannot be rated", _.validRatedVariant)
2020-05-16 15:15:22 -06:00
)
2020-04-29 10:31:34 -06:00
2021-06-01 07:34:46 -06:00
def create(user: User) =
form(user) fill SwissData(
2020-05-05 22:11:15 -06:00
name = none,
clock = ClockConfig(180, 0),
2020-05-14 08:26:37 -06:00
startsAt = Some(DateTime.now plusSeconds {
if (mode == Mode.Prod) 60 * 10 else 20
}),
2020-05-11 12:16:18 -06:00
variant = Variant.default.key.some,
2020-05-11 14:20:46 -06:00
rated = true.some,
2020-05-23 21:18:43 -06:00
nbRounds = 7,
2020-05-05 22:11:15 -06:00
description = none,
position = none,
2020-05-23 21:18:43 -06:00
chatFor = Swiss.ChatFor.default.some,
2020-07-09 01:14:39 -06:00
roundInterval = Swiss.RoundInterval.auto.some,
2020-10-05 06:39:25 -06:00
password = None,
conditions = SwissCondition.DataForm.AllSetup.default,
forbiddenPairings = none
2020-05-05 22:11:15 -06:00
)
2020-05-04 00:31:50 -06:00
2021-06-01 07:34:46 -06:00
def edit(user: User, s: Swiss) =
form(user, s.round.value) fill SwissData(
2020-05-05 22:11:15 -06:00
name = s.name.some,
clock = s.clock,
startsAt = s.startsAt.some,
2020-05-11 12:16:18 -06:00
variant = s.variant.key.some,
2020-05-11 14:20:46 -06:00
rated = s.settings.rated.some,
2020-05-05 22:11:15 -06:00
nbRounds = s.settings.nbRounds,
description = s.settings.description,
position = s.settings.position,
2020-05-23 21:18:43 -06:00
chatFor = s.settings.chatFor.some,
2020-07-09 01:14:39 -06:00
roundInterval = s.settings.roundInterval.toSeconds.toInt.some,
2020-10-05 06:39:25 -06:00
password = s.settings.password,
conditions = SwissCondition.DataForm.AllSetup(s.settings.conditions),
forbiddenPairings = s.settings.forbiddenPairings.some.filter(_.nonEmpty)
2020-05-05 22:11:15 -06:00
)
2020-05-15 12:15:14 -06:00
def nextRound =
2020-05-15 12:15:14 -06:00
Form(
single(
"date" -> inTheFuture(ISODateTimeOrTimestamp.isoDateTimeOrTimestamp)
)
)
2020-04-29 10:31:34 -06:00
}
object SwissForm {
2020-04-29 11:43:52 -06:00
val clockLimits: Seq[Int] = Seq(0, 15, 30, 45, 60, 90) ++ {
2020-05-17 08:10:39 -06:00
(120 to 420 by 60) ++ (600 to 1800 by 300) ++ (2400 to 10800 by 600)
2020-04-29 11:43:52 -06:00
}
val clockLimitChoices = options(
clockLimits,
l => s"${chess.Clock.Config(l, 0).limitString}${if (l <= 1) " minute" else " minutes"}"
)
2020-04-29 10:31:34 -06:00
2020-05-05 22:11:15 -06:00
val roundIntervals: Seq[Int] =
2020-05-17 08:10:39 -06:00
Seq(
Swiss.RoundInterval.auto,
5,
10,
20,
30,
45,
60,
120,
180,
300,
600,
900,
1200,
1800,
2700,
3600,
24 * 3600,
2 * 24 * 3600,
7 * 24 * 3600,
Swiss.RoundInterval.manual
)
2020-05-05 19:26:39 -06:00
val roundIntervalChoices = options(
roundIntervals,
2020-05-15 12:15:14 -06:00
s =>
2020-05-23 21:18:43 -06:00
if (s == Swiss.RoundInterval.auto) s"Automatic"
2020-05-17 08:10:39 -06:00
else if (s == Swiss.RoundInterval.manual) s"Manually schedule each round"
2020-05-15 12:15:14 -06:00
else if (s < 60) s"$s seconds"
else if (s < 3600) s"${s / 60} minute(s)"
else if (s < 24 * 3600) s"${s / 3600} hour(s)"
else s"${s / 24 / 3600} days(s)"
2020-05-05 19:26:39 -06:00
)
2020-05-23 21:18:43 -06:00
val chatForChoices = List(
Swiss.ChatFor.NONE -> "No chat",
Swiss.ChatFor.LEADERS -> "Team leaders only",
Swiss.ChatFor.MEMBERS -> "Team members only",
Swiss.ChatFor.ALL -> "All Lichess players"
)
2020-04-29 10:31:34 -06:00
case class SwissData(
2020-04-29 11:43:52 -06:00
name: Option[String],
2020-04-29 10:31:34 -06:00
clock: ClockConfig,
2020-05-04 20:34:05 -06:00
startsAt: Option[DateTime],
2020-05-11 12:16:18 -06:00
variant: Option[String],
2020-05-11 14:20:46 -06:00
rated: Option[Boolean],
2020-04-29 10:31:34 -06:00
nbRounds: Int,
description: Option[String],
position: Option[FEN],
2020-05-23 21:18:43 -06:00
chatFor: Option[Int],
2020-07-09 01:14:39 -06:00
roundInterval: Option[Int],
2020-10-05 06:39:25 -06:00
password: Option[String],
conditions: SwissCondition.DataForm.AllSetup,
forbiddenPairings: Option[String]
2020-04-29 10:31:34 -06:00
) {
2020-05-17 08:10:39 -06:00
def realVariant = variant flatMap Variant.apply getOrElse Variant.default
def realStartsAt = startsAt | DateTime.now.plusMinutes(10)
2020-05-23 21:18:43 -06:00
def realChatFor = chatFor | Swiss.ChatFor.default
2021-08-06 01:56:39 -06:00
def realRoundInterval =
2020-05-17 08:10:39 -06:00
(roundInterval | Swiss.RoundInterval.auto) match {
2021-08-06 01:56:39 -06:00
case Swiss.RoundInterval.auto => autoInterval(clock)
case i => i.seconds
2020-05-17 08:10:39 -06:00
}
def realPosition = position ifTrue realVariant.standard
def isRated = rated | true
def validRatedVariant =
!isRated ||
lila.game.Game.allowRated(realVariant, clock.some)
2020-04-29 10:31:34 -06:00
}
2021-08-06 01:56:39 -06:00
def autoInterval(clock: ClockConfig) = {
import Speed._
Speed(clock) match {
case UltraBullet => 5
case Bullet => 10
case Blitz if clock.estimateTotalSeconds < 300 => 20
case Blitz => 30
case Rapid => 60
case _ => 300
}
}.seconds
val joinForm = Form(single("password" -> optional(nonEmptyText)))
2020-04-29 10:31:34 -06:00
}