lila/modules/challenge/src/main/Challenge.scala

162 lines
4.8 KiB
Scala

package lila.challenge
import chess.variant.{ Variant, FromPosition }
import chess.{ Mode, Clock, Speed }
import org.joda.time.DateTime
import lila.game.PerfPicker
import lila.rating.PerfType
import lila.user.User
case class Challenge(
_id: String,
status: Challenge.Status,
variant: Variant,
initialFen: Option[String],
timeControl: Challenge.TimeControl,
mode: Mode,
colorChoice: Challenge.ColorChoice,
finalColor: chess.Color,
challenger: EitherChallenger,
destUser: Option[Challenge.Registered],
rematchOf: Option[String],
createdAt: DateTime,
seenAt: DateTime,
expiresAt: DateTime) {
import Challenge._
def id = _id
def challengerUser = challenger.right.toOption
def challengerUserId = challengerUser.map(_.id)
def challengerIsAnon = challenger.isLeft
def destUserId = destUser.map(_.id)
def userIds = List(challengerUserId, destUserId).flatten
def daysPerTurn = timeControl match {
case TimeControl.Correspondence(d) => d.some
case _ => none
}
def unlimited = timeControl == TimeControl.Unlimited
def clock = timeControl match {
case c: TimeControl.Clock => c.some
case _ => none
}
def hasClock = clock.isDefined
def openDest = destUser.isEmpty
def active = status == Status.Created || status == Status.Offline
def declined = status == Status.Declined
def accepted = status == Status.Accepted
lazy val perfType = perfTypeOf(variant, timeControl)
}
object Challenge {
type ID = String
sealed abstract class Status(val id: Int) {
val name = toString.toLowerCase
}
object Status {
case object Created extends Status(10)
case object Offline extends Status(15)
case object Canceled extends Status(20)
case object Declined extends Status(30)
case object Accepted extends Status(40)
val all = List(Created, Offline, Canceled, Declined, Accepted)
def apply(id: Int): Option[Status] = all.find(_.id == id)
}
case class Rating(int: Int, provisional: Boolean) {
def show = s"$int${provisional.fold("?", "")}"
}
object Rating {
def apply(p: lila.rating.Perf): Rating = Rating(p.intRating, p.provisional)
}
case class Registered(id: User.ID, rating: Rating)
case class Anonymous(secret: String)
sealed trait TimeControl
object TimeControl {
case object Unlimited extends TimeControl
case class Correspondence(days: Int) extends TimeControl
case class Clock(config: chess.Clock.Config) extends TimeControl {
// All durations are expressed in seconds
def limit = config.limit
def increment = config.increment
def show = config.show
}
}
sealed trait ColorChoice
object ColorChoice {
case object Random extends ColorChoice
case object White extends ColorChoice
case object Black extends ColorChoice
}
private def speedOf(timeControl: TimeControl) = timeControl match {
case TimeControl.Clock(config) => Speed(config)
case _ => Speed.Correspondence
}
private def perfTypeOf(variant: Variant, timeControl: TimeControl): PerfType =
PerfPicker.perfType(speedOf(timeControl), variant, timeControl match {
case TimeControl.Correspondence(d) => d.some
case _ => none
}).orElse {
(variant == FromPosition) option perfTypeOf(chess.variant.Standard, timeControl)
}.|(PerfType.Correspondence)
private val idSize = 8
private def randomId = ornicar.scalalib.Random nextString idSize
private def toRegistered(variant: Variant, timeControl: TimeControl)(u: User) =
Registered(u.id, Rating(u.perfs(perfTypeOf(variant, timeControl))))
def make(
variant: Variant,
initialFen: Option[String],
timeControl: TimeControl,
mode: Mode,
color: String,
challenger: Either[String, User],
destUser: Option[User],
rematchOf: Option[String]): Challenge = {
val (colorChoice, finalColor) = color match {
case "white" => ColorChoice.White -> chess.White
case "black" => ColorChoice.Black -> chess.Black
case _ => ColorChoice.Random -> chess.Color(scala.util.Random.nextBoolean)
}
new Challenge(
_id = randomId,
status = Status.Created,
variant = variant,
initialFen = (variant == FromPosition).fold(
initialFen,
Some(variant.initialFen).ifFalse(variant.standardInitialPosition)
),
timeControl = timeControl,
mode = mode,
colorChoice = colorChoice,
finalColor = finalColor,
challenger = challenger.fold[EitherChallenger](
sid => Left(Anonymous(sid)),
u => Right(toRegistered(variant, timeControl)(u))
),
destUser = destUser map toRegistered(variant, timeControl),
rematchOf = rematchOf,
createdAt = DateTime.now,
seenAt = DateTime.now,
expiresAt = inTwoWeeks)
}
}