avoid stalled tournament with only 2 players left

pull/83/head
Thibault Duplessis 2012-09-16 19:17:55 +02:00
parent f1c10f762c
commit 9258b9e575
10 changed files with 49 additions and 40 deletions

View File

@ -124,7 +124,7 @@ object Tournament extends LilaController {
def form = Auth { implicit ctx
me
Ok(html.tournament.form(forms.create))
Ok(html.tournament.form(forms.create, forms))
}
def create = AuthBody { implicit ctx
@ -132,7 +132,7 @@ object Tournament extends LilaController {
IOResult {
implicit val req = ctx.body
forms.create.bindFromRequest.fold(
err io(BadRequest(html.tournament.form(err))),
err io(BadRequest(html.tournament.form(err, forms))),
setup api.createTournament(setup, me) map { tour
Redirect(routes.Tournament.show(tour.id))
})

View File

@ -8,6 +8,7 @@ import akka.actor._
import play.api.libs.concurrent._
import play.api.Application
import play.api.Mode.Dev
import ui._
@ -167,6 +168,6 @@ object CoreEnv {
def apply(app: Application) = new CoreEnv(
app,
new Settings(app.configuration.underlying)
new Settings(app.configuration.underlying, app.mode == Dev)
)
}

View File

@ -4,7 +4,7 @@ package core
import com.typesafe.config.Config
import scalaz.{ Success, Failure }
final class Settings(config: Config) {
final class Settings(config: Config, val IsDev: Boolean) {
import config._

View File

@ -5,26 +5,48 @@ import play.api.data._
import play.api.data.Forms._
import play.api.data.validation.Constraints._
final class DataForm {
final class DataForm(isDev: Boolean) {
import lila.core.Form._
import Tournament._
val create = Form(mapping(
val clockTimes = 0 to 10 by 1
val clockTimeDefault = 2
val clockTimeChoices = options(clockTimes, "%d minute{s}")
val clockIncrements = 0 to 2 by 1
val clockIncrementDefault = 0
val clockIncrementChoices = options(clockIncrements, "%d second{s}")
val minutes = 10 to 60 by 5
val minuteDefault = 30
val minuteChoices = options(minutes, "%d minute{s}")
val minPlayers = isDev.fold(
(2 to 9) ++ (10 to 30 by 5),
(5 to 9) ++ (10 to 30 by 5)
)
val minPlayerDefault = 10
val minPlayerChoices = options(minPlayers, "%d player{s}")
lazy val create = Form(mapping(
"clockTime" -> numberIn(clockTimeChoices),
"clockIncrement" -> numberIn(clockIncrementChoices),
"minutes" -> numberIn(minuteChoices),
"minPlayers" -> numberIn(minPlayerChoices)
)(TournamentSetup.apply)(TournamentSetup.unapply)
.verifying("Invalid clock", _.validClock)
) fill TournamentSetup()
) fill TournamentSetup(
clockTime = clockTimeDefault,
clockIncrement = clockIncrementDefault,
minutes = minuteDefault,
minPlayers = minPlayerDefault)
}
case class TournamentSetup(
clockTime: Int = Tournament.clockTimeDefault,
clockIncrement: Int = Tournament.clockIncrementDefault,
minutes: Int = Tournament.minuteDefault,
minPlayers: Int = Tournament.minPlayerDefault) {
clockTime: Int,
clockIncrement: Int,
minutes: Int,
minPlayers: Int) {
def validClock = (clockTime + clockIncrement) > 0
}

View File

@ -71,7 +71,7 @@ final class Organizer(
(hubMaster ? GetTournamentUsernames(tour.id)).mapTo[Iterable[String]] onSuccess {
case usernames
(tour.activeUserIds intersect usernames.toList.map(_.toLowerCase)) |> { users
Pairing.createNewPairings(users, tour.pairings).toNel foreach { pairings
Pairing.createNewPairings(users, tour.pairings, tour.nbActiveUsers).toNel foreach { pairings
api.makePairings(tour, pairings).unsafePerformIO
}
}

View File

@ -66,7 +66,7 @@ object Pairing {
user2 = user2,
winner = none)
def createNewPairings(users: List[String], pairings: Pairings): Pairings =
def createNewPairings(users: List[String], pairings: Pairings, nbActiveUsers: Int): Pairings =
if (users.size < 2)
Nil
@ -79,7 +79,7 @@ object Pairing {
naivePairings(idles),
(idles.size > 12).fold(
naivePairings(idles),
smartPairings(idles, pairings)
smartPairings(idles, pairings, nbActiveUsers)
)
)
}
@ -89,7 +89,7 @@ object Pairing {
case List(u1, u2) Pairing(u1, u2)
} toList
private def smartPairings(users: List[String], pairings: Pairings): Pairings = {
private def smartPairings(users: List[String], pairings: Pairings, nbActiveUsers: Int): Pairings = {
def lastOpponent(user: String): Option[String] =
pairings find (_ contains user) flatMap (_ opponentOf user)
@ -109,6 +109,7 @@ object Pairing {
(users match {
case x if x.size < 2 Nil
case List(u1, u2) if nbActiveUsers == 2 List(u1 -> u2)
case List(u1, u2) if justPlayedTogether(u1, u2) Nil
case List(u1, u2) List(u1 -> u2)
case us allPairCombinations(us)

View File

@ -42,6 +42,7 @@ sealed trait Tournament {
def userIds = players map (_.id)
def activeUserIds = players filter (_.active) map (_.id)
def nbActiveUsers = players count (_.active)
def nbPlayers = players.size
def minPlayers = data.minPlayers
def playerRatio = "%d/%d".format(nbPlayers, minPlayers)
@ -255,20 +256,4 @@ object Tournament {
minPlayers = minPlayers),
players = List(Player(createdBy))
)
val clockTimes = 0 to 10 by 1
val clockTimeDefault = 2
val clockTimeChoices = options(clockTimes, "%d minute{s}")
val clockIncrements = 0 to 2 by 1
val clockIncrementDefault = 0
val clockIncrementChoices = options(clockIncrements, "%d second{s}")
val minutes = 10 to 60 by 5
val minuteDefault = 20
val minuteChoices = options(minutes, "%d minute{s}")
val minPlayers = (5 to 9) ++ (10 to 30 by 5)
val minPlayerDefault = 10
val minPlayerChoices = options(minPlayers, "%d player{s}")
}

View File

@ -29,7 +29,7 @@ final class TournamentEnv(
implicit val ctx = app
import settings._
lazy val forms = new DataForm
lazy val forms = new DataForm(IsDev)
lazy val repo = new TournamentRepo(
collection = mongodb(TournamentCollectionTournament))

View File

@ -1,6 +1,6 @@
@(form: Form[_])(implicit ctx: Context)
@(form: Form[_], config: lila.tournament.DataForm)(implicit ctx: Context)
@import lila.tournament.Tournament
@import config._
@tournament.layout(
title = "New tournament") {
@ -17,7 +17,7 @@ title = "New tournament") {
<label for="@form("clockTime").id">Clock time</label>
</th>
<td>
@base.select(form("clockTime"), Tournament.clockTimeChoices)
@base.select(form("clockTime"), config.clockTimeChoices)
</td>
</tr>
<tr>
@ -25,7 +25,7 @@ title = "New tournament") {
<label for="@form("clockIncrement").id">Clock increment</label>
</th>
<td>
@base.select(form("clockIncrement"), Tournament.clockIncrementChoices)
@base.select(form("clockIncrement"), config.clockIncrementChoices)
</td>
</tr>
<tr>
@ -33,7 +33,7 @@ title = "New tournament") {
<label for="@form("minutes").id">Duration</label>
</th>
<td>
@base.select(form("minutes"), Tournament.minuteChoices)
@base.select(form("minutes"), config.minuteChoices)
</td>
</tr>
<tr>
@ -41,7 +41,7 @@ title = "New tournament") {
<label for="@form("minPlayers").id">Players</label>
</th>
<td>
@base.select(form("minPlayers"), Tournament.minPlayerChoices)
@base.select(form("minPlayers"), config.minPlayerChoices)
</td>
</tr>
<tr>

2
todo
View File

@ -48,5 +48,5 @@ finish games when tournament is over
add opera to list of supported browsers (with websocket trick)
tournament ties
tournament detect leavers and withdraw them (started) (also use force resign)
tournament stalled with 2 players http://en.lichess.org/tournament/6ad411tf
tournament end when all but one players resigned
send lobby new forum posts html through websockets