chess960 and rated tournaments
This commit is contained in:
parent
c8b2a450c1
commit
a52ac0d8ec
|
@ -15,11 +15,11 @@ trait SetupHelper { self: I18nHelper ⇒
|
|||
|
||||
def translatedVariantChoices(implicit ctx: Context) = List(
|
||||
Variant.Standard.id.toString -> trans.standard.str(),
|
||||
Variant.Chess960.id.toString -> StringHelper.ucFirst(Variant.Chess960.name)
|
||||
Variant.Chess960.id.toString -> Variant.Chess960.name.capitalize
|
||||
)
|
||||
|
||||
def translatedSpeedChoices(implicit ctx: Context) = Speed.all map { s ⇒
|
||||
s.id.toString -> (s.toString + " - " + StringHelper.ucFirst(s.name))
|
||||
s.id.toString -> (s.toString + " - " + s.name.capitalize)
|
||||
}
|
||||
|
||||
def eloDiffChoices(elo: Int)(implicit ctx: Context) = FilterConfig.eloDiffs map { diff ⇒
|
||||
|
|
|
@ -11,8 +11,6 @@ object StringHelper extends StringHelper
|
|||
|
||||
trait StringHelper {
|
||||
|
||||
def ucFirst(str: String) = ~(str lift 0).map(_.toUpper) + (str drop 1)
|
||||
|
||||
def slugify(input: String) = {
|
||||
val nowhitespace = input.trim.replace(" ", "-")
|
||||
val normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package lila
|
||||
package tournament
|
||||
|
||||
import chess.{ Mode, Variant }
|
||||
import lila.setup.Mappings
|
||||
|
||||
import play.api.data._
|
||||
import play.api.data.Forms._
|
||||
import play.api.data.validation.Constraints._
|
||||
|
@ -35,7 +38,9 @@ final class DataForm(isDev: Boolean) {
|
|||
"clockTime" -> numberIn(clockTimeChoices),
|
||||
"clockIncrement" -> numberIn(clockIncrementChoices),
|
||||
"minutes" -> numberIn(minuteChoices),
|
||||
"minPlayers" -> numberIn(minPlayerChoices)
|
||||
"minPlayers" -> numberIn(minPlayerChoices),
|
||||
"variant" -> Mappings.variant,
|
||||
"mode" -> Mappings.mode(true)
|
||||
)(TournamentSetup.apply)(TournamentSetup.unapply)
|
||||
.verifying("Invalid clock", _.validClock)
|
||||
.verifying("Increase tournament duration, or decrease game clock", _.validTiming)
|
||||
|
@ -43,14 +48,18 @@ final class DataForm(isDev: Boolean) {
|
|||
clockTime = clockTimeDefault,
|
||||
clockIncrement = clockIncrementDefault,
|
||||
minutes = minuteDefault,
|
||||
minPlayers = minPlayerDefault)
|
||||
minPlayers = minPlayerDefault,
|
||||
variant = Variant.Standard.id,
|
||||
mode = Mode.Casual.id.some)
|
||||
}
|
||||
|
||||
private[tournament] case class TournamentSetup(
|
||||
clockTime: Int,
|
||||
clockIncrement: Int,
|
||||
minutes: Int,
|
||||
minPlayers: Int) {
|
||||
minPlayers: Int,
|
||||
variant: Int,
|
||||
mode: Option[Int]) {
|
||||
|
||||
def validClock = (clockTime + clockIncrement) > 0
|
||||
|
||||
|
|
|
@ -22,18 +22,17 @@ final class GameJoiner(
|
|||
def apply(tour: Started)(pairing: Pairing): IO[DbGame] = for {
|
||||
user1 ← getUser(pairing.user1) map (_ err "No such user " + pairing)
|
||||
user2 ← getUser(pairing.user2) map (_ err "No such user " + pairing)
|
||||
variant = chess.Variant.Standard
|
||||
game = DbGame(
|
||||
game = chess.Game(
|
||||
board = chess.Board init variant,
|
||||
board = chess.Board init tour.variant,
|
||||
clock = tour.clock.chessClock.some
|
||||
),
|
||||
ai = None,
|
||||
whitePlayer = DbPlayer.white withUser user1,
|
||||
blackPlayer = DbPlayer.black withUser user2,
|
||||
creatorColor = chess.Color.White,
|
||||
mode = chess.Mode.Casual,
|
||||
variant = variant
|
||||
mode = tour.mode,
|
||||
variant = tour.variant
|
||||
).withTournamentId(tour.id)
|
||||
.withId(pairing.gameId)
|
||||
.start
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.mongodb.casbah.query.Imports.DBObject
|
|||
import ornicar.scalalib.Random
|
||||
import scalaz.NonEmptyList
|
||||
|
||||
import chess.{ Variant, Mode }
|
||||
import user.User
|
||||
import game.PovRef
|
||||
|
||||
|
@ -16,6 +17,8 @@ case class Data(
|
|||
clock: TournamentClock,
|
||||
minutes: Int,
|
||||
minPlayers: Int,
|
||||
variant: Variant,
|
||||
mode: Mode,
|
||||
createdAt: DateTime,
|
||||
createdBy: String)
|
||||
|
||||
|
@ -40,6 +43,10 @@ sealed trait Tournament {
|
|||
def minutes = data.minutes
|
||||
lazy val duration = new Duration(minutes * 60 * 1000)
|
||||
|
||||
def variant = data.variant
|
||||
def mode = data.mode
|
||||
def rated = mode.rated
|
||||
|
||||
def userIds = players map (_.id)
|
||||
def activeUserIds = players filter (_.active) map (_.id)
|
||||
def nbActiveUsers = players count (_.active)
|
||||
|
@ -79,6 +86,8 @@ sealed trait StartedOrFinished extends Tournament {
|
|||
clock = data.clock,
|
||||
minutes = data.minutes,
|
||||
minPlayers = data.minPlayers,
|
||||
variant = data.variant.id,
|
||||
mode = data.mode.id,
|
||||
createdAt = data.createdAt,
|
||||
createdBy = data.createdBy,
|
||||
startedAt = startedAt.some,
|
||||
|
@ -112,6 +121,8 @@ case class Created(
|
|||
status = Status.Created.id,
|
||||
name = data.name,
|
||||
clock = data.clock,
|
||||
variant = data.variant.id,
|
||||
mode = data.mode.id,
|
||||
minutes = data.minutes,
|
||||
minPlayers = data.minPlayers,
|
||||
createdAt = data.createdAt,
|
||||
|
@ -214,11 +225,13 @@ case class RawTournament(
|
|||
status: Int,
|
||||
startedAt: Option[DateTime] = None,
|
||||
players: List[Player] = Nil,
|
||||
pairings: List[RawPairing] = Nil) {
|
||||
pairings: List[RawPairing] = Nil,
|
||||
variant: Int = Variant.Standard.id,
|
||||
mode: Int = Mode.Casual.id) {
|
||||
|
||||
def created: Option[Created] = (status == Status.Created.id) option Created(
|
||||
id = id,
|
||||
data = Data(name, clock, minutes, minPlayers, createdAt, createdBy),
|
||||
data = Data(name, clock, minutes, minPlayers, Variant orDefault variant, Mode orDefault mode, createdAt, createdBy),
|
||||
players = players)
|
||||
|
||||
def started: Option[Started] = for {
|
||||
|
@ -226,7 +239,7 @@ case class RawTournament(
|
|||
if status == Status.Started.id
|
||||
} yield Started(
|
||||
id = id,
|
||||
data = Data(name, clock, minutes, minPlayers, createdAt, createdBy),
|
||||
data = Data(name, clock, minutes, minPlayers, Variant orDefault variant, Mode orDefault mode, createdAt, createdBy),
|
||||
startedAt = stAt,
|
||||
players = players,
|
||||
pairings = decodePairings)
|
||||
|
@ -236,7 +249,7 @@ case class RawTournament(
|
|||
if status == Status.Finished.id
|
||||
} yield Finished(
|
||||
id = id,
|
||||
data = Data(name, clock, minutes, minPlayers, createdAt, createdBy),
|
||||
data = Data(name, clock, minutes, minPlayers, Variant orDefault variant, Mode orDefault mode, createdAt, createdBy),
|
||||
startedAt = stAt,
|
||||
players = players,
|
||||
pairings = decodePairings)
|
||||
|
@ -258,13 +271,17 @@ object Tournament {
|
|||
createdBy: User,
|
||||
clock: TournamentClock,
|
||||
minutes: Int,
|
||||
minPlayers: Int): Created = Created(
|
||||
minPlayers: Int,
|
||||
variant: Variant,
|
||||
mode: Mode): Created = Created(
|
||||
id = Random nextString 8,
|
||||
data = Data(
|
||||
name = RandomName(),
|
||||
clock = clock,
|
||||
createdBy = createdBy.id,
|
||||
createdAt = DateTime.now,
|
||||
variant = variant,
|
||||
mode = mode,
|
||||
minutes = minutes,
|
||||
minPlayers = minPlayers),
|
||||
players = List(Player(createdBy))
|
||||
|
|
|
@ -7,6 +7,7 @@ import scalaz.effects._
|
|||
import scalaz.{ NonEmptyList, Success, Failure }
|
||||
import play.api.libs.json._
|
||||
|
||||
import chess.{ Mode, Variant }
|
||||
import controllers.routes
|
||||
import game.DbGame
|
||||
import user.User
|
||||
|
@ -37,7 +38,9 @@ private[tournament] final class TournamentApi(
|
|||
createdBy = me,
|
||||
clock = TournamentClock(setup.clockTime * 60, setup.clockIncrement),
|
||||
minutes = setup.minutes,
|
||||
minPlayers = setup.minPlayers)
|
||||
minPlayers = setup.minPlayers,
|
||||
mode = Mode orDefault ~setup.mode,
|
||||
variant = Variant orDefault setup.variant)
|
||||
_ ← repo saveIO created
|
||||
_ ← (withdrawIds map socket.reload).sequence
|
||||
_ ← reloadSiteSocket
|
||||
|
@ -50,12 +53,8 @@ private[tournament] final class TournamentApi(
|
|||
def earlyStart(created: Created): Option[IO[Unit]] =
|
||||
created.readyToEarlyStart option doStart(created.start)
|
||||
|
||||
private def doStart(started: Started): IO[Unit] = for {
|
||||
_ ← repo saveIO started
|
||||
_ ← socket start started.id
|
||||
_ ← reloadSiteSocket
|
||||
_ ← lobbyReload
|
||||
} yield ()
|
||||
private def doStart(started: Started): IO[Unit] =
|
||||
(repo saveIO started) >> (socket start started.id) >> reloadSiteSocket >> lobbyReload
|
||||
|
||||
def wipeEmpty(created: Created): IO[Unit] = (for {
|
||||
_ ← repo removeIO created
|
||||
|
|
|
@ -10,6 +10,14 @@
|
|||
@tours.map { tour =>
|
||||
<tr>
|
||||
<td>@tournament.linkTo(tour)</td>
|
||||
<td class="small">
|
||||
@if(tour.variant.exotic) {
|
||||
960
|
||||
}
|
||||
@if(tour.rated) {
|
||||
rated
|
||||
}
|
||||
</td>
|
||||
<td>@tour.clock.show</td>
|
||||
<td>@{tour.minutes}m</td>
|
||||
<td>@tour.playerRatio</td>
|
||||
|
|
|
@ -8,7 +8,7 @@ You will be notified when the tournament starts, so it is safe to
|
|||
<a target="_blank" href="@routes.Lobby.home()">play in another tab</a> while waiting.
|
||||
|
||||
## Is it rated?
|
||||
No, tournament games won't impact your ELO.
|
||||
Some tournaments are rated and will affect your ELO.
|
||||
|
||||
## How to win?
|
||||
The players with the more points at the end of the tournament duration wins.
|
||||
|
|
|
@ -12,6 +12,22 @@ title = "New tournament") {
|
|||
<p class="error">@error.message</p>
|
||||
}
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="@form("variant").id">@trans.variant()</label>
|
||||
</th>
|
||||
<td>
|
||||
@base.select(form("variant"), translatedVariantChoices)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="@form("mode").id">@trans.mode()</label>
|
||||
</th>
|
||||
<td>
|
||||
@base.select(form("mode"), translatedModeChoices)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label>Game clock</label>
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th class="large">Open tournaments</th>
|
||||
<th>Clock</th>
|
||||
<th>Duration</th>
|
||||
<th>@trans.mode()</th>
|
||||
<th>@trans.timeControl()</th>
|
||||
<th colspan="2">Players</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -15,8 +15,15 @@
|
|||
@createds.map { tour =>
|
||||
<tr>
|
||||
<td>@linkTo(tour)</td>
|
||||
<td>@tour.clock.show</td>
|
||||
<td>@{tour.minutes}m</td>
|
||||
<td class="small">
|
||||
@if(tour.variant.exotic) {
|
||||
960
|
||||
}
|
||||
@if(tour.rated) {
|
||||
@trans.rated()
|
||||
}
|
||||
</td>
|
||||
<td>@tour.clock.show | @{tour.minutes}m</td>
|
||||
<td>@tour.playerRatio</td>
|
||||
<td>
|
||||
@ctx.me.map { me =>
|
||||
|
@ -43,8 +50,8 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th class="large">Now Playing</th>
|
||||
<th>Clock</th>
|
||||
<th>Duration</th>
|
||||
<th>@trans.mode()</th>
|
||||
<th>@trans.timeControl()</th>
|
||||
<th>Players</th>
|
||||
<th>Leader</th>
|
||||
</tr>
|
||||
|
@ -53,8 +60,15 @@
|
|||
@starteds.map { tour =>
|
||||
<tr>
|
||||
<td>@linkTo(tour)</td>
|
||||
<td>@tour.clock.show</td>
|
||||
<td>@{tour.minutes}m</td>
|
||||
<td class="small">
|
||||
@if(tour.variant.exotic) {
|
||||
960
|
||||
}
|
||||
@if(tour.rated) {
|
||||
@trans.rated()
|
||||
}
|
||||
</td>
|
||||
<td>@tour.clock.show | @{tour.minutes}m</td>
|
||||
<td>@tour.playerRatio</td>
|
||||
<td>@tour.winner.map { player =>
|
||||
@userInfosLink(player.username, player.elo.some, withOnline = false)
|
||||
|
@ -67,8 +81,8 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th class="large">Finished</th>
|
||||
<th>Clock</th>
|
||||
<th>Duration</th>
|
||||
<th>@trans.mode()</th>
|
||||
<th>@trans.timeControl()</th>
|
||||
<th>Players</th>
|
||||
<th>Winner</th>
|
||||
</tr>
|
||||
|
@ -77,8 +91,15 @@
|
|||
@finisheds.map { tour =>
|
||||
<tr>
|
||||
<td>@linkTo(tour)</td>
|
||||
<td>@tour.clock.show</td>
|
||||
<td>@{tour.minutes}m</td>
|
||||
<td class="small">
|
||||
@if(tour.variant.exotic) {
|
||||
960
|
||||
}
|
||||
@if(tour.rated) {
|
||||
@trans.rated()
|
||||
}
|
||||
</td>
|
||||
<td>@tour.clock.show | @{tour.minutes}m</td>
|
||||
<td>@tour.playerRatio</td>
|
||||
<td>@tour.winner.map { player =>
|
||||
@userInfosLink(player.username, player.elo.some, withOnline = false)
|
||||
|
|
|
@ -6,15 +6,10 @@
|
|||
<br />
|
||||
Created by @userIdLink(tour.createdBy.some, withOnline = false)
|
||||
<br /><br />
|
||||
@tour.playerRatio players,
|
||||
@if(false) {
|
||||
rated
|
||||
} else {
|
||||
casual
|
||||
}
|
||||
<span class="s16 clock">@tour.clock.show</span>,
|
||||
@variantName(tour.variant).capitalize,
|
||||
@{ tour.rated.fold(trans.rated(), trans.casual()) }
|
||||
<br /><br />
|
||||
<span class="s16 clock">@tour.clock.show</span>
|
||||
<br />
|
||||
Duration: @tour.minutes minutes
|
||||
@if(tour.isRunning && (tour isActive ctx.me)) {
|
||||
<br /><br />
|
||||
|
|
|
@ -42,6 +42,9 @@
|
|||
#tournament table.slist tbody span.winner {
|
||||
background-position: right -304px;
|
||||
}
|
||||
#tournament table.slist .small {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
#tournament form.inline {
|
||||
display: inline;
|
||||
|
|
Loading…
Reference in a new issue