From 632b29fb78a040a221070694e9dd1dceb09430e4 Mon Sep 17 00:00:00 2001 From: Claude Brisson Date: Mon, 3 Feb 2025 08:51:20 +0100 Subject: [PATCH] More team tournaments debugging --- .../jeudego/pairgoth/api/ResultsHandler.kt | 12 ++++- .../org/jeudego/pairgoth/model/Tournament.kt | 46 +++++++++++++++++-- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt index 6671080..d169b6c 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt @@ -4,6 +4,8 @@ import com.republicate.kson.Json import com.republicate.kson.toJsonArray import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest import org.jeudego.pairgoth.model.Game +import org.jeudego.pairgoth.model.TeamTournament +import org.jeudego.pairgoth.model.TeamTournament.Team import org.jeudego.pairgoth.model.toJson import org.jeudego.pairgoth.server.Event import javax.servlet.http.HttpServletRequest @@ -22,8 +24,11 @@ object ResultsHandler: PairgothApiHandler { val tournament = getTournament(request) val round = getSubSelector(request)?.toIntOrNull() ?: badRequest("invalid round number") val payload = getObjectPayload(request) - val game = tournament.games(round)[payload.getInt("id")] ?: badRequest("invalid game id") + val game = tournament.individualGames(round)[payload.getInt("id")] ?: badRequest("invalid game id") game.result = Game.Result.fromSymbol(payload.getChar("result") ?: badRequest("missing result")) + if (tournament is TeamTournament && tournament.type.individual) { + tournament.propagateIndividualResult(round, game) + } tournament.dispatchEvent(Event.ResultUpdated, request, Json.Object("round" to round, "data" to game)) return Json.Object("success" to true) } @@ -31,9 +36,12 @@ object ResultsHandler: PairgothApiHandler { override fun delete(request: HttpServletRequest, response: HttpServletResponse): Json { val tournament = getTournament(request) val round = getSubSelector(request)?.toIntOrNull() ?: badRequest("invalid round number") - for (game in tournament.games(round).values) { + for (game in tournament.individualGames(round).values) { game.result = Game.Result.UNKNOWN } + if (tournament is TeamTournament && tournament.type.individual) { + tournament.propagateIndividualResults(round) + } tournament.dispatchEvent(Event.ResultsCleared, request, Json.Object("round" to round)) return Json.Object("success" to true) } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt index b2c4ee9..9852270 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt @@ -16,6 +16,7 @@ import org.jeudego.pairgoth.util.mutableBiMultiMapOf import kotlin.math.max import java.util.* import java.util.regex.Pattern +import kotlin.collections.get import kotlin.math.roundToInt sealed class Tournament ( @@ -276,10 +277,15 @@ class TeamTournament( if (type.individual) { games.forEach { game -> individualGames.computeIfAbsent(game.id) { id -> + // Here white and black just denote the first board color val whitePlayers = teams[game.white]!!.activePlayers(round) val blackPlayers = teams[game.black]!!.activePlayers(round) - whitePlayers.zip(blackPlayers).map { - Game(nextGameId, game.table, it.first.id, it.second.id) + whitePlayers.zip(blackPlayers).mapIndexed { i, players -> + // alternate colors in the following boards + if ((i % 2) == 0) + Game(nextGameId, game.table, players.first.id, players.second.id) + else + Game(nextGameId, game.table, players.second.id, players.first.id) }.toMutableSet() } } @@ -355,6 +361,40 @@ class TeamTournament( it.playerIds.addAll(teamPlayersIds) } } + + fun propagateIndividualResult(round: Int, game: Game) { + val inverseMap = individualGames.inverse.map { it.key.id to it.value }.toMap() + val teamGameID = inverseMap[game.id] + val score = individualGames[teamGameID]?.sumOf { game -> + when (game.result) { + Game.Result.WHITE -> 1 + Game.Result.BLACK -> -1 + else -> 0 + } as Int + } ?: error("Team game not found: $teamGameID") + val teamGame = games[round - 1].get(teamGameID) ?: error("Team game not found: $teamGameID") + teamGame.result = + if (score < type.playersNumber / 2.0) Game.Result.BLACK + else if (score > type.playersNumber / 2.0) Game.Result.WHITE + else Game.Result.UNKNOWN + } + + fun propagateIndividualResults(round: Int) { + for (teamGame in games(round).values) { + val score = individualGames[teamGame.id]?.sumOf { game -> + when (game.result) { + Game.Result.WHITE -> 1 + Game.Result.BLACK -> -1 + else -> 0 + } as Int + } ?: error("Team game not found: $teamGame.id") + val teamGame = games[round - 1].get(teamGame.id) ?: error("Team game not found: $teamGame.id") + teamGame.result = + if (score < type.playersNumber / -2.0) Game.Result.BLACK + else if (score > type.playersNumber / 2.0) Game.Result.WHITE + else Game.Result.UNKNOWN + } + } } fun Pairable.asPlayer() = this as? Player @@ -406,7 +446,7 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n pairing = json.getObject("pairing")?.let { Pairing.fromJson(it, default?.pairing) } ?: default?.pairing ?: badRequest("missing pairing"), tablesExclusion = json.getArray("tablesExclusion")?.map { item -> item as String }?.toMutableList() ?: default?.tablesExclusion ?: mutableListOf(), individualGames = json.getObject("individualGames")?.entries?.flatMap { - (key, value) -> (value as Json.Array).map { game -> Game.fromJson(game as Json.Object) }.map { Pair(key.toID(), it) } + (key, value) -> (value as Json.Array).map { game -> Game.fromJson(game as Json.Object) }.map { key.toID() to it } }?.let { mutableBiMultiMapOf(*it.toTypedArray()) } ?: (default as? TeamTournament)?.individualGames ?: mutableBiMultiMapOf()