lila/modules/tournament/src/main/Schedule.scala

287 lines
9.8 KiB
Scala

package lila.tournament
import chess.StartingPosition
import chess.variant.Variant
import org.joda.time.DateTime
import lila.rating.PerfType
case class Schedule(
freq: Schedule.Freq,
speed: Schedule.Speed,
variant: Variant,
position: StartingPosition,
at: DateTime,
conditions: Condition.All = Condition.All.empty
) {
def name = freq match {
case m @ Schedule.Freq.ExperimentalMarathon => m.name
case _ if variant.standard && position.initial =>
(conditions.minRating, conditions.maxRating) match {
case (None, None) => s"${freq.toString} ${speed.toString}"
case (Some(min), _) => s"Elite ${speed.toString}"
case (_, Some(max)) => s"U${max.rating} ${speed.toString}"
}
case _ if variant.standard => s"${position.shortName} ${speed.toString}"
case Schedule.Freq.Hourly => s"${variant.name} ${speed.toString}"
case _ => s"${freq.toString} ${variant.name}"
}
def day = at.withTimeAtStartOfDay
def sameSpeed(other: Schedule) = speed == other.speed
def similarSpeed(other: Schedule) = Schedule.Speed.similar(speed, other.speed)
def sameVariant(other: Schedule) = variant.id == other.variant.id
def sameVariantAndSpeed(other: Schedule) = sameVariant(other) && sameSpeed(other)
def sameFreq(other: Schedule) = freq == other.freq
def sameConditions(other: Schedule) = conditions == other.conditions
def sameMaxRating(other: Schedule) = conditions sameMaxRating other.conditions
def similarConditions(other: Schedule) = conditions similar other.conditions
def sameDay(other: Schedule) = day == other.day
def hasMaxRating = conditions.maxRating.isDefined
def similarTo(other: Schedule) =
similarSpeed(other) && sameVariant(other) && sameFreq(other) && sameConditions(other)
def perfType = PerfType.byVariant(variant) | Schedule.Speed.toPerfType(speed)
def plan = Schedule.Plan(this, None)
def plan(build: Tournament => Tournament) = Schedule.Plan(this, build.some)
override def toString = s"$freq $variant $speed $conditions $at"
}
object Schedule {
case class Plan(schedule: Schedule, buildFunc: Option[Tournament => Tournament]) {
def build: Tournament = {
val t = Tournament.schedule(addCondition(schedule), durationFor(schedule))
buildFunc.foldRight(t) { _(_) }
}
def map(f: Tournament => Tournament) = copy(
buildFunc = buildFunc.fold(f)(f.compose).some
)
}
sealed abstract class Freq(val id: Int, val importance: Int) extends Ordered[Freq] {
val name = toString.toLowerCase
def compare(other: Freq) = Integer.compare(importance, other.importance)
def isDaily = this == Schedule.Freq.Daily
def isDailyOrBetter = this >= Schedule.Freq.Daily
def isWeeklyOrBetter = this >= Schedule.Freq.Weekly
}
object Freq {
case object Hourly extends Freq(10, 10)
case object Daily extends Freq(20, 20)
case object Eastern extends Freq(30, 15)
case object Weekly extends Freq(40, 40)
case object Weekend extends Freq(41, 41)
case object Monthly extends Freq(50, 50)
case object Shield extends Freq(51, 51)
case object Marathon extends Freq(60, 60)
case object ExperimentalMarathon extends Freq(61, 55) { // for DB BC
override val name = "Experimental Marathon"
}
case object Yearly extends Freq(70, 70)
case object Unique extends Freq(90, 59)
val all: List[Freq] = List(Hourly, Daily, Eastern, Weekly, Weekend, Monthly, Shield, Marathon, ExperimentalMarathon, Yearly, Unique)
def apply(name: String) = all.find(_.name == name)
def byId(id: Int) = all.find(_.id == id)
}
sealed abstract class Speed(val id: Int) {
def name = toString.toLowerCase
}
object Speed {
case object UltraBullet extends Speed(5)
case object HyperBullet extends Speed(10)
case object Bullet extends Speed(20)
case object HippoBullet extends Speed(25)
case object SuperBlitz extends Speed(30)
case object Blitz extends Speed(40)
case object Rapid extends Speed(50)
case object Classical extends Speed(60)
val all: List[Speed] = List(UltraBullet, HyperBullet, Bullet, HippoBullet, SuperBlitz, Blitz, Rapid, Classical)
val mostPopular: List[Speed] = List(Bullet, Blitz, Rapid, Classical)
def apply(name: String) = all find (_.name == name)
def byId(id: Int) = all find (_.id == id)
def similar(s1: Speed, s2: Speed) = (s1, s2) match {
case (a, b) if a == b => true
case (HyperBullet, Bullet) | (Bullet, HyperBullet) => true
case (Bullet, HippoBullet) | (HippoBullet, Bullet) => true
case _ => false
}
def fromClock(clock: chess.Clock.Config) = {
val time = clock.estimateTotalSeconds
if (time < 30) UltraBullet
else if (time < 60) HyperBullet
else if (time < 120) Bullet
else if (time < 180) HippoBullet
else if (time < 480) Blitz
else if (time < 1500) Rapid
else Classical
}
def toPerfType(speed: Speed) = speed match {
case UltraBullet => PerfType.UltraBullet
case HyperBullet | Bullet | HippoBullet => PerfType.Bullet
case SuperBlitz | Blitz => PerfType.Blitz
case Rapid => PerfType.Rapid
case Classical => PerfType.Classical
}
}
sealed trait Season
object Season {
case object Spring extends Season
case object Summer extends Season
case object Autumn extends Season
case object Winter extends Season
}
private[tournament] def durationFor(s: Schedule): Int = {
import Freq._, Speed._
import chess.variant._
(s.freq, s.variant, s.speed) match {
case (Hourly, _, UltraBullet | HyperBullet | Bullet) => 27
case (Hourly, _, HippoBullet | SuperBlitz | Blitz) => 57
case (Hourly, _, Rapid) if s.hasMaxRating => 57
case (Hourly, _, Rapid | Classical) => 117
case (Daily | Eastern, Standard, SuperBlitz) => 90
case (Daily | Eastern, Standard, Blitz) => 120
case (Daily | Eastern, _, Blitz) => 90
case (Daily | Eastern, _, Rapid | Classical) => 150
case (Daily | Eastern, _, _) => 60
case (Weekly, _, UltraBullet | HyperBullet | Bullet) => 60 * 2
case (Weekly, _, HippoBullet | SuperBlitz | Blitz) => 60 * 3
case (Weekly, _, Rapid) => 60 * 4
case (Weekly, _, Classical) => 60 * 5
case (Weekend, _, UltraBullet | HyperBullet | Bullet) => 90
case (Weekend, _, HippoBullet | SuperBlitz) => 60 * 2
case (Weekend, _, Blitz) => 60 * 3
case (Weekend, _, Rapid) => 60 * 4
case (Weekend, _, Classical) => 60 * 5
case (Monthly, _, UltraBullet) => 60 * 2
case (Monthly, _, HyperBullet | Bullet) => 60 * 3
case (Monthly, _, HippoBullet | SuperBlitz) => 60 * 3 + 30
case (Monthly, _, Blitz) => 60 * 4
case (Monthly, _, Rapid) => 60 * 5
case (Monthly, _, Classical) => 60 * 6
case (Shield, _, UltraBullet) => 60 * 3
case (Shield, _, HyperBullet | Bullet) => 60 * 4
case (Shield, _, HippoBullet | SuperBlitz) => 60 * 5
case (Shield, _, Blitz) => 60 * 6
case (Shield, _, Rapid) => 60 * 8
case (Shield, _, Classical) => 60 * 10
case (Yearly, _, UltraBullet | HyperBullet | Bullet) => 60 * 4
case (Yearly, _, HippoBullet | SuperBlitz) => 60 * 5
case (Yearly, _, Blitz) => 60 * 6
case (Yearly, _, Rapid) => 60 * 8
case (Yearly, _, Classical) => 60 * 10
case (Marathon, _, _) => 60 * 24 // lol
case (ExperimentalMarathon, _, _) => 60 * 4
case (Unique, _, _) => 60 * 6
}
}
private val standardIncHours = Set(1, 7, 13, 19)
private def standardInc(s: Schedule) = standardIncHours(s.at.getHourOfDay)
private def zhInc(s: Schedule) = s.at.getHourOfDay % 2 == 0
private[tournament] def clockFor(s: Schedule) = {
import Freq._, Speed._
import chess.variant._
val TC = chess.Clock.Config
(s.freq, s.variant, s.speed) match {
// Special cases.
case (Hourly, Crazyhouse, SuperBlitz) if zhInc(s) => TC(3 * 60, 1)
case (Hourly, Crazyhouse, Blitz) if zhInc(s) => TC(4 * 60, 2)
case (Hourly, Standard, Blitz) if standardInc(s) => TC(3 * 60, 2)
case (Shield, variant, Blitz) if variant.exotic => TC(3 * 60, 2)
case (_, _, UltraBullet) => TC(15, 0)
case (_, _, HyperBullet) => TC(30, 0)
case (_, _, Bullet) => TC(60, 0)
case (_, _, HippoBullet) => TC(2 * 60, 0)
case (_, _, SuperBlitz) => TC(3 * 60, 0)
case (_, _, Blitz) => TC(5 * 60, 0)
case (_, _, Rapid) => TC(10 * 60, 0)
case (_, _, Classical) => TC(20 * 60, 10)
}
}
private[tournament] def addCondition(s: Schedule) =
s.copy(conditions = conditionFor(s))
private[tournament] def conditionFor(s: Schedule) =
if (s.conditions.relevant) s.conditions
else {
import Freq._, Speed._
val nbRatedGame = (s.freq, s.speed) match {
case (_, UltraBullet) => 0
case (Hourly, UltraBullet | HyperBullet | Bullet) => 20
case (Hourly, HippoBullet | SuperBlitz | Blitz) => 15
case (Hourly, Rapid) => 10
case (Daily | Eastern, UltraBullet | HyperBullet | Bullet) => 20
case (Daily | Eastern, HippoBullet | SuperBlitz | Blitz) => 15
case (Daily | Eastern, Rapid) => 15
case (Weekly | Monthly | Shield, UltraBullet | HyperBullet | Bullet) => 30
case (Weekly | Monthly | Shield, HippoBullet | SuperBlitz | Blitz) => 20
case (Weekly | Monthly | Shield, Rapid) => 15
case (Weekend, UltraBullet | HyperBullet | Bullet) => 30
case (Weekend, HippoBullet | SuperBlitz | Blitz) => 20
case _ => 0
}
val minRating = s.freq match {
case Weekend => 2200
case _ => 0
}
Condition.All(
nbRatedGame = nbRatedGame.some.filter(0<).map {
Condition.NbRatedGame(s.perfType.some, _)
},
minRating = minRating.some.filter(0<).map {
Condition.MinRating(s.perfType, _)
},
maxRating = none,
titled = none,
teamMember = none
)
}
}