From 9e7d912c98252aa5a6664ca37669ba6020b02503 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 12 May 2013 08:27:05 -0300 Subject: [PATCH] tournament organizer --- modules/game/src/main/GameRepo.scala | 3 + modules/tournament/src/main/Organizer.scala | 73 +++++++++++++++++ .../tournament/src/main/TournamentApi.scala | 39 +++++---- old/tournament/Organizer.scala | 82 ------------------- todo | 1 + 5 files changed, 98 insertions(+), 100 deletions(-) create mode 100644 modules/tournament/src/main/Organizer.scala delete mode 100644 old/tournament/Organizer.scala diff --git a/modules/game/src/main/GameRepo.scala b/modules/game/src/main/GameRepo.scala index 0aa4c48261..49b331d7bb 100644 --- a/modules/game/src/main/GameRepo.scala +++ b/modules/game/src/main/GameRepo.scala @@ -25,6 +25,9 @@ object GameRepo { 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]] = $find byId gameId map2 { (game: Game) ⇒ game player color } diff --git a/modules/tournament/src/main/Organizer.scala b/modules/tournament/src/main/Organizer.scala new file mode 100644 index 0000000000..873ef6dbef --- /dev/null +++ b/modules/tournament/src/main/Organizer.scala @@ -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) + } + } + +} diff --git a/modules/tournament/src/main/TournamentApi.scala b/modules/tournament/src/main/TournamentApi.scala index 0d418828b4..e290bebc67 100644 --- a/modules/tournament/src/main/TournamentApi.scala +++ b/modules/tournament/src/main/TournamentApi.scala @@ -5,7 +5,7 @@ import tube.roomTube import tube.tournamentTubes._ import lila.db.api._ import chess.{ Mode, Variant } -import lila.game.Game +import lila.game.{ Game, GameRepo } import lila.user.User import lila.hub.actorApi.lobby.{ SysTalk, UnTalk, ReloadTournaments } import lila.hub.actorApi.router.Tourney @@ -27,7 +27,7 @@ private[tournament] final class TournamentApi( site: ActorRef, lobby: ActorRef, roundMeddler: lila.round.Meddler, - incToints: String ⇒ Int ⇒ Funit) { + incToints: String ⇒ Int ⇒ Funit) extends scalaz.OptionTs { def makePairings(tour: Started, pairings: NonEmptyList[Pairing]): Funit = (tour addPairings pairings) |> { tour2 ⇒ @@ -56,24 +56,24 @@ private[tournament] final class TournamentApi( 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) - private def doStart(started: Started): Fu[Unit] = + private def doStart(started: Started): Funit = $update(started) >>- sendTo(started.id, Start) >>- reloadSiteSocket >>- lobbyReload - def wipeEmpty(created: Created): Fu[Unit] = created.isEmpty ?? { + def wipeEmpty(created: Created): Funit = created.isEmpty ?? { $remove(created) >> - $remove.byId[Room](created.id) >>- - reloadSiteSocket >>- - lobbyReload >>- - (lobby ! UnTalk("%s tournament created".format(created.name).r)) - } + $remove.byId[Room](created.id) >>- + reloadSiteSocket >>- + lobbyReload >>- + (lobby ! UnTalk("%s tournament created".format(created.name).r)) + } def finish(started: Started): Fu[Tournament] = started.readyToFinish.fold({ val pairingsToAbort = started.playingPairings @@ -111,13 +111,16 @@ private[tournament] final class TournamentApi( case finished: Finished ⇒ fufail("Cannot withdraw from finished tournament " + finished.id) } - def finishGame(game: Game): Fu[Option[Tournament]] = for { - tourOption ← ~(game.tournamentId map TournamentRepo.startedById) - result ← ~(tourOption.filter(_ ⇒ game.finished).map(tour ⇒ { + def finishGame(gameId: String): Fu[Option[Tournament]] = for { + game ← optionT(GameRepo finished gameId) + tour ← optionT(game.tournamentId zmap TournamentRepo.startedById) + result ← optionT { val tour2 = tour.updatePairing(game.id, _.finish(game.status, game.winnerUserId, game.turns)) - $update(tour2) >> tripleQuickLossWithdraw(tour2, game.loserUserId) inject tour2.some - })) - } yield result + $update(tour2) >> + tripleQuickLossWithdraw(tour2, game.loserUserId) inject + tour2.some + } + } yield result.value private def tripleQuickLossWithdraw(tour: Started, loser: Option[String]): Funit = 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) } diff --git a/old/tournament/Organizer.scala b/old/tournament/Organizer.scala deleted file mode 100644 index d1b46250f7..0000000000 --- a/old/tournament/Organizer.scala +++ /dev/null @@ -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 - } - } - } - } -} diff --git a/todo b/todo index eeb4535292..254ed763b1 100644 --- a/todo +++ b/todo @@ -80,6 +80,7 @@ chess variants https://github.com/ornicar/lila/issues/2://github.com/ornicar/lil publish scalastic 0.90.0-thib stream game export 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 ----------