swiss WIP
parent
1408c888d6
commit
64761dafc9
|
@ -368,6 +368,9 @@ tournament {
|
|||
api_actor.name = tournament-api
|
||||
pairing.delay = 3.1 seconds
|
||||
}
|
||||
swiss {
|
||||
bbpairing = "/path/to/bbpPairings.exe"
|
||||
}
|
||||
simul {
|
||||
collection.simul = simul
|
||||
feature.views = 10000 # max frontpage views of simul per host per day
|
||||
|
|
|
@ -382,6 +382,7 @@ object mon {
|
|||
}
|
||||
object swiss {
|
||||
def standingOverload = counter("swiss.standing.overload").withoutTags
|
||||
val tick = future("swiss.tick")
|
||||
}
|
||||
object plan {
|
||||
val paypal = histogram("plan.amount").withTag("service", "paypal")
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
package lila.swiss
|
||||
|
||||
import com.softwaremill.macwire._
|
||||
import play.api.Configuration
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.socket.Socket.{ GetVersion, SocketVersion }
|
||||
import lila.common.config._
|
||||
import lila.common.{ AtMost, Every, ResilientScheduler }
|
||||
import lila.socket.Socket.{ GetVersion, SocketVersion }
|
||||
|
||||
@Module
|
||||
final class Env(
|
||||
remoteSocketApi: lila.socket.RemoteSocket,
|
||||
appConfig: Configuration,
|
||||
db: lila.db.Db,
|
||||
remoteSocketApi: lila.socket.RemoteSocket,
|
||||
chatApi: lila.chat.ChatApi,
|
||||
cacheApi: lila.memo.CacheApi,
|
||||
lightUserApi: lila.user.LightUserApi
|
||||
|
@ -23,6 +27,10 @@ final class Env(
|
|||
|
||||
private val colls = wire[SwissColls]
|
||||
|
||||
private val pairingSystem = new PairingSystem(appConfig.get[String]("swiss.bbpairing"))
|
||||
|
||||
private val director = wire[SwissDirector]
|
||||
|
||||
val api = wire[SwissApi]
|
||||
|
||||
private lazy val socket = wire[SwissSocket]
|
||||
|
@ -41,6 +49,13 @@ final class Env(
|
|||
private lazy val cache: SwissCache = wire[SwissCache]
|
||||
|
||||
lazy val getName = new GetSwissName(cache.name.sync)
|
||||
|
||||
ResilientScheduler(
|
||||
every = Every(2 seconds),
|
||||
atMost = AtMost(15 seconds),
|
||||
// initialDelay = 20 seconds
|
||||
initialDelay = 5 seconds
|
||||
) { api.tick }
|
||||
}
|
||||
|
||||
private class SwissColls(db: lila.db.Db) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package lila.swiss
|
|||
|
||||
import java.io.{ File, PrintWriter }
|
||||
import scala.util.chaining._
|
||||
import scala.sys.process._
|
||||
|
||||
final private class PairingSystem(executable: String) {
|
||||
|
||||
|
@ -10,11 +11,11 @@ final private class PairingSystem(executable: String) {
|
|||
players: List[SwissPlayer],
|
||||
pairings: List[SwissPairing]
|
||||
): List[SwissPairing.Pending] =
|
||||
writer(swiss, players, pairings) pipe invoke pipe reader
|
||||
writer(swiss, players, pairings).pp pipe invoke pipe reader
|
||||
|
||||
private def invoke(input: String): String =
|
||||
withTempFile(input) { file =>
|
||||
s"$executable --dutch $file -p"
|
||||
s"$executable --dutch $file -p".pp.!!
|
||||
}
|
||||
|
||||
private def reader(output: String): List[SwissPairing.Pending] =
|
||||
|
@ -35,6 +36,7 @@ final private class PairingSystem(executable: String) {
|
|||
|
||||
def apply(swiss: Swiss, players: List[SwissPlayer], pairings: List[SwissPairing]): String = {
|
||||
s"XXR ${swiss.nbRounds}" ::
|
||||
s"XXC ${chess.Color(scala.util.Random.nextBoolean).name}1" ::
|
||||
players.map(player(swiss, SwissPairing.toMap(pairings))).map(format)
|
||||
} mkString "\n"
|
||||
|
||||
|
@ -72,11 +74,12 @@ final private class PairingSystem(executable: String) {
|
|||
val p = new PrintWriter(file, "UTF-8")
|
||||
try {
|
||||
p.write(contents)
|
||||
p.flush()
|
||||
val res = f(file)
|
||||
file.delete()
|
||||
res
|
||||
} finally {
|
||||
p.close()
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package lila.swiss
|
||||
|
||||
import akka.stream.scaladsl._
|
||||
import org.joda.time.DateTime
|
||||
import ornicar.scalalib.Zero
|
||||
import reactivemongo.akkastream.cursorProducer
|
||||
import reactivemongo.api._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
@ -12,7 +14,8 @@ import lila.user.User
|
|||
|
||||
final class SwissApi(
|
||||
colls: SwissColls,
|
||||
socket: SwissSocket
|
||||
socket: SwissSocket,
|
||||
director: SwissDirector
|
||||
)(
|
||||
implicit ec: scala.concurrent.ExecutionContext,
|
||||
mat: akka.stream.Materializer,
|
||||
|
@ -25,8 +28,9 @@ final class SwissApi(
|
|||
import BsonHandlers._
|
||||
|
||||
def byId(id: Swiss.Id) = colls.swiss.byId[Swiss](id.value)
|
||||
def enterableById(id: Swiss.Id) = colls.swiss.byId[Swiss](id.value).dmap(_.filter(_.isEnterable))
|
||||
def startedById(id: Swiss.Id) = colls.swiss.byId[Swiss](id.value).dmap(_.filter(_.isStarted))
|
||||
def enterableById(id: Swiss.Id) = byId(id).dmap(_.filter(_.isEnterable))
|
||||
def createdById(id: Swiss.Id) = byId(id).dmap(_.filter(_.isCreated))
|
||||
def startedById(id: Swiss.Id) = byId(id).dmap(_.filter(_.isStarted))
|
||||
|
||||
def create(data: SwissForm.SwissData, me: User, teamId: TeamID): Fu[Swiss] = {
|
||||
val swiss = Swiss(
|
||||
|
@ -67,10 +71,10 @@ final class SwissApi(
|
|||
def join(id: Swiss.Id, me: User, isInTeam: TeamID => Boolean): Fu[Boolean] = Sequencing(id)(enterableById) {
|
||||
swiss =>
|
||||
isInTeam(swiss.teamId) ?? {
|
||||
val number = SwissPlayer.Number(swiss.nbPlayers + 1)
|
||||
colls.player.insert.one(SwissPlayer.make(swiss.id, number, me, swiss.perfLens)) >>
|
||||
updateNbPlayers(swiss.id) >>-
|
||||
socket.reload(swiss.id) inject true
|
||||
val number = SwissPlayer.Number(swiss.nbPlayers + 1).pp
|
||||
colls.player.insert.one(SwissPlayer.make(swiss.id, number, me, swiss.perfLens)) zip
|
||||
colls.swiss.updateField($id(swiss.id), "nbPlayers", number) >>-
|
||||
socket.reload(swiss.id) inject true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,10 +119,18 @@ final class SwissApi(
|
|||
else funit
|
||||
}
|
||||
|
||||
private def updateNbPlayers(swissId: Swiss.Id): Funit =
|
||||
colls.player.countSel($doc(SwissPlayer.Fields.swissId -> swissId)) flatMap {
|
||||
colls.swiss.updateField($id(swissId), "nbPlayers", _).void
|
||||
}
|
||||
private[swiss] def tick: Funit =
|
||||
colls.swiss.ext
|
||||
.find($doc("startsAt" $lt DateTime.now, "finishedAt" $exists false))
|
||||
.batchSize(1)
|
||||
.cursor[Swiss]()
|
||||
.documentSource()
|
||||
.mapAsync(2)(director.apply)
|
||||
.log(getClass.getName)
|
||||
.toMat(Sink.ignore)(Keep.right)
|
||||
.run
|
||||
.monSuccess(_.swiss.tick)
|
||||
.void
|
||||
|
||||
private def insertPairing(pairing: SwissPairing) =
|
||||
colls.pairing.insert.one {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package lila.swiss
|
||||
|
||||
import lila.db.dsl._
|
||||
|
||||
final private class SwissDirector(
|
||||
colls: SwissColls,
|
||||
pairingSystem: PairingSystem
|
||||
)(
|
||||
implicit ec: scala.concurrent.ExecutionContext
|
||||
) {
|
||||
import BsonHandlers._
|
||||
|
||||
def apply(swiss: Swiss): Funit =
|
||||
for {
|
||||
players <- SwissPlayer.fields { f =>
|
||||
colls.player.ext
|
||||
.find($doc(f.swissId -> swiss.id))
|
||||
.sort($sort asc f.number)
|
||||
.list[SwissPlayer]()
|
||||
}
|
||||
prevPairings <- SwissPairing.fields { f =>
|
||||
colls.pairing.ext
|
||||
.find($doc(f.swissId -> swiss.id))
|
||||
.sort($sort asc f.round)
|
||||
.list[SwissPairing]()
|
||||
}
|
||||
} yield {
|
||||
val pairings = pairingSystem(swiss, players, prevPairings)
|
||||
println(pairings)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue