big tournament refactoring
This commit is contained in:
parent
99610be216
commit
f2ba016161
|
@ -41,7 +41,7 @@ object Tournament extends LilaController {
|
|||
|
||||
private def showCreated(tour: Created)(implicit ctx: Context) = for {
|
||||
roomHtml ← messenger render tour
|
||||
users ← userRepo byIds tour.data.users
|
||||
users ← userRepo byIds tour.userIds
|
||||
} yield html.tournament.show.created(
|
||||
tour = tour,
|
||||
roomHtml = Html(roomHtml),
|
||||
|
@ -69,12 +69,10 @@ object Tournament extends LilaController {
|
|||
def join(id: String) = Auth { implicit ctx ⇒
|
||||
implicit me ⇒
|
||||
IOptionIORedirect(repo createdById id) { tour ⇒
|
||||
api.join(tour, me) map { result ⇒
|
||||
result.fold(
|
||||
err ⇒ { println(err.shows); routes.Tournament.home() },
|
||||
_ ⇒ routes.Tournament.show(tour.id)
|
||||
)
|
||||
}
|
||||
api.join(tour, me).fold(
|
||||
err ⇒ putStrLn(err.shows) map (_ ⇒ routes.Tournament.home()),
|
||||
res ⇒ res map (_ ⇒ routes.Tournament.show(tour.id))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,15 +84,33 @@ object Tournament extends LilaController {
|
|||
}
|
||||
|
||||
def reload(id: String) = Open { implicit ctx ⇒
|
||||
IOptionIOk(repo startedById id) { tour ⇒
|
||||
gameRepo.recentTournamentGames(tour.id, 4) map { games ⇒
|
||||
val pairings = html.tournament.pairings(tour)
|
||||
val inner = html.tournament.show.startedInner(tour, games)
|
||||
html.tournament.show.inner(pairings.some)(inner)
|
||||
}
|
||||
IOptionIOk(repo byId id) {
|
||||
case tour: Created ⇒ reloadCreated(tour)
|
||||
case tour: Started ⇒ reloadStarted(tour)
|
||||
case tour: Finished ⇒ reloadFinished(tour)
|
||||
}
|
||||
}
|
||||
|
||||
private def reloadCreated(tour: Created)(implicit ctx: Context) =
|
||||
userRepo byIds tour.userIds map { users ⇒
|
||||
val inner = html.tournament.show.createdInner(tour, users)
|
||||
html.tournament.show.inner(none)(inner)
|
||||
}
|
||||
|
||||
private def reloadStarted(tour: Started)(implicit ctx: Context) =
|
||||
gameRepo.recentTournamentGames(tour.id, 4) map { games ⇒
|
||||
val pairings = html.tournament.pairings(tour)
|
||||
val inner = html.tournament.show.startedInner(tour, games)
|
||||
html.tournament.show.inner(pairings.some)(inner)
|
||||
}
|
||||
|
||||
private def reloadFinished(tour: Finished)(implicit ctx: Context) =
|
||||
gameRepo.recentTournamentGames(tour.id, 4) map { games ⇒
|
||||
val pairings = html.tournament.pairings(tour)
|
||||
val inner = html.tournament.show.finishedInner(tour, games)
|
||||
html.tournament.show.inner(pairings.some)(inner)
|
||||
}
|
||||
|
||||
def form = Auth { implicit ctx ⇒
|
||||
me ⇒
|
||||
Ok(html.tournament.form(forms.create))
|
||||
|
|
|
@ -17,7 +17,7 @@ sealed abstract class Context(val req: RequestHeader, val me: Option[User]) {
|
|||
def isGranted(permission: Permission): Boolean =
|
||||
me.fold(Granter(permission), false)
|
||||
|
||||
def is(user: User) = me == Some(user)
|
||||
def is(user: User): Boolean = me == Some(user)
|
||||
|
||||
def userId = me map (_.id)
|
||||
}
|
||||
|
|
|
@ -8,13 +8,18 @@ import play.api.data.validation.Constraints._
|
|||
final class DataForm {
|
||||
|
||||
import lila.core.Form._
|
||||
import Tournament._
|
||||
|
||||
val create = Form(mapping(
|
||||
"minutes" -> numberIn(Tournament.minuteChoices),
|
||||
"minUsers" -> numberIn(Tournament.minUserChoices)
|
||||
"clockTime" -> numberIn(clockTimeChoices),
|
||||
"clockIncrement" -> numberIn(clockIncrementChoices),
|
||||
"minutes" -> numberIn(minuteChoices),
|
||||
"minPlayers" -> numberIn(minPlayerChoices)
|
||||
)(TournamentSetup.apply)(TournamentSetup.unapply)) fill TournamentSetup()
|
||||
}
|
||||
|
||||
case class TournamentSetup(
|
||||
clockTime: Int = Tournament.clockTimeDefault,
|
||||
clockIncrement: Int = Tournament.clockIncrementDefault,
|
||||
minutes: Int = Tournament.minuteDefault,
|
||||
minUsers: Int = Tournament.minUserDefault)
|
||||
minPlayers: Int = Tournament.minPlayerDefault)
|
||||
|
|
|
@ -64,7 +64,7 @@ final class Organizer(
|
|||
def startPairing(tour: Started) {
|
||||
(hubMaster ? GetTournamentUsernames(tour.id)).mapTo[Iterable[String]] onSuccess {
|
||||
case usernames ⇒
|
||||
(tour.users intersect usernames.toList) |> { users ⇒
|
||||
(tour.activeUserIds intersect usernames.toList) |> { users ⇒
|
||||
Pairing.createNewPairings(users, tour.pairings).toNel foreach { pairings ⇒
|
||||
api.makePairings(tour, pairings).unsafePerformIO
|
||||
}
|
||||
|
|
|
@ -3,25 +3,41 @@ package tournament
|
|||
|
||||
import com.mongodb.casbah.query.Imports._
|
||||
|
||||
import user.User
|
||||
|
||||
case class Player(
|
||||
id: String,
|
||||
username: String,
|
||||
nbWin: Int,
|
||||
nbLoss: Int,
|
||||
winStreak: Int,
|
||||
score: Int) {
|
||||
elo: Int,
|
||||
withdraw: Boolean = false,
|
||||
nbWin: Int = 0,
|
||||
nbLoss: Int = 0,
|
||||
winStreak: Int = 0,
|
||||
score: Int = 0) {
|
||||
|
||||
def active = !withdraw
|
||||
|
||||
def is(user: User) = id == user.id
|
||||
|
||||
def doWithdraw = copy(withdraw = true)
|
||||
}
|
||||
|
||||
object Standing {
|
||||
object Player {
|
||||
|
||||
def of(tour: Tournament): Standing = tour.users.map { user ⇒
|
||||
def apply(user: User): Player = new Player(
|
||||
id = user.id,
|
||||
username = user.username,
|
||||
elo = user.elo)
|
||||
|
||||
def refresh(tour: Tournament): Players = tour.players.map { player ⇒
|
||||
tour.pairings
|
||||
.filter(_ contains user)
|
||||
.foldLeft(Builder(user))(_ + _.winner)
|
||||
.player
|
||||
.filter(_ contains player.id)
|
||||
.foldLeft(Builder(player))(_ + _.winner)
|
||||
.toPlayer
|
||||
} sortBy (p ⇒ -p.score)
|
||||
|
||||
private case class Builder(
|
||||
username: String,
|
||||
player: Player,
|
||||
nbWin: Int = 0,
|
||||
nbLoss: Int = 0,
|
||||
score: Int = 0,
|
||||
|
@ -31,7 +47,7 @@ object Standing {
|
|||
|
||||
def +(winner: Option[String]) = {
|
||||
val (win, loss) = winner.fold(
|
||||
w ⇒ if (w == username) true -> false else false -> true,
|
||||
w ⇒ if (w == player.id) true -> false else false -> true,
|
||||
false -> false)
|
||||
val newWinSeq = if (win) prevWin.fold(winSeq + 1, 1) else 0
|
||||
val points = win.fold(newWinSeq * 2, loss.fold(0, 1))
|
||||
|
@ -44,6 +60,10 @@ object Standing {
|
|||
prevWin = win)
|
||||
}
|
||||
|
||||
def player = Player(username, nbWin, nbLoss, bestWinSeq, score)
|
||||
def toPlayer = player.copy(
|
||||
nbWin = nbWin,
|
||||
nbLoss = nbLoss,
|
||||
winStreak = bestWinSeq,
|
||||
score = score)
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ final class Socket(
|
|||
implicit private val timeout = Timeout(timeoutDuration)
|
||||
|
||||
def reload(tournamentId: String) = io {
|
||||
hubMaster ! Forward(tournamentId, Reload)
|
||||
hubMaster ! Forward(tournamentId, Reload).pp
|
||||
}
|
||||
|
||||
def notifyPairing(game: DbGame) = io {
|
||||
|
|
|
@ -12,11 +12,12 @@ import user.User
|
|||
|
||||
case class Data(
|
||||
name: String,
|
||||
clock: TournamentClock,
|
||||
minutes: Int,
|
||||
minUsers: Int,
|
||||
minPlayers: Int,
|
||||
createdAt: DateTime,
|
||||
createdBy: String,
|
||||
users: List[String])
|
||||
players: Players)
|
||||
|
||||
sealed trait Tournament {
|
||||
|
||||
|
@ -30,19 +31,21 @@ sealed trait Tournament {
|
|||
def name = data.name
|
||||
def nameT = name + " tournament"
|
||||
|
||||
def clock = data.clock
|
||||
def minutes = data.minutes
|
||||
lazy val duration = new Duration(minutes * 60 * 1000)
|
||||
|
||||
def users = data.users
|
||||
def nbUsers = users.size
|
||||
def minUsers = data.minUsers
|
||||
def contains(username: String): Boolean = users contains username
|
||||
def players = data.players
|
||||
def userIds = players map (_.id)
|
||||
def activeUserIds = players filter (_.active) map (_.id)
|
||||
def nbPlayers = players.size
|
||||
def minPlayers = data.minPlayers
|
||||
def playerRatio = "%d/%d".format(nbPlayers, minPlayers)
|
||||
def contains(userId: String): Boolean = userIds contains userId
|
||||
def contains(user: User): Boolean = contains(user.id)
|
||||
def contains(user: Option[User]): Boolean =
|
||||
user.fold(u ⇒ contains(u.id), false)
|
||||
def missingUsers = minUsers - users.size
|
||||
def contains(user: Option[User]): Boolean = user.fold(u ⇒ contains(u.id), false)
|
||||
def missingPlayers = minPlayers - players.size
|
||||
|
||||
def showClock = "2+0"
|
||||
def createdBy = data.createdBy
|
||||
def createdAt = data.createdAt
|
||||
}
|
||||
|
@ -51,10 +54,9 @@ sealed trait StartedOrFinished extends Tournament {
|
|||
|
||||
def startedAt: DateTime
|
||||
|
||||
def standing: Standing
|
||||
def rankedStanding = (1 to standing.size) zip standing
|
||||
def rankedPlayers = (1 to players.size) zip players
|
||||
|
||||
def winner = standing.headOption
|
||||
def winner = players.headOption
|
||||
def winnerUserId = winner map (_.username)
|
||||
|
||||
def encode(status: Status) = new RawTournament(
|
||||
|
@ -62,7 +64,8 @@ sealed trait StartedOrFinished extends Tournament {
|
|||
status = status.id,
|
||||
data = data,
|
||||
startedAt = startedAt.some,
|
||||
pairings = pairings map (_.encode))
|
||||
pairings = pairings map (_.encode),
|
||||
players = players)
|
||||
|
||||
def finishedAt = startedAt + duration
|
||||
}
|
||||
|
@ -73,10 +76,9 @@ case class Created(
|
|||
|
||||
import data._
|
||||
|
||||
def readyToStart = users.size >= minUsers
|
||||
def readyToStart = players.size >= minPlayers
|
||||
|
||||
def pairings = Nil
|
||||
lazy val standing = Standing of this
|
||||
|
||||
def encode = new RawTournament(
|
||||
id = id,
|
||||
|
@ -85,17 +87,17 @@ case class Created(
|
|||
|
||||
def join(user: User): Valid[Created] = contains(user).fold(
|
||||
!!("User %s is already part of the tournament" format user.id),
|
||||
withUsers(users :+ user.id).success
|
||||
withPlayers(players :+ Player(user)).success
|
||||
)
|
||||
|
||||
def withdraw(user: User): Valid[Created] = contains(user).fold(
|
||||
withUsers(users filterNot (user.id ==)).success,
|
||||
withPlayers(players filterNot (_ is user)).success,
|
||||
!!("User %s is not part of the tournament" format user.id)
|
||||
)
|
||||
|
||||
def start = Started(id, data, DateTime.now, Nil)
|
||||
private def withPlayers(s: Players) = copy(data = data.copy(players = s))
|
||||
|
||||
private def withUsers(x: List[String]) = copy(data = data.copy(users = x))
|
||||
def start = Started(id, data, DateTime.now, Nil)
|
||||
}
|
||||
|
||||
case class Started(
|
||||
|
@ -104,8 +106,6 @@ case class Started(
|
|||
startedAt: DateTime,
|
||||
pairings: List[Pairing]) extends StartedOrFinished {
|
||||
|
||||
lazy val standing = Standing of this
|
||||
|
||||
def addPairings(ps: NonEmptyList[Pairing]) =
|
||||
copy(pairings = ps.list ::: pairings)
|
||||
|
||||
|
@ -117,24 +117,36 @@ case class Started(
|
|||
|
||||
def remainingSeconds: Int = math.max(0, finishedAt.getSeconds - nowSeconds).toInt
|
||||
|
||||
def finish = Finished(
|
||||
id = id,
|
||||
data = data,
|
||||
startedAt = startedAt,
|
||||
pairings = pairings,
|
||||
standing = standing)
|
||||
def finish = refreshPlayers |> { tour ⇒
|
||||
Finished(
|
||||
id = tour.id,
|
||||
data = tour.data,
|
||||
startedAt = tour.startedAt,
|
||||
pairings = tour.pairings)
|
||||
}
|
||||
|
||||
def encode = encode(Status.Started)
|
||||
def withdraw(user: User): Valid[Started] = contains(user).fold(
|
||||
withPlayers(players map {
|
||||
case p if p is user ⇒ p.doWithdraw
|
||||
case p ⇒ p
|
||||
}).success,
|
||||
!!("User %s is not part of the tournament" format user.id)
|
||||
)
|
||||
|
||||
private def withPlayers(s: Players) = copy(data = data.copy(players = s))
|
||||
|
||||
private def refreshPlayers = withPlayers(Player refresh this)
|
||||
|
||||
def encode = refreshPlayers.encode(Status.Started)
|
||||
}
|
||||
|
||||
case class Finished(
|
||||
id: String,
|
||||
data: Data,
|
||||
startedAt: DateTime,
|
||||
pairings: List[Pairing],
|
||||
standing: Standing) extends StartedOrFinished {
|
||||
pairings: List[Pairing]) extends StartedOrFinished {
|
||||
|
||||
def encode = encode(Status.Finished) withStanding standing
|
||||
def encode = encode(Status.Finished)
|
||||
}
|
||||
|
||||
case class RawTournament(
|
||||
|
@ -143,7 +155,7 @@ case class RawTournament(
|
|||
data: Data,
|
||||
startedAt: Option[DateTime] = None,
|
||||
pairings: List[RawPairing] = Nil,
|
||||
standing: List[Player] = Nil) {
|
||||
players: List[Player] = Nil) {
|
||||
|
||||
def created: Option[Created] = (status == Status.Created.id) option Created(
|
||||
id = id,
|
||||
|
@ -165,8 +177,7 @@ case class RawTournament(
|
|||
id = id,
|
||||
data = data,
|
||||
startedAt = stAt,
|
||||
decodePairings,
|
||||
standing = standing)
|
||||
decodePairings)
|
||||
|
||||
def decodePairings = pairings map (_.decode) flatten
|
||||
|
||||
|
@ -176,7 +187,7 @@ case class RawTournament(
|
|||
case Status.Finished ⇒ finished
|
||||
}
|
||||
|
||||
def withStanding(s: Standing) = copy(standing = s)
|
||||
def withPlayers(s: Players) = copy(players = s)
|
||||
}
|
||||
|
||||
object Tournament {
|
||||
|
@ -184,24 +195,34 @@ object Tournament {
|
|||
import lila.core.Form._
|
||||
|
||||
def apply(
|
||||
createdBy: String,
|
||||
createdBy: User,
|
||||
clock: TournamentClock,
|
||||
minutes: Int,
|
||||
minUsers: Int): Created = Created(
|
||||
minPlayers: Int): Created = Created(
|
||||
id = Random nextString 8,
|
||||
data = Data(
|
||||
name = RandomName(),
|
||||
createdBy = createdBy,
|
||||
clock = clock,
|
||||
createdBy = createdBy.id,
|
||||
createdAt = DateTime.now,
|
||||
minutes = minutes,
|
||||
minUsers = minUsers,
|
||||
users = List(createdBy))
|
||||
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 5 by 1
|
||||
val clockIncrementDefault = 0
|
||||
val clockIncrementChoices = options(clockIncrements, "%d second{s}")
|
||||
|
||||
val minutes = 5 to 60 by 5
|
||||
val minuteDefault = 10
|
||||
val minuteChoices = options(minutes, "%d minute{s}")
|
||||
|
||||
val minUsers = (2 to 4) ++ (5 to 30 by 5)
|
||||
val minUserDefault = 10
|
||||
val minUserChoices = options(minUsers, "%d player{s}")
|
||||
val minPlayers = (2 to 4) ++ (5 to 30 by 5)
|
||||
val minPlayerDefault = 10
|
||||
val minPlayerChoices = options(minPlayers, "%d player{s}")
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ final class TournamentApi(
|
|||
(tour addPairings pairings) |> { tour2 ⇒
|
||||
for {
|
||||
_ ← repo saveIO tour2
|
||||
games ← (pairings map makeGame(tour.id)).sequence
|
||||
games ← (pairings map makeGame(tour)).sequence
|
||||
_ ← (games map socket.notifyPairing).sequence
|
||||
} yield ()
|
||||
}
|
||||
|
@ -30,9 +30,10 @@ final class TournamentApi(
|
|||
tournament ← hasTournament.fold(
|
||||
io(alreadyInATournament(me)),
|
||||
Tournament(
|
||||
createdBy = me.id,
|
||||
createdBy = me,
|
||||
clock = TournamentClock(setup.clockTime, setup.clockIncrement),
|
||||
minutes = setup.minutes,
|
||||
minUsers = setup.minUsers
|
||||
minPlayers = setup.minPlayers
|
||||
) |> { created ⇒ repo saveIO created map (_ ⇒ created.success) }
|
||||
)
|
||||
} yield tournament
|
||||
|
@ -43,20 +44,13 @@ final class TournamentApi(
|
|||
def finish(started: Started): IO[Unit] =
|
||||
repo saveIO started.finish doIf started.readyToFinish
|
||||
|
||||
def join(tour: Created, me: User): IO[Valid[Unit]] = for {
|
||||
hasTournament ← repo userHasRunningTournament me.id
|
||||
tour2Valid = for {
|
||||
tour2 ← tour join me
|
||||
_ ← hasTournament.fold(alreadyInATournament(me), true.success)
|
||||
} yield tour2
|
||||
result ← tour2Valid.fold(
|
||||
err ⇒ io(failure(err)),
|
||||
tour2 ⇒ for {
|
||||
_ ← repo saveIO tour2
|
||||
_ ← io(socket reload tour.id)
|
||||
} yield ().success
|
||||
): IO[Valid[Unit]]
|
||||
} yield result
|
||||
def join(tour: Created, me: User): Valid[IO[Unit]] = for {
|
||||
tour2 ← tour join me
|
||||
} yield for {
|
||||
withdrawIds ← repo withdraw me
|
||||
_ ← repo saveIO tour2
|
||||
_ ← ((tour.id :: withdrawIds) map socket.reload).sequence
|
||||
} yield ()
|
||||
|
||||
def withdraw(tour: Created, me: User): IO[Unit] = (tour withdraw me).fold(
|
||||
err ⇒ putStrLn(err.shows),
|
||||
|
@ -79,14 +73,14 @@ final class TournamentApi(
|
|||
} | io(none)
|
||||
} yield result
|
||||
|
||||
private def makeGame(tournamentId: String)(pairing: Pairing): IO[DbGame] = for {
|
||||
private def makeGame(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,
|
||||
clock = chess.Clock(1, 0).some
|
||||
clock = tour.clock.chessClock.some
|
||||
),
|
||||
ai = None,
|
||||
whitePlayer = DbPlayer.white withUser user1,
|
||||
|
@ -94,7 +88,7 @@ final class TournamentApi(
|
|||
creatorColor = chess.Color.White,
|
||||
mode = chess.Mode.Rated,
|
||||
variant = variant
|
||||
).withTournamentId(tournamentId)
|
||||
).withTournamentId(tour.id)
|
||||
.withId(pairing.gameId)
|
||||
.start
|
||||
.startClock(2)
|
||||
|
|
14
app/tournament/TournamentClock.scala
Normal file
14
app/tournament/TournamentClock.scala
Normal file
|
@ -0,0 +1,14 @@
|
|||
package lila
|
||||
package tournament
|
||||
|
||||
// All durations are expressed in seconds
|
||||
case class TournamentClock(
|
||||
limit: Int,
|
||||
increment: Int) {
|
||||
|
||||
def limitInMinutes = limit / 60
|
||||
|
||||
def show = limitInMinutes.toString + " + " + increment.toString
|
||||
|
||||
def chessClock = chess.Clock(limit, increment)
|
||||
}
|
|
@ -6,9 +6,12 @@ import com.novus.salat.dao._
|
|||
import com.mongodb.casbah.MongoCollection
|
||||
import com.mongodb.casbah.query.Imports._
|
||||
import scalaz.effects._
|
||||
import scalaz.Success
|
||||
import org.joda.time.DateTime
|
||||
import org.scala_tools.time.Imports._
|
||||
|
||||
import user.User
|
||||
|
||||
class TournamentRepo(collection: MongoCollection)
|
||||
extends SalatDAO[RawTournament, String](collection) {
|
||||
|
||||
|
@ -18,7 +21,7 @@ class TournamentRepo(collection: MongoCollection)
|
|||
|
||||
def startedById(id: String): IO[Option[Started]] = byIdAs(id, _.started)
|
||||
|
||||
private def byIdAs[A](id: String, as: RawTournament => Option[A]): IO[Option[A]] = io {
|
||||
private def byIdAs[A](id: String, as: RawTournament ⇒ Option[A]): IO[Option[A]] = io {
|
||||
findOneById(id) flatMap as
|
||||
}
|
||||
|
||||
|
@ -59,6 +62,17 @@ class TournamentRepo(collection: MongoCollection)
|
|||
save(tournament.encode)
|
||||
}
|
||||
|
||||
def withdraw(user: User): IO[List[String]] = for {
|
||||
createds ← created
|
||||
createdIds ← (createds map (_ withdraw user) collect {
|
||||
case Success(tour) ⇒ saveIO(tour) map (_ ⇒ tour.id)
|
||||
}).sequence
|
||||
starteds ← started
|
||||
startedIds ← (starteds map (_ withdraw user) collect {
|
||||
case Success(tour) ⇒ saveIO(tour) map (_ ⇒ tour.id)
|
||||
}).sequence
|
||||
} yield createdIds ::: startedIds
|
||||
|
||||
private def idSelector(id: String): DBObject = DBObject("_id" -> id)
|
||||
private def idSelector(tournament: Tournament): DBObject = idSelector(tournament.id)
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@ package object tournament {
|
|||
|
||||
type Pairings = List[tournament.Pairing]
|
||||
|
||||
type Standing = List[Player]
|
||||
type Players = List[tournament.Player]
|
||||
}
|
||||
|
|
|
@ -7,8 +7,24 @@ title = "New tournament") {
|
|||
<div id="tournament">
|
||||
<div class="content_box tournament_box">
|
||||
<h1>New tournament</h1>
|
||||
<form action="@routes.Tournament.create" method="POST">
|
||||
<form class="plain" action="@routes.Tournament.create" method="POST">
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="@form("clockTime").id">Clock time</label>
|
||||
</th>
|
||||
<td>
|
||||
@base.select(form("clockTime"), Tournament.clockTimeChoices)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="@form("clockIncrement").id">Clock increment</label>
|
||||
</th>
|
||||
<td>
|
||||
@base.select(form("clockIncrement"), Tournament.clockIncrementChoices)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="@form("minutes").id">Duration</label>
|
||||
|
@ -19,10 +35,10 @@ title = "New tournament") {
|
|||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="@form("minUsers").id">Players</label>
|
||||
<label for="@form("minPlayers").id">Players</label>
|
||||
</th>
|
||||
<td>
|
||||
@base.select(form("minUsers"), Tournament.minUserChoices)
|
||||
@base.select(form("minPlayers"), Tournament.minPlayerChoices)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
<div class="box">
|
||||
@showDate(tour.createdAt)
|
||||
<br /><br />
|
||||
@tour.nbUsers / @tour.minUsers players
|
||||
@tour.minPlayers players
|
||||
<br /><br />
|
||||
<span class="s16 clock">@tour.showClock</span>
|
||||
<span class="s16 clock">@tour.clock.show</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,25 +7,5 @@ goodies = tournament.infoBox(tour),
|
|||
version = version,
|
||||
title = tour.nameT) {
|
||||
|
||||
<h1>@tour.nameT</h1>
|
||||
<div>
|
||||
Waiting for @tour.missingUsers players
|
||||
</div>
|
||||
<div class="user_list">
|
||||
@tournament.userList(users)
|
||||
</div>
|
||||
|
||||
@ctx.me.map { me =>
|
||||
<div>
|
||||
@if(tour contains me) {
|
||||
<form action="@routes.Tournament.withdraw(tour.id)" method="POST">
|
||||
<input type="submit" class="submit button" value="Withdraw" />
|
||||
</form>
|
||||
} else {
|
||||
<form action="@routes.Tournament.join(tour.id)" method="POST">
|
||||
<input type="submit" class="submit button" value="Join tournament" />
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@tournament.show.createdInner(tour, users)
|
||||
}
|
||||
|
|
41
app/views/tournament/show/createdInner.scala.html
Normal file
41
app/views/tournament/show/createdInner.scala.html
Normal file
|
@ -0,0 +1,41 @@
|
|||
@(tour: lila.tournament.Created, users: List[User])(implicit ctx: Context)
|
||||
|
||||
<h1>@tour.nameT</h1>
|
||||
<div class="user_list">
|
||||
<table class="data user_list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="large">
|
||||
@if(tour.readyToStart) {
|
||||
Tournament is starting!
|
||||
} else {
|
||||
Waiting for @tour.missingPlayers players
|
||||
}
|
||||
</th>
|
||||
@ctx.me.map { me =>
|
||||
<th>
|
||||
@if(tour contains me) {
|
||||
<form class="inline" action="@routes.Tournament.withdraw(tour.id)" method="POST">
|
||||
<input type="submit" class="submit button strong" value="Withdraw" />
|
||||
</form>
|
||||
} else {
|
||||
<form class="inline" action="@routes.Tournament.join(tour.id)" method="POST">
|
||||
<input type="submit" class="submit button strong" value="Join tournament" />
|
||||
</form>
|
||||
}
|
||||
</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@users.map { user =>
|
||||
<tr>
|
||||
<td colspan="2">@userLink(user)</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<br />
|
||||
The tournament will start as soon as @tour.minPlayers players join it.
|
||||
|
|
@ -11,13 +11,13 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@tour.rankedStanding.map {
|
||||
@tour.rankedPlayers.map {
|
||||
case (rank, player) => {
|
||||
<tr>
|
||||
<td><span class="rank">@rank</span></td>
|
||||
<td>
|
||||
<a class="username" href="@routes.User.show(player.username)">
|
||||
@userIdToUsername(player.username)
|
||||
<a class="username" href="@routes.User.show(player.id)">
|
||||
@player.username
|
||||
</a>
|
||||
</td>
|
||||
<td><strong>@player.score</strong></td>
|
||||
|
@ -33,4 +33,3 @@
|
|||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -13,16 +13,16 @@
|
|||
@tours.map { tour =>
|
||||
<tr>
|
||||
<td>@linkTo(tour)</td>
|
||||
<td><span class="s16 clock">@tour.showClock</span></td>
|
||||
<td><span class="s16 clock">@tour.clock.show</span></td>
|
||||
<td>@{tour.minutes}m</td>
|
||||
<td>@tour.nbUsers/@tour.minUsers</td>
|
||||
<td>@tour.nbPlayers/@tour.minPlayers</td>
|
||||
<td>
|
||||
@if(tour contains ctx.me) {
|
||||
<span class="label">JOINED</span>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
@moreTr
|
||||
}
|
||||
@moreTr
|
||||
</tbody>
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
@(users: List[User])(implicit ctx: Context)
|
||||
|
||||
Players: @users.map { user =>
|
||||
@userLink(user)
|
||||
}
|
|
@ -15,11 +15,11 @@ $.widget("lichess.game", {
|
|||
self.options.socketUrl = self.element.data('socket-url');
|
||||
self.socketAckTimeout;
|
||||
|
||||
setInterval(function() {
|
||||
$('a.view_tournament').each(function() {
|
||||
location.href = $(this).attr("href");
|
||||
});
|
||||
}, 3000);
|
||||
//setInterval(function() {
|
||||
//$('a.view_tournament').each(function() {
|
||||
//location.href = $(this).attr("href");
|
||||
//});
|
||||
//}, 3000);
|
||||
|
||||
if (self.options.game.started) {
|
||||
self.indicateTurn();
|
||||
|
@ -697,7 +697,6 @@ $.widget("lichess.clock", {
|
|||
self.element.addClass('running');
|
||||
var end_time = new Date().getTime() + self.options.time;
|
||||
self.options.interval = setInterval(function() {
|
||||
console.debug(this);
|
||||
if (self.options.state == 'running') {
|
||||
var current_time = Math.round(end_time - new Date().getTime());
|
||||
if (current_time <= 0) {
|
||||
|
|
|
@ -575,7 +575,7 @@ div.pagination span.current,
|
|||
div.hooks td.action:hover,
|
||||
div.hooks table.empty_table tr:hover,
|
||||
div.progressbar.flashy div,
|
||||
div.lichess_table .button.strong:hover,
|
||||
.button.strong:hover,
|
||||
div.locale_menu a.active
|
||||
{
|
||||
color: #fff;
|
||||
|
@ -591,7 +591,7 @@ div.locale_menu a.active
|
|||
/* strong inactive gradient */
|
||||
div.hooks td.action,
|
||||
.button:hover,
|
||||
div.lichess_table .button.strong,
|
||||
.button.strong,
|
||||
div.notification
|
||||
{
|
||||
background: #ffffff;
|
||||
|
@ -619,7 +619,7 @@ div.notification
|
|||
div.lichess_chat,
|
||||
div.undertable,
|
||||
div.lichess_board_wrap,
|
||||
div.lichess_table .button.strong,
|
||||
.button.strong,
|
||||
div.content_box
|
||||
{
|
||||
box-shadow: 0 0 7px #d0d0d0;
|
||||
|
|
|
@ -26,7 +26,7 @@ body.dark div.undergame_box,
|
|||
body.dark div.clock,
|
||||
body.dark #GameText,
|
||||
body.dark #GameBoard table.boardTable,
|
||||
body.dark div.lichess_table .button.strong,
|
||||
body.dark .button.strong,
|
||||
body.dark #tournament_side,
|
||||
body.dark div.content_box
|
||||
{
|
||||
|
@ -86,6 +86,7 @@ body.dark div.adv_chart,
|
|||
body.dark #top .dropdown,
|
||||
body.dark div.lichess_overboard p.explanations,
|
||||
body.dark div.search_status,
|
||||
body.dark #tournament table.data,
|
||||
body.dark #tournament table.data thead th,
|
||||
#tournament table.data
|
||||
{
|
||||
|
@ -294,7 +295,7 @@ body.dark #tournament table.data thead
|
|||
}
|
||||
/* strong inactive gradient */
|
||||
body.dark div.hooks td.action,
|
||||
body.dark div.lichess_table .button.strong,
|
||||
body.dark .button.strong,
|
||||
body.dark .button:hover
|
||||
{
|
||||
background: #505050;
|
||||
|
@ -315,7 +316,7 @@ body.dark div.pagination span.current,
|
|||
body.dark #top span.new_messages.unread,
|
||||
body.dark div.hooks td.action:hover,
|
||||
body.dark div.hooks table.empty_table tr:hover,
|
||||
body.dark div.lichess_table .button.strong:hover,
|
||||
body.dark .button.strong:hover,
|
||||
body.dark div.progressbar.flashy div,
|
||||
body.dark div.locale_menu a.active
|
||||
{
|
||||
|
|
|
@ -59,34 +59,40 @@
|
|||
width: 511px;
|
||||
}
|
||||
|
||||
#tournament form {
|
||||
#tournament form.inline {
|
||||
display: inline;
|
||||
float: right;
|
||||
padding-right: 0.6em;
|
||||
}
|
||||
|
||||
#tournament form.plain {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
#tournament form td {
|
||||
#tournament form.plain td {
|
||||
padding: 0.6em;
|
||||
}
|
||||
|
||||
#tournament form label {
|
||||
#tournament form.plain label {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#tournament form input {
|
||||
#tournament form.plain input {
|
||||
width: 96%;
|
||||
padding: 0.5% 1%;
|
||||
}
|
||||
|
||||
#tournament form select {
|
||||
#tournament form.plain select {
|
||||
padding: 0.5% 1%;
|
||||
text-transform: capitalize;
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
#tournament form input.submit {
|
||||
#tournament form.plain input.submit {
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
#tournament form .error {
|
||||
#tournament form.plain .error {
|
||||
margin-left: 160px;
|
||||
color: red;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue