From 8c4a16e6e06e43d38bf569500d5262a6fd7d7b20 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sat, 8 Sep 2012 20:18:38 +0200 Subject: [PATCH] tournaments wip --- app/controllers/Tournament.scala | 30 ++++++++++++++++++++++++++ app/core/CoreEnv.scala | 4 ++++ app/core/Settings.scala | 2 ++ app/game/GameRepo.scala | 4 ++-- app/i18n/I18nKeys.scala | 3 ++- app/tournament/DataForm.scala | 16 ++++++++++++++ app/tournament/Status.scala | 26 ++++++++++++++++++++++ app/tournament/Tournament.scala | 13 ++++++++--- app/tournament/TournamentEnv.scala | 20 +++++++++++++++++ app/tournament/TournamentRepo.scala | 27 +++++++++++++++++++++++ app/ui/SiteMenu.scala | 5 +++-- app/views/tournament/form.scala.html | 8 +++++++ app/views/tournament/home.scala.html | 13 +++++++++++ app/views/tournament/layout.scala.html | 12 +++++++++++ conf/messages | 1 + conf/messages.fr | 1 + conf/routes | 8 ++++++- 17 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 app/controllers/Tournament.scala create mode 100644 app/tournament/DataForm.scala create mode 100644 app/tournament/Status.scala create mode 100644 app/tournament/TournamentEnv.scala create mode 100644 app/tournament/TournamentRepo.scala create mode 100644 app/views/tournament/form.scala.html create mode 100644 app/views/tournament/home.scala.html create mode 100644 app/views/tournament/layout.scala.html diff --git a/app/controllers/Tournament.scala b/app/controllers/Tournament.scala new file mode 100644 index 0000000000..e87a8fa622 --- /dev/null +++ b/app/controllers/Tournament.scala @@ -0,0 +1,30 @@ +package controllers + +import lila._ +import views._ +import http.Context + +import play.api.mvc.Result + +object Tournament extends LilaController { + + val repo = env.tournament.repo + val forms = env.tournament.forms + + val home = Open { implicit ctx ⇒ + IOk(repo.created map { tournaments ⇒ + html.tournament.home(tournaments) + }) + } + + def show(id: String) = Open { implicit ctx ⇒ + Ok(id) + } + + def form = Auth { implicit ctx ⇒ + me ⇒ + Ok(html.tournament.form(forms.create)) + } + + def create = TODO +} diff --git a/app/core/CoreEnv.scala b/app/core/CoreEnv.scala index eada923fe0..ac4e704929 100644 --- a/app/core/CoreEnv.scala +++ b/app/core/CoreEnv.scala @@ -97,6 +97,10 @@ final class CoreEnv private (application: Application, val settings: Settings) { flood = security.flood, indexGame = search.indexGame) + lazy val tournament = new lila.tournament.TournamentEnv( + settings = settings, + mongodb = mongodb.apply _) + lazy val analyse = new lila.analyse.AnalyseEnv( settings = settings, gameRepo = game.gameRepo, diff --git a/app/core/Settings.scala b/app/core/Settings.scala index 715c255d1b..41a2f3a03e 100644 --- a/app/core/Settings.scala +++ b/app/core/Settings.scala @@ -40,6 +40,8 @@ final class Settings(config: Config) { val RoundCollectionRoom = getString("round.collection.room") val RoundCollectionWatcherRoom = getString("round.collection.watcher_room") + val TournamentCollectionTournament = getString("tournament.collection.tournament") + val AnalyseCachedNbTtl = millis("analyse.cached.nb.ttl") val UserPaginatorMaxPerPage = getInt("user.paginator.max_per_page") diff --git a/app/game/GameRepo.scala b/app/game/GameRepo.scala index a31167a37f..f1b4bc9b00 100644 --- a/app/game/GameRepo.scala +++ b/app/game/GameRepo.scala @@ -174,9 +174,9 @@ class GameRepo(collection: MongoCollection) def recentGames(limit: Int): IO[List[DbGame]] = io { find(Query.started ++ Query.turnsGt(1)) - .sort(DBObject("createdAt" -> -1)) + .sort(Query.sortCreated) .limit(limit) - .toList.map(_.decode).flatten sortBy (_.id) + .toList.map(_.decode).flatten } def games(ids: List[String]): IO[List[DbGame]] = io { diff --git a/app/i18n/I18nKeys.scala b/app/i18n/I18nKeys.scala index 771c489793..ce341ebbe2 100644 --- a/app/i18n/I18nKeys.scala +++ b/app/i18n/I18nKeys.scala @@ -162,7 +162,8 @@ final class I18nKeys(translator: Translator) { val bookmarkThisGame = new Key("bookmarkThisGame") val toggleBackground = new Key("toggleBackground") val advancedSearch = new Key("advancedSearch") + val tournament = new Key("tournament") val freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents = new Key("freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents") - def keys = List(playWithAFriend, inviteAFriendToPlayWithYou, playWithTheMachine, challengeTheArtificialIntelligence, toInviteSomeoneToPlayGiveThisUrl, gameOver, waitingForOpponent, waiting, yourTurn, aiNameLevelAiLevel, level, toggleTheChat, toggleSound, chat, resign, checkmate, stalemate, white, black, createAGame, noGameAvailableRightNowCreateOne, whiteIsVictorious, blackIsVictorious, playWithTheSameOpponentAgain, newOpponent, playWithAnotherOpponent, yourOpponentWantsToPlayANewGameWithYou, joinTheGame, whitePlays, blackPlays, theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim, makeYourOpponentResign, forceResignation, talkInChat, theFirstPersonToComeOnThisUrlWillPlayWithYou, whiteCreatesTheGame, blackCreatesTheGame, whiteJoinsTheGame, blackJoinsTheGame, whiteResigned, blackResigned, whiteLeftTheGame, blackLeftTheGame, shareThisUrlToLetSpectatorsSeeTheGame, youAreViewingThisGameAsASpectator, replayAndAnalyse, viewGameStats, flipBoard, threefoldRepetition, claimADraw, offerDraw, draw, nbConnectedPlayers, talkAboutChessAndDiscussLichessFeaturesInTheForum, seeTheGamesBeingPlayedInRealTime, gamesBeingPlayedRightNow, viewAllNbGames, viewNbCheckmates, nbBookmarks, nbPopularGames, nbAnalysedGames, bookmarkedByNbPlayers, viewInFullSize, logOut, signIn, signUp, people, games, forum, chessPlayers, minutesPerSide, variant, timeControl, start, username, password, haveAnAccount, allYouNeedIsAUsernameAndAPassword, learnMoreAboutLichess, rank, gamesPlayed, declineInvitation, cancel, timeOut, drawOfferSent, drawOfferDeclined, drawOfferAccepted, drawOfferCanceled, yourOpponentOffersADraw, accept, decline, playingRightNow, abortGame, gameAborted, standard, unlimited, mode, casual, rated, thisGameIsRated, rematch, rematchOfferSent, rematchOfferAccepted, rematchOfferCanceled, rematchOfferDeclined, cancelRematchOffer, viewRematch, play, inbox, chatRoom, spectatorRoom, composeMessage, sentMessages, incrementInSeconds, freeOnlineChess, spectators, nbWins, nbLosses, nbDraws, exportGames, color, eloRange, giveNbSeconds, searchAPlayer, whoIsOnline, allPlayers, namedPlayers, premoveEnabledClickAnywhereToCancel, thisPlayerUsesChessComputerAssistance, opening, takeback, proposeATakeback, takebackPropositionSent, takebackPropositionDeclined, takebackPropositionAccepted, takebackPropositionCanceled, yourOpponentProposesATakeback, bookmarkThisGame, toggleBackground, advancedSearch, freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents) + def keys = List(playWithAFriend, inviteAFriendToPlayWithYou, playWithTheMachine, challengeTheArtificialIntelligence, toInviteSomeoneToPlayGiveThisUrl, gameOver, waitingForOpponent, waiting, yourTurn, aiNameLevelAiLevel, level, toggleTheChat, toggleSound, chat, resign, checkmate, stalemate, white, black, createAGame, noGameAvailableRightNowCreateOne, whiteIsVictorious, blackIsVictorious, playWithTheSameOpponentAgain, newOpponent, playWithAnotherOpponent, yourOpponentWantsToPlayANewGameWithYou, joinTheGame, whitePlays, blackPlays, theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim, makeYourOpponentResign, forceResignation, talkInChat, theFirstPersonToComeOnThisUrlWillPlayWithYou, whiteCreatesTheGame, blackCreatesTheGame, whiteJoinsTheGame, blackJoinsTheGame, whiteResigned, blackResigned, whiteLeftTheGame, blackLeftTheGame, shareThisUrlToLetSpectatorsSeeTheGame, youAreViewingThisGameAsASpectator, replayAndAnalyse, viewGameStats, flipBoard, threefoldRepetition, claimADraw, offerDraw, draw, nbConnectedPlayers, talkAboutChessAndDiscussLichessFeaturesInTheForum, seeTheGamesBeingPlayedInRealTime, gamesBeingPlayedRightNow, viewAllNbGames, viewNbCheckmates, nbBookmarks, nbPopularGames, nbAnalysedGames, bookmarkedByNbPlayers, viewInFullSize, logOut, signIn, signUp, people, games, forum, chessPlayers, minutesPerSide, variant, timeControl, start, username, password, haveAnAccount, allYouNeedIsAUsernameAndAPassword, learnMoreAboutLichess, rank, gamesPlayed, declineInvitation, cancel, timeOut, drawOfferSent, drawOfferDeclined, drawOfferAccepted, drawOfferCanceled, yourOpponentOffersADraw, accept, decline, playingRightNow, abortGame, gameAborted, standard, unlimited, mode, casual, rated, thisGameIsRated, rematch, rematchOfferSent, rematchOfferAccepted, rematchOfferCanceled, rematchOfferDeclined, cancelRematchOffer, viewRematch, play, inbox, chatRoom, spectatorRoom, composeMessage, sentMessages, incrementInSeconds, freeOnlineChess, spectators, nbWins, nbLosses, nbDraws, exportGames, color, eloRange, giveNbSeconds, searchAPlayer, whoIsOnline, allPlayers, namedPlayers, premoveEnabledClickAnywhereToCancel, thisPlayerUsesChessComputerAssistance, opening, takeback, proposeATakeback, takebackPropositionSent, takebackPropositionDeclined, takebackPropositionAccepted, takebackPropositionCanceled, yourOpponentProposesATakeback, bookmarkThisGame, toggleBackground, advancedSearch, tournament, freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents) } diff --git a/app/tournament/DataForm.scala b/app/tournament/DataForm.scala new file mode 100644 index 0000000000..8895dafc41 --- /dev/null +++ b/app/tournament/DataForm.scala @@ -0,0 +1,16 @@ +package lila +package tournament + +import play.api.data._ +import play.api.data.Forms._ +import play.api.data.validation.Constraints._ + +final class DataForm { + + val create = Form(mapping( + "maxUsers" -> number.verifying(min(3), max(8)) + )(TournamentSetup.apply)(TournamentSetup.unapply)) +} + +case class TournamentSetup( + maxUsers: Int) diff --git a/app/tournament/Status.scala b/app/tournament/Status.scala new file mode 100644 index 0000000000..150997d85f --- /dev/null +++ b/app/tournament/Status.scala @@ -0,0 +1,26 @@ +package lila +package tournament + +sealed abstract class Status(val id: Int) extends Ordered[Status] { + + def compare(other: Status) = id compare other.id + + def name = toString + + def is(s: Status): Boolean = this == s + def is(f: Status.type ⇒ Status): Boolean = is(f(Status)) +} + +object Status { + + case object Created extends Status(10) + case object Started extends Status(20) + case object Aborted extends Status(25) // from this point the game is finished + case object Finished extends Status(30) + + val all = List(Created, Started, Aborted, Finished) + + val byId = all map { v ⇒ (v.id, v) } toMap + + def apply(id: Int): Option[Status] = byId get id +} diff --git a/app/tournament/Tournament.scala b/app/tournament/Tournament.scala index 7ed38b21a9..2a68343d34 100644 --- a/app/tournament/Tournament.scala +++ b/app/tournament/Tournament.scala @@ -6,8 +6,15 @@ import org.scala_tools.time.Imports._ import com.novus.salat.annotations.Key case class Tournament( - @Key("_id") id: String, - createdAt: DateTime, - createdBy: String) { + @Key("_id") id: String, + createdBy: String, + maxUsers: Int, + createdAt: DateTime = DateTime.now, + status: Status = Status.Created, + users: List[String]) { + +} + +object Tournament { } diff --git a/app/tournament/TournamentEnv.scala b/app/tournament/TournamentEnv.scala new file mode 100644 index 0000000000..e5ae9217b9 --- /dev/null +++ b/app/tournament/TournamentEnv.scala @@ -0,0 +1,20 @@ +package lila +package tournament + +import game.{ GameRepo, DbGame } +import core.Settings + +import com.traackr.scalastic.elasticsearch +import com.mongodb.casbah.MongoCollection + +final class TournamentEnv( + settings: Settings, + mongodb: String ⇒ MongoCollection) { + + import settings._ + + lazy val forms = new DataForm + + lazy val repo = new TournamentRepo( + collection = mongodb(TournamentCollectionTournament)) +} diff --git a/app/tournament/TournamentRepo.scala b/app/tournament/TournamentRepo.scala new file mode 100644 index 0000000000..853186b20e --- /dev/null +++ b/app/tournament/TournamentRepo.scala @@ -0,0 +1,27 @@ +package lila +package tournament + +import com.novus.salat._ +import com.novus.salat.dao._ +import com.mongodb.casbah.MongoCollection +import com.mongodb.casbah.query.Imports._ +import scalaz.effects._ +import org.joda.time.DateTime +import org.scala_tools.time.Imports._ + +class TournamentRepo(collection: MongoCollection) + extends SalatDAO[Tournament, String](collection) { + + def byId(id: String): IO[Option[Tournament]] = io { + findOneById(id) + } + + def created: IO[List[Tournament]] = io { + find(DBObject("status" -> Status.Created)) + .sort(DBObject("createdAt" -> -1)) + .toList + } + + private def idSelector(id: String): DBObject = DBObject("_id" -> id) + private def idSelector(tournament: Tournament): DBObject = idSelector(tournament.id) +} diff --git a/app/ui/SiteMenu.scala b/app/ui/SiteMenu.scala index 7bf56045d6..2a3c602b0f 100644 --- a/app/ui/SiteMenu.scala +++ b/app/ui/SiteMenu.scala @@ -13,12 +13,13 @@ final class SiteMenu(trans: I18nKeys) { val play = new Elem("play", routes.Lobby.home, trans.play) val game = new Elem("game", routes.Game.realtime, trans.games) + val tournament = new Elem("tournament", routes.Tournament.home, trans.tournament) val user = new Elem("user", routes.User.list(page = 1), trans.people) val forum = new Elem("forum", routes.ForumCateg.index, trans.forum) val message = new Elem("message", routes.Message.inbox(page = 1), trans.inbox) - private val authenticated = List(play, game, user, forum, message) - private val anonymous = List(play, game, user, forum) + private val authenticated = List(play, game, tournament, user, forum, message) + private val anonymous = List(play, game, tournament, user, forum) def all(me: Option[User]) = me.isDefined.fold(authenticated, anonymous) } diff --git a/app/views/tournament/form.scala.html b/app/views/tournament/form.scala.html new file mode 100644 index 0000000000..8054bdb09e --- /dev/null +++ b/app/views/tournament/form.scala.html @@ -0,0 +1,8 @@ +@(form: Form[_])(implicit ctx: Context) + +@tournament.layout( +title = "New tournament") { +

New tournament

+
+
+} diff --git a/app/views/tournament/home.scala.html b/app/views/tournament/home.scala.html new file mode 100644 index 0000000000..304ece4648 --- /dev/null +++ b/app/views/tournament/home.scala.html @@ -0,0 +1,13 @@ +@(tournaments: List[lila.tournament.Tournament])(implicit ctx: Context) + +@tournament.layout( +title = "Tournaments") { + +
+

Tournaments

+
+ list of tournaments +
+ Create a new tournament +
+} diff --git a/app/views/tournament/layout.scala.html b/app/views/tournament/layout.scala.html new file mode 100644 index 0000000000..207f7631ae --- /dev/null +++ b/app/views/tournament/layout.scala.html @@ -0,0 +1,12 @@ +@(title: String)(body: Html)(implicit ctx: Context) + +@moreCss = { +@cssTag("tournament.css") +} + +@base.layout( +title = title, +moreCss = moreCss, +active = siteMenu.tournament.some) { +
@body
+} diff --git a/conf/messages b/conf/messages index fa31b824ad..2365da0ce1 100644 --- a/conf/messages +++ b/conf/messages @@ -138,4 +138,5 @@ yourOpponentProposesATakeback=Your opponent proposes a takeback bookmarkThisGame=Bookmark this game toggleBackground=Toggle background color advancedSearch=Advanced Search +tournament=Tournament freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents=Free online Chess game. Play Chess now in a clean interface. No registration, no ads, no plugin required. Play Chess with computer, friends or random opponents. diff --git a/conf/messages.fr b/conf/messages.fr index 07693b39fd..b0acad8e73 100644 --- a/conf/messages.fr +++ b/conf/messages.fr @@ -138,4 +138,5 @@ yourOpponentProposesATakeback=Votre adversaire propose l'annulation du coup bookmarkThisGame=Mettre cette partie en favoris toggleBackground=Changer la couleur d'arrière plan advancedSearch=Recherche avancée +tournament=Tournoi freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents=Jeu d'échecs gratuit en ligne. Jouez aux échecs immédiatement avec une interface simple. Pas d'inscription obligatoire, pas de pub, pas de plugin. Jouez aux échecs contre l'ordinateur, des amis ou des adversaires en ligne. diff --git a/conf/routes b/conf/routes index ba9b824895..59b5c7b3b7 100644 --- a/conf/routes +++ b/conf/routes @@ -38,8 +38,14 @@ GET /$gameId<[\w\-]{8}>/$color/table controllers.Round.tableWatc GET /$fullId<[\w\-]{12}>/table controllers.Round.tablePlayer(fullId: String) GET /$gameId<[\w\-]{8}>/players controllers.Round.players(gameId: String) +# Tournament +GET /tournament controllers.Tournament.home +GET /tournament/$id<[\w\-]{8}> controllers.Tournament.show(id: String) +GET /tournament/new controllers.Tournament.form +POST /tournament/new controllers.Tournament.create + # Analyse -GET /analyse/$gameId<[\w\-]{8}> controllers.Analyse.replay(gameId: String, color: String = "white") +GET /analyse/$gameId<[\w\-]{8}> controllers.Analyse.replay(gameId: String, color: String = "white") GET /analyse/$gameId<[\w\-]{8}>/$color controllers.Analyse.replay(gameId: String, color: String) POST /analyse/$gameId<[\w\-]{8}>/$color/computer controllers.Analyse.computer(gameId: String, color: String) GET /$gameId<[\w\-]{8}>/stats controllers.Analyse.stats(gameId: String)