Table numbers exclusion is functional

This commit is contained in:
Claude Brisson
2024-07-27 16:34:35 +02:00
parent 6e97c91b2f
commit 471b316dce
4 changed files with 44 additions and 2 deletions

View File

@@ -3,6 +3,7 @@ package org.jeudego.pairgoth.api
import com.republicate.kson.Json import com.republicate.kson.Json
import com.republicate.kson.toJsonArray import com.republicate.kson.toJsonArray
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
import org.jeudego.pairgoth.api.TournamentHandler.dispatchEvent
import org.jeudego.pairgoth.model.Game import org.jeudego.pairgoth.model.Game
import org.jeudego.pairgoth.model.getID import org.jeudego.pairgoth.model.getID
import org.jeudego.pairgoth.model.toID import org.jeudego.pairgoth.model.toID
@@ -38,6 +39,7 @@ object PairingHandler: PairgothApiHandler {
if (round > tournament.lastRound() + 1) badRequest("invalid round: previous round has not been played") if (round > tournament.lastRound() + 1) badRequest("invalid round: previous round has not been played")
val payload = getArrayPayload(request) val payload = getArrayPayload(request)
if (payload.isEmpty()) badRequest("nobody to pair") if (payload.isEmpty()) badRequest("nobody to pair")
// CB TODO - change convention to empty array for all players
val allPlayers = payload.size == 1 && payload[0] == "all" val allPlayers = payload.size == 1 && payload[0] == "all"
//if (!allPlayers && tournament.pairing.type == PairingType.SWISS) badRequest("Swiss pairing requires all pairable players") //if (!allPlayers && tournament.pairing.type == PairingType.SWISS) badRequest("Swiss pairing requires all pairable players")
val playing = (tournament.games(round).values).flatMap { val playing = (tournament.games(round).values).flatMap {
@@ -57,6 +59,10 @@ object PairingHandler: PairgothApiHandler {
} ?: badRequest("invalid pairable id: #$id") } ?: badRequest("invalid pairable id: #$id")
} }
val games = tournament.pair(round, pairables) val games = tournament.pair(round, pairables)
// always renumber table to take table exclusion into account
tournament.renumberTables(round)
val ret = games.map { it.toJson() }.toJsonArray() val ret = games.map { it.toJson() }.toJsonArray()
tournament.dispatchEvent(GamesAdded, request, Json.Object("round" to round, "games" to ret)) tournament.dispatchEvent(GamesAdded, request, Json.Object("round" to round, "games" to ret))
return ret return ret
@@ -122,6 +128,14 @@ object PairingHandler: PairgothApiHandler {
return Json.Object("success" to true) return Json.Object("success" to true)
} else { } else {
// without id, it's a table renumbering // without id, it's a table renumbering
if (payload.containsKey("excludeTables")) {
val tablesExclusion = payload.getString("excludeTables") ?: badRequest("missing 'excludeTables'")
TournamentHandler.validateTablesExclusion(tablesExclusion)
while (tournament.tablesExclusion.size < round) tournament.tablesExclusion.add("")
tournament.tablesExclusion[round - 1] = tablesExclusion
tournament.dispatchEvent(TournamentUpdated, request, tournament.toJson())
}
val sortedPairables = tournament.getSortedPairables(round) val sortedPairables = tournament.getSortedPairables(round)
val sortedMap = sortedPairables.associateBy { val sortedMap = sortedPairables.associateBy {
it.getID()!! it.getID()!!

View File

@@ -91,7 +91,7 @@ object TournamentHandler: PairgothApiHandler {
return Json.Object("success" to true) return Json.Object("success" to true)
} }
private fun validateTablesExclusion(exclusion: String) { internal fun validateTablesExclusion(exclusion: String) {
if (!tablesExclusionValidator.matches(exclusion)) badRequest("invalid tables exclusion pattern") if (!tablesExclusionValidator.matches(exclusion)) badRequest("invalid tables exclusion pattern")
} }

View File

@@ -6,10 +6,12 @@ import com.republicate.kson.toJsonArray
//import kotlinx.datetime.LocalDate //import kotlinx.datetime.LocalDate
import java.time.LocalDate import java.time.LocalDate
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
import org.jeudego.pairgoth.api.ApiHandler.Companion.logger
import org.jeudego.pairgoth.store.nextPlayerId import org.jeudego.pairgoth.store.nextPlayerId
import org.jeudego.pairgoth.store.nextTournamentId import org.jeudego.pairgoth.store.nextTournamentId
import kotlin.math.max import kotlin.math.max
import java.util.* import java.util.*
import java.util.regex.Pattern
import kotlin.math.roundToInt import kotlin.math.roundToInt
sealed class Tournament <P: Pairable>( sealed class Tournament <P: Pairable>(
@@ -127,9 +129,12 @@ sealed class Tournament <P: Pairable>(
fun renumberTables(round: Int, pivot: Game? = null, orderBY: (Game) -> Int = ::defaultGameOrderBy): Boolean { fun renumberTables(round: Int, pivot: Game? = null, orderBY: (Game) -> Int = ::defaultGameOrderBy): Boolean {
var changed = false var changed = false
var nextTable = 1 var nextTable = 1
val excluded = excludedTables(round)
games(round).values.filter{ game -> pivot?.let { pivot.id != game.id } ?: true }.sortedBy(orderBY).forEach { game -> games(round).values.filter{ game -> pivot?.let { pivot.id != game.id } ?: true }.sortedBy(orderBY).forEach { game ->
while (excluded.contains(nextTable)) ++nextTable
if (pivot != null && nextTable == pivot.table) { if (pivot != null && nextTable == pivot.table) {
++nextTable ++nextTable
while (excluded.contains(nextTable)) ++nextTable
} }
if (game.table != 0) { if (game.table != 0) {
changed = changed || game.table != nextTable changed = changed || game.table != nextTable
@@ -150,6 +155,22 @@ sealed class Tournament <P: Pairable>(
"ready" to (games.getOrNull(index)?.values?.count { it.result != Game.Result.UNKNOWN } ?: 0) "ready" to (games.getOrNull(index)?.values?.count { it.result != Game.Result.UNKNOWN } ?: 0)
) )
}.toJsonArray() }.toJsonArray()
fun excludedTables(round: Int): Set<Int> {
if (round > tablesExclusion.size) return emptySet()
val excluded = mutableSetOf<Int>()
val parser = Regex("(\\d+)(?:-(\\d+))?")
parser.findAll(tablesExclusion[round - 1]).forEach { match ->
val left = match.groupValues[1].toInt()
val right = match.groupValues[2].let { if (it.isEmpty()) left else it.toInt() }
var t = left
do {
excluded.add(t)
++t
} while (t <= right)
}
return excluded
}
} }
// standard tournament of individuals // standard tournament of individuals

View File

@@ -38,7 +38,14 @@ function unpair(games) {
} }
function renumberTables() { function renumberTables() {
api.putJson(`tour/${tour_id}/pair/${activeRound}`, {}) let payload = {}
let tablesExclusionControl = $('#exclude-tables');
let value = tablesExclusionControl[0].value;
let origValue = tablesExclusionControl.data('orig');
if (value !== origValue) {
payload['excludeTables'] = value;
}
api.putJson(`tour/${tour_id}/pair/${activeRound}`, payload)
.then(rst => { .then(rst => {
if (rst !== 'error') { if (rst !== 'error') {
document.location.reload(); document.location.reload();