tournament pairing countdown WIP

tournament-pairing-countdown
Thibault Duplessis 2015-09-28 09:48:39 +02:00
parent 9130dad304
commit 156d92d1ba
8 changed files with 43 additions and 20 deletions

View File

@ -44,6 +44,7 @@ object BSONHandlers {
createdAt = r date "createdAt",
createdBy = r str "createdBy",
startsAt = startsAt,
pairsAt = r dateO "pairsAt",
winnerId = r strO "winner")
}
def writes(w: BSON.Writer, o: Tournament) = BSONDocument(
@ -66,6 +67,7 @@ object BSONHandlers {
"createdAt" -> w.date(o.createdAt),
"createdBy" -> w.str(o.createdBy),
"startsAt" -> w.date(o.startsAt),
"pairsAt" -> o.pairsAt.map(w.date),
"winner" -> o.winnerId)
}

View File

@ -41,6 +41,7 @@ final class Env(
val SequencerTimeout = config duration "sequencer.timeout"
val SequencerMapName = config getString "sequencer.map_name"
val NetDomain = config getString "net.domain"
val PairingDelay = config duration "pairing.delay"
}
import settings._
@ -54,6 +55,7 @@ final class Env(
system = system,
sequencers = sequencerMap,
autoPairing = autoPairing,
pairingDelay = PairingDelay,
clearJsonViewCache = jsonView.clearCache,
router = hub.actor.router,
renderer = hub.actor.renderer,
@ -120,7 +122,7 @@ final class Env(
organizer -> actorApi.AllCreatedTournaments
}
scheduler.message(3 seconds) {
scheduler.message(PairingDelay) {
organizer -> actorApi.StartedTournaments
}

View File

@ -1,13 +1,13 @@
package lila.tournament
import actorApi._
import akka.actor._
import akka.pattern.{ ask, pipe }
import actorApi._
import lila.game.actorApi.FinishGame
import lila.hub.actorApi.map.Ask
import lila.hub.actorApi.WithUserIds
import makeTimeout.short
import org.joda.time.DateTime
private[tournament] final class Organizer(
api: TournamentApi,
@ -36,7 +36,7 @@ private[tournament] final class Organizer(
}
case StartedTournaments =>
val startAt = nowMillis
val startAt = DateTime.now
TournamentRepo.started foreach {
_ foreach { tour =>
PlayerRepo activeUserIds tour.id foreach { activeUserIds =>
@ -62,7 +62,7 @@ private[tournament] final class Organizer(
_ filterNot isOnline foreach { api.withdraw(tour.id, _) }
}
private def startPairing(tour: Tournament, activeUserIds: List[String], startAt: Long) =
private def startPairing(tour: Tournament, activeUserIds: List[String], startAt: DateTime) =
getWaitingUsers(tour) zip PairingRepo.playingUserIds(tour) foreach {
case (waitingUsers, playingUserIds) =>
val users = waitingUsers intersect activeUserIds diff playingUserIds

View File

@ -23,6 +23,7 @@ case class Tournament(
createdAt: DateTime,
createdBy: String,
startsAt: DateTime,
pairsAt: Option[DateTime] = None,
winnerId: Option[String] = None) {
def isCreated = status == Status.Created

View File

@ -27,6 +27,7 @@ private[tournament] final class TournamentApi(
system: ActorSystem,
sequencers: ActorRef,
autoPairing: AutoPairing,
pairingDelay: Duration,
clearJsonViewCache: String => Funit,
router: ActorSelection,
renderer: ActorSelection,
@ -59,19 +60,21 @@ private[tournament] final class TournamentApi(
TournamentRepo.insert(created).void >>- publish()
}
def makePairings(oldTour: Tournament, users: WaitingUsers, startAt: Long) {
def makePairings(oldTour: Tournament, users: WaitingUsers, startAt: DateTime) {
Sequencing(oldTour.id)(TournamentRepo.startedById) { tour =>
tour.system.pairingSystem.createPairings(tour, users) flatMap {
case Nil => funit
case pairings if nowMillis - startAt > 1000 =>
play.api.Logger("tourpairing").warn(s"Give up making http://lichess.org/tournament/${tour.id} ${pairings.size} pairings in ${nowMillis - startAt}ms")
case pairings if nowMillis - startAt.getMillis > 1000 =>
play.api.Logger("tourpairing").warn(s"Give up making http://lichess.org/tournament/${tour.id} ${pairings.size} pairings in ${nowMillis - startAt.getMillis}ms")
funit
case pairings => pairings.map { pairing =>
PairingRepo.insert(pairing) >> autoPairing(tour, pairing)
}.sequenceFu.map {
_ map StartGame.apply foreach { sendTo(tour.id, _) }
}.sequenceFu.flatMap { games =>
TournamentRepo.setPairsAt(tour.id, startAt plus pairingDelay.toMillis) inject {
games map StartGame.apply foreach { sendTo(tour.id, _) }
}
} >>- {
val time = nowMillis - startAt
val time = nowMillis - startAt.getMillis
if (time > 100)
play.api.Logger("tourpairing").debug(s"Done making http://lichess.org/tournament/${tour.id} ${pairings.size} pairings in ${time}ms")
}

View File

@ -89,6 +89,11 @@ object TournamentRepo {
BSONDocument("$set" -> BSONDocument("winner" -> userId))
).void
def setPairsAt(tourId: String, pairsAt: DateTime) = coll.update(
selectId(tourId),
BSONDocument("$set" -> BSONDocument("pairsAt" -> pairsAt))
).void
private def allCreatedSelect(aheadMinutes: Int) = createdSelect ++ BSONDocument(
"$or" -> BSONArray(
BSONDocument("schedule" -> BSONDocument("$exists" -> false)),

View File

@ -1,6 +1,7 @@
package lila.tournament
import org.joda.time.DateTime
import scala.concurrent.duration._
private[tournament] case class WaitingUsers(
hash: Map[String, DateTime],
@ -11,10 +12,12 @@ private[tournament] case class WaitingUsers(
// 3+0 -> 16 -> 16
// 5+0 -> 24 -> 24
// 10+0 -> 44 -> 35
private val waitSeconds = {
val waitSeconds = {
(clock.fold(60)(_.estimateTotalTime) / 15) + 4
} min 35 max 10
val waitDuration = waitSeconds.seconds
lazy val all = hash.keys.toList
lazy val size = hash.size
@ -26,10 +29,6 @@ private[tournament] case class WaitingUsers(
else all
}
def waitSecondsOf(userId: String) = hash get userId map { d =>
nowSeconds - d.getSeconds
}
def waiting = {
val since = date minusSeconds waitSeconds
hash.collect {
@ -37,6 +36,16 @@ private[tournament] case class WaitingUsers(
}.toList
}
def minWaitToPairing: Option[Duration] =
hash.foldLeft(none[Duration]) {
case (res, (u, d)) =>
case (u, d) if d.isBefore(since) => u
}.toList
def waitSecondsOf(userId: String) = hash get userId map { d =>
nowSeconds - d.getSeconds
}
def update(us: Seq[String], clock: Option[chess.Clock]) = {
val newDate = DateTime.now
copy(

View File

@ -2,6 +2,7 @@ package lila.tournament
package arena
import lila.tournament.{ PairingSystem => AbstractPairingSystem }
import scala.concurrent.duration._
import scala.util.Random
@ -18,16 +19,16 @@ object PairingSystem extends AbstractPairingSystem {
// then pair all users
def createPairings(
tour: Tournament,
users: WaitingUsers): Fu[Pairings] = for {
users: WaitingUsers): Fu[(Pairings, Option[Duration])] = for {
recentPairings <- PairingRepo.recentByTourAndUserIds(tour.id, users.all, Math.min(120, users.size * 5))
nbActiveUsers <- PlayerRepo.countActive(tour.id)
ranking <- PlayerRepo.ranking(tour.id)
data = Data(tour, recentPairings, ranking, nbActiveUsers)
pairings <- {
if (recentPairings.isEmpty) evenOrAll(data, users)
if (recentPairings.isEmpty) evenOrAll(data, users).map(_ -> none)
else tryPairings(data, users.waiting) flatMap {
case Nil => fuccess(Nil)
case _ => evenOrAll(data, users)
case Nil => fuccess(Nil -> none)
case _ => evenOrAll(data, users).map(_ -> none)
}
}
} yield pairings