tournament organizer
This commit is contained in:
parent
b085fbf35d
commit
9e7d912c98
|
@ -25,6 +25,9 @@ object GameRepo {
|
||||||
|
|
||||||
def game(gameId: ID): Fu[Option[Game]] = $find byId gameId
|
def game(gameId: ID): Fu[Option[Game]] = $find byId gameId
|
||||||
|
|
||||||
|
def finished(gameId: ID): Fu[Option[Game]] =
|
||||||
|
$find.one($select(gameId) ++ Query.finished)
|
||||||
|
|
||||||
def player(gameId: ID, color: Color): Fu[Option[Player]] =
|
def player(gameId: ID, color: Color): Fu[Option[Player]] =
|
||||||
$find byId gameId map2 { (game: Game) ⇒ game player color }
|
$find byId gameId map2 { (game: Game) ⇒ game player color }
|
||||||
|
|
||||||
|
|
73
modules/tournament/src/main/Organizer.scala
Normal file
73
modules/tournament/src/main/Organizer.scala
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package lila.tournament
|
||||||
|
|
||||||
|
import actorApi._
|
||||||
|
import lila.hub.actorApi.round.FinishGame
|
||||||
|
import makeTimeout.short
|
||||||
|
|
||||||
|
import akka.actor._
|
||||||
|
import akka.pattern.{ ask, pipe }
|
||||||
|
|
||||||
|
private[tournament] final class Organizer(
|
||||||
|
api: TournamentApi,
|
||||||
|
reminder: ActorRef,
|
||||||
|
socketHub: ActorRef) extends Actor {
|
||||||
|
|
||||||
|
def receive = {
|
||||||
|
|
||||||
|
case CreatedTournaments ⇒ createdTournaments
|
||||||
|
case CreatedTournament(tour: Created) ⇒ createdTournament(tour)
|
||||||
|
|
||||||
|
case StartedTournaments ⇒ startedTournaments
|
||||||
|
case StartedTournament(tour: Started) ⇒ startedTournament(tour)
|
||||||
|
|
||||||
|
case StartPairings ⇒ startPairings
|
||||||
|
case StartPairing(tour: Started) ⇒ startPairing(tour)
|
||||||
|
|
||||||
|
case FinishGame(gameId) ⇒
|
||||||
|
api.finishGame(gameId) foreach { _ map (_.id) foreach api.socketReload }
|
||||||
|
}
|
||||||
|
|
||||||
|
def createdTournaments {
|
||||||
|
TournamentRepo.created foreach { _ foreach createdTournament }
|
||||||
|
}
|
||||||
|
|
||||||
|
def createdTournament(tour: Created) {
|
||||||
|
if (tour.isEmpty) api wipeEmpty tour
|
||||||
|
else if (tour.readyToStart) api startIfReady tour
|
||||||
|
else withUserIds(tour.id) { ids ⇒
|
||||||
|
(tour.userIds diff ids) foreach { api.withdraw(tour, _) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def startedTournaments {
|
||||||
|
TournamentRepo.started foreach { tours ⇒
|
||||||
|
tours foreach startedTournament
|
||||||
|
reminder ! RemindTournaments(tours)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def startedTournament(tour: Started) {
|
||||||
|
if (tour.readyToFinish) api finish tour
|
||||||
|
}
|
||||||
|
|
||||||
|
def startPairings {
|
||||||
|
TournamentRepo.started foreach { _ foreach startPairing }
|
||||||
|
}
|
||||||
|
|
||||||
|
def startPairing(tour: Started) {
|
||||||
|
withUserIds(tour.id) { ids ⇒
|
||||||
|
(tour.activeUserIds intersect ids) |> { users ⇒
|
||||||
|
Pairing.createNewPairings(users, tour.pairings, tour.nbActiveUsers).toNel foreach { pairings ⇒
|
||||||
|
api.makePairings(tour, pairings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def withUserIds(tourId: String)(f: List[String] ⇒ Unit) {
|
||||||
|
(socketHub ? GetTournamentUserIds(tourId)).mapTo[Iterable[String]] foreach { ids ⇒
|
||||||
|
f(ids.toList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ import tube.roomTube
|
||||||
import tube.tournamentTubes._
|
import tube.tournamentTubes._
|
||||||
import lila.db.api._
|
import lila.db.api._
|
||||||
import chess.{ Mode, Variant }
|
import chess.{ Mode, Variant }
|
||||||
import lila.game.Game
|
import lila.game.{ Game, GameRepo }
|
||||||
import lila.user.User
|
import lila.user.User
|
||||||
import lila.hub.actorApi.lobby.{ SysTalk, UnTalk, ReloadTournaments }
|
import lila.hub.actorApi.lobby.{ SysTalk, UnTalk, ReloadTournaments }
|
||||||
import lila.hub.actorApi.router.Tourney
|
import lila.hub.actorApi.router.Tourney
|
||||||
|
@ -27,7 +27,7 @@ private[tournament] final class TournamentApi(
|
||||||
site: ActorRef,
|
site: ActorRef,
|
||||||
lobby: ActorRef,
|
lobby: ActorRef,
|
||||||
roundMeddler: lila.round.Meddler,
|
roundMeddler: lila.round.Meddler,
|
||||||
incToints: String ⇒ Int ⇒ Funit) {
|
incToints: String ⇒ Int ⇒ Funit) extends scalaz.OptionTs {
|
||||||
|
|
||||||
def makePairings(tour: Started, pairings: NonEmptyList[Pairing]): Funit =
|
def makePairings(tour: Started, pairings: NonEmptyList[Pairing]): Funit =
|
||||||
(tour addPairings pairings) |> { tour2 ⇒
|
(tour addPairings pairings) |> { tour2 ⇒
|
||||||
|
@ -56,24 +56,24 @@ private[tournament] final class TournamentApi(
|
||||||
sendLobbyMessage(created) inject created
|
sendLobbyMessage(created) inject created
|
||||||
}
|
}
|
||||||
|
|
||||||
def startIfReady(created: Created): Option[Fu[Unit]] = created.startIfReady map doStart
|
def startIfReady(created: Created): Option[Funit] = created.startIfReady map doStart
|
||||||
|
|
||||||
def earlyStart(created: Created): Option[Fu[Unit]] =
|
def earlyStart(created: Created): Option[Funit] =
|
||||||
created.readyToEarlyStart option doStart(created.start)
|
created.readyToEarlyStart option doStart(created.start)
|
||||||
|
|
||||||
private def doStart(started: Started): Fu[Unit] =
|
private def doStart(started: Started): Funit =
|
||||||
$update(started) >>-
|
$update(started) >>-
|
||||||
sendTo(started.id, Start) >>-
|
sendTo(started.id, Start) >>-
|
||||||
reloadSiteSocket >>-
|
reloadSiteSocket >>-
|
||||||
lobbyReload
|
lobbyReload
|
||||||
|
|
||||||
def wipeEmpty(created: Created): Fu[Unit] = created.isEmpty ?? {
|
def wipeEmpty(created: Created): Funit = created.isEmpty ?? {
|
||||||
$remove(created) >>
|
$remove(created) >>
|
||||||
$remove.byId[Room](created.id) >>-
|
$remove.byId[Room](created.id) >>-
|
||||||
reloadSiteSocket >>-
|
reloadSiteSocket >>-
|
||||||
lobbyReload >>-
|
lobbyReload >>-
|
||||||
(lobby ! UnTalk("%s tournament created".format(created.name).r))
|
(lobby ! UnTalk("%s tournament created".format(created.name).r))
|
||||||
}
|
}
|
||||||
|
|
||||||
def finish(started: Started): Fu[Tournament] = started.readyToFinish.fold({
|
def finish(started: Started): Fu[Tournament] = started.readyToFinish.fold({
|
||||||
val pairingsToAbort = started.playingPairings
|
val pairingsToAbort = started.playingPairings
|
||||||
|
@ -111,13 +111,16 @@ private[tournament] final class TournamentApi(
|
||||||
case finished: Finished ⇒ fufail("Cannot withdraw from finished tournament " + finished.id)
|
case finished: Finished ⇒ fufail("Cannot withdraw from finished tournament " + finished.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
def finishGame(game: Game): Fu[Option[Tournament]] = for {
|
def finishGame(gameId: String): Fu[Option[Tournament]] = for {
|
||||||
tourOption ← ~(game.tournamentId map TournamentRepo.startedById)
|
game ← optionT(GameRepo finished gameId)
|
||||||
result ← ~(tourOption.filter(_ ⇒ game.finished).map(tour ⇒ {
|
tour ← optionT(game.tournamentId zmap TournamentRepo.startedById)
|
||||||
|
result ← optionT {
|
||||||
val tour2 = tour.updatePairing(game.id, _.finish(game.status, game.winnerUserId, game.turns))
|
val tour2 = tour.updatePairing(game.id, _.finish(game.status, game.winnerUserId, game.turns))
|
||||||
$update(tour2) >> tripleQuickLossWithdraw(tour2, game.loserUserId) inject tour2.some
|
$update(tour2) >>
|
||||||
}))
|
tripleQuickLossWithdraw(tour2, game.loserUserId) inject
|
||||||
} yield result
|
tour2.some
|
||||||
|
}
|
||||||
|
} yield result.value
|
||||||
|
|
||||||
private def tripleQuickLossWithdraw(tour: Started, loser: Option[String]): Funit =
|
private def tripleQuickLossWithdraw(tour: Started, loser: Option[String]): Funit =
|
||||||
loser.filter(tour.quickLossStreak).zmap(withdraw(tour, _))
|
loser.filter(tour.quickLossStreak).zmap(withdraw(tour, _))
|
||||||
|
@ -135,7 +138,7 @@ private[tournament] final class TournamentApi(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def socketReload(tourId: String) {
|
def socketReload(tourId: String) {
|
||||||
sendTo(tourId, Reload)
|
sendTo(tourId, Reload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
package lila.app
|
|
||||||
package tournament
|
|
||||||
|
|
||||||
import game.DbGame
|
|
||||||
import round.FinishGame
|
|
||||||
|
|
||||||
import akka.actor._
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
import akka.util.Timeout
|
|
||||||
import akka.pattern.{ ask, pipe }
|
|
||||||
import scala.concurrent.{ Future, Promise }
|
|
||||||
import play.api.libs.concurrent._
|
|
||||||
import play.api.libs.concurrent.Execution.Implicits._
|
|
||||||
|
|
||||||
private[tournament] final class Organizer(
|
|
||||||
api: TournamentApi,
|
|
||||||
repo: TournamentRepo,
|
|
||||||
reminder: ActorRef,
|
|
||||||
hubMaster: ActorRef) extends Actor {
|
|
||||||
|
|
||||||
implicit val timeout = Timeout(1 second)
|
|
||||||
|
|
||||||
def receive = {
|
|
||||||
|
|
||||||
case CreatedTournaments ⇒ createdTournaments
|
|
||||||
case CreatedTournament(tour: Created) ⇒ createdTournament(tour)
|
|
||||||
|
|
||||||
case StartedTournaments ⇒ startedTournaments
|
|
||||||
case StartedTournament(tour: Started) ⇒ startedTournament(tour)
|
|
||||||
|
|
||||||
case StartPairings ⇒ startPairings
|
|
||||||
case StartPairing(tour: Started) ⇒ startPairing(tour)
|
|
||||||
|
|
||||||
case FinishGame(game) ⇒ finishGame(game)
|
|
||||||
}
|
|
||||||
|
|
||||||
def finishGame(game: DbGame) {
|
|
||||||
api.finishGame(game).unsafePerformIO foreach { tour ⇒
|
|
||||||
hubMaster ! Forward(tour.id, Reload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def createdTournaments {
|
|
||||||
repo.created.unsafePerformIO foreach createdTournament
|
|
||||||
}
|
|
||||||
|
|
||||||
def createdTournament(tour: Created) {
|
|
||||||
if (tour.isEmpty) (api wipeEmpty tour).unsafePerformIO
|
|
||||||
else if (tour.readyToStart) api startIfReady tour map (_.unsafePerformIO)
|
|
||||||
else (hubMaster ? GetTournamentUsernames(tour.id)).mapTo[Iterable[String]] onSuccess {
|
|
||||||
case usernames ⇒ (tour.userIds diff usernames.toList.map(_.toLowerCase)) |> { leavers ⇒
|
|
||||||
leavers.map(u ⇒ api.withdraw(tour, u)).sequence.unsafePerformIO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def startedTournaments {
|
|
||||||
repo.started.unsafePerformIO ~ { tours ⇒
|
|
||||||
tours foreach startedTournament
|
|
||||||
reminder ! RemindTournaments(tours)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def startedTournament(tour: Started) {
|
|
||||||
if (tour.readyToFinish) (api finish tour).unsafePerformIO
|
|
||||||
}
|
|
||||||
|
|
||||||
def startPairings {
|
|
||||||
repo.started.unsafePerformIO foreach startPairing
|
|
||||||
}
|
|
||||||
|
|
||||||
def startPairing(tour: Started) {
|
|
||||||
(hubMaster ? GetTournamentUsernames(tour.id)).mapTo[Iterable[String]] onSuccess {
|
|
||||||
case usernames ⇒
|
|
||||||
(tour.activeUserIds intersect usernames.toList.map(_.toLowerCase)) |> { users ⇒
|
|
||||||
Pairing.createNewPairings(users, tour.pairings, tour.nbActiveUsers).toNel foreach { pairings ⇒
|
|
||||||
api.makePairings(tour, pairings).unsafePerformIO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1
todo
1
todo
|
@ -80,6 +80,7 @@ chess variants https://github.com/ornicar/lila/issues/2://github.com/ornicar/lil
|
||||||
publish scalastic 0.90.0-thib
|
publish scalastic 0.90.0-thib
|
||||||
stream game export
|
stream game export
|
||||||
show fen only after game is finished http://en.lichess.org/forum/lichess-feedback/please-disable-live-fen-notation?page=1
|
show fen only after game is finished http://en.lichess.org/forum/lichess-feedback/please-disable-live-fen-notation?page=1
|
||||||
|
I owe the admins decent tools
|
||||||
|
|
||||||
DEPLOY p21
|
DEPLOY p21
|
||||||
----------
|
----------
|
||||||
|
|
Loading…
Reference in a new issue