Consistency checks on participations changes ; plus some code cleaning

This commit is contained in:
Claude Brisson
2025-01-26 11:51:22 +01:00
parent 169546ae66
commit 0ed9bfb5eb
5 changed files with 37 additions and 23 deletions

View File

@@ -4,6 +4,7 @@ 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.model.Player import org.jeudego.pairgoth.model.Player
import org.jeudego.pairgoth.model.TeamTournament
import org.jeudego.pairgoth.model.fromJson import org.jeudego.pairgoth.model.fromJson
import org.jeudego.pairgoth.server.Event.* import org.jeudego.pairgoth.server.Event.*
import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletRequest
@@ -44,12 +45,25 @@ object PlayerHandler: PairgothApiHandler {
val leavingRounds = updated.skip.toSet().minus(player.skip.toSet()) val leavingRounds = updated.skip.toSet().minus(player.skip.toSet())
leavingRounds.forEach { round -> leavingRounds.forEach { round ->
if (round <= tournament.lastRound()) { if (round <= tournament.lastRound()) {
val playing = tournament.games(round).values.flatMap { listOf(it.black, it.white) } val playing = tournament.pairedPlayers(round)
if (playing.contains(id)) { if (playing.contains(id)) {
badRequest("player is playing in round #$round") badRequest("player is playing in round #$round")
} }
} }
} }
if (tournament is TeamTournament) {
// participations cannot be changed in an already paired team
val joiningRounds = player.skip.toSet().minus(updated.skip.toSet())
val changedRounds = leavingRounds.union(joiningRounds)
changedRounds.forEach { round ->
if (round <= tournament.lastRound()) {
val team = tournament.getPlayerTeam(id)
if (team != null && tournament.pairedTeams().contains(team.id)) {
badRequest("number of active players for team #${team.id} cannot be changed for round $round")
}
}
}
}
tournament.players[id] = updated tournament.players[id] = updated
tournament.dispatchEvent(PlayerUpdated, request, player.toJson()) tournament.dispatchEvent(PlayerUpdated, request, player.toJson())
return Json.Object("success" to true) return Json.Object("success" to true)

View File

@@ -71,7 +71,7 @@ sealed class Tournament <P: Pairable>(
} }
// games per id for each round // games per id for each round
private val games = mutableListOf<MutableMap<ID, Game>>() protected val games = mutableListOf<MutableMap<ID, Game>>()
fun games(round: Int) = games.getOrNull(round - 1) ?: fun games(round: Int) = games.getOrNull(round - 1) ?:
if (round > games.size + 1) throw Error("invalid round") if (round > games.size + 1) throw Error("invalid round")
@@ -160,7 +160,10 @@ sealed class Tournament <P: Pairable>(
return changed return changed
} }
fun pairedPlayers() = games.flatMap { it.values }.flatMap { listOf(it.black, it.white) }.toSet() open fun pairedPlayers() = games.flatMap { it.values }.flatMap { listOf(it.black, it.white) }.toSet()
open fun pairedPlayers(round: Int) = games(round).values.flatMap { listOf(it.black, it.white) }.filter { it != 0 }.toSet()
fun hasPlayer(dbId: DatabaseId, pId: String) = pId.isNotBlank() && players.values.filter { player -> pId == player.externalIds[dbId] }.isNotEmpty() fun hasPlayer(dbId: DatabaseId, pId: String) = pId.isNotBlank() && players.values.filter { player -> pId == player.externalIds[dbId] }.isNotEmpty()
fun stats() = (0..rounds - 1).map { index -> fun stats() = (0..rounds - 1).map { index ->
@@ -238,6 +241,12 @@ class TeamTournament(
override val players = mutableMapOf<ID, Player>() override val players = mutableMapOf<ID, Player>()
val teams: MutableMap<ID, Team> = _pairables val teams: MutableMap<ID, Team> = _pairables
fun pairedTeams() = super.pairedPlayers()
override fun pairedPlayers() = super.pairedPlayers().flatMap { pairables[it]!!.asTeam()!!.playerIds }.toSet()
override fun pairedPlayers(round: Int) = super.pairedPlayers(round).flatMap { pairables[it]!!.asTeam()!!.playerIds }.toSet()
private fun List<Int>.average(provider: (Player)->Int) = ((sumOf {id -> provider(players[id]!!)} - epsilon) / size).roundToInt() private fun List<Int>.average(provider: (Player)->Int) = ((sumOf {id -> provider(players[id]!!)} - epsilon) / size).roundToInt()
inner class Team(id: ID, name: String, rating: Int, rank: Int, final: Boolean, mmsCorrection: Int = 0): Pairable(id, name, rating, rank, final, mmsCorrection) { inner class Team(id: ID, name: String, rating: Int, rank: Int, final: Boolean, mmsCorrection: Int = 0): Pairable(id, name, rating, rank, final, mmsCorrection) {
@@ -260,11 +269,11 @@ class TeamTournament(
} }
val teamOfIndividuals: Boolean get() = type.individual val teamOfIndividuals: Boolean get() = type.individual
// override val skip get() = playerIds.map { players[it]!!.skip }.reduce { left, right -> (left union right) as MutableSet<Int> }
override fun canPlay(round: Int) = teamPlayers.filter { it.canPlay(round) }.size == type.playersNumber override fun canPlay(round: Int) = teamPlayers.filter { it.canPlay(round) }.size == type.playersNumber
} }
fun getPlayerTeam(playerId: ID) = teams.values.filter { it.playerIds.contains(playerId) }.firstOrNull()
fun teamFromJson(json: Json.Object, default: TeamTournament.Team? = null): Team { fun teamFromJson(json: Json.Object, default: TeamTournament.Team? = null): Team {
val teamPlayersIds = json.getArray("players")?.let { arr -> val teamPlayersIds = json.getArray("players")?.let { arr ->
arr.map { arr.map {
@@ -287,6 +296,9 @@ class TeamTournament(
} }
} }
fun Pairable.asPlayer() = this as? Player
fun Pairable.asTeam() = this as? TeamTournament.Team
// Serialization // Serialization
fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = null): Tournament<*> { fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = null): Tournament<*> {

View File

@@ -2,6 +2,7 @@ package org.jeudego.pairgoth.pairing
import org.jeudego.pairgoth.model.* import org.jeudego.pairgoth.model.*
import org.jeudego.pairgoth.model.Game.Result.* import org.jeudego.pairgoth.model.Game.Result.*
import org.jeudego.pairgoth.model.TeamTournament.Team
open class HistoryHelper( open class HistoryHelper(
protected val history: List<List<Game>>, protected val history: List<List<Game>>,
@@ -247,18 +248,4 @@ open class HistoryHelper(
class TeamOfIndividualsHistoryHelper(history: List<List<Game>>, scoresGetter: () -> Map<ID, Pair<Double, Double>>): class TeamOfIndividualsHistoryHelper(history: List<List<Game>>, scoresGetter: () -> Map<ID, Pair<Double, Double>>):
HistoryHelper(history, { scoresGetter() }) { HistoryHelper(history, { scoresGetter() }) {
private fun Pairable.asTeam() = this as TeamTournament.Team
override fun playedTogether(p1: Pairable, p2: Pairable) = paired.intersect(p1.asTeam().playerIds.first().let { id ->
(p2.asTeam()).playerIds.map {Pair(it, id) }
}.toSet()).isNotEmpty()
override fun nbW(p: Pairable) = p.asTeam().teamPlayers.map { super.nbW(it) ?: throw Error("unknown player id: #${it.id}") }.sum()
//override fun sos(p:Pairable) = p.asTeam().teamPlayers.map { super.sos(it) ?: throw Error("unknown player id: #${it.id}") }.sum()
//override fun sosos(p:Pairable) = p.asTeam().teamPlayers.map { super.sosos(it) ?: throw Error("unknown player id: #${it.id}") }.sum()
//override fun sodos(p:Pairable) = p.asTeam().teamPlayers.map { super.sodos(it) ?: throw Error("unknown player id: #${it.id}") }.sum()
// TODO CB - now that we've got the rounds in history helper, calculate virtual scores
// also - try to factorize a bit calculations
} }

View File

@@ -40,9 +40,10 @@ function leave(teamId, playerId) {
let team = teams.get(teamId); let team = teams.get(teamId);
let index = team.players.indexOf(playerId); let index = team.players.indexOf(playerId);
if (index > -1) { if (index > -1) {
team.players.splice(index, 1); let newPlayers = team.players.slice();
newPlayers.splice(index, 1);
api.putJson(`tour/${tour_id}/team/${teamId}`, { api.putJson(`tour/${tour_id}/team/${teamId}`, {
"players": team.players "players": newPlayers
}).then(rst => { }).then(rst => {
if (rst !== 'error') { if (rst !== 'error') {
document.location.reload(); document.location.reload();

View File

@@ -99,8 +99,8 @@
#set($black = $pmap[$game.b]) #set($black = $pmap[$game.b])
<tr> <tr>
<td class="t" data-table="${game.t}">${game.t}</td> <td class="t" data-table="${game.t}">${game.t}</td>
<td class="left">#if($white)${white.name} ${white.firstname} (#rank($white.rank), $white.country $white.club)#{else}BIP#end</td> <td class="left">#if($white)${white.name} $!{white.firstname} (#rank($white.rank) $!white.country $!white.club)#{else}BIP#end</td>
<td class="left">#if($black)${black.name} ${black.firstname} (#rank($black.rank), $black.country $black.club)#{else}BIP#end</td> <td class="left">#if($black)${black.name} $!{black.firstname} (#rank($black.rank) $!black.country $!black.club)#{else}BIP#end</td>
<td>${game.h}</td> <td>${game.h}</td>
</tr> </tr>
#end #end