Partial handling of teams of individuals

This commit is contained in:
Claude Brisson
2023-06-05 13:28:17 +02:00
parent 76aa60f3c6
commit 121e1a22b7
4 changed files with 121 additions and 70 deletions

3
run.sh
View File

@@ -1,4 +1,3 @@
#!/bin/sh
mvn package
java -jar application/target/pairgoth-engine.war
mvn package && java -jar application/target/pairgoth-engine.war

View File

@@ -25,11 +25,11 @@ sealed class Tournament <P: Pairable>(
val komi: Double = 7.5
) {
companion object {}
enum class Type(val playersNumber: Int) {
enum class Type(val playersNumber: Int, val individual: Boolean = true) {
INDIVIDUAL(1),
PAIRGO(2),
RENGO2(2),
RENGO3(3),
PAIRGO(2, false),
RENGO2(2, false),
RENGO3(3, false),
TEAM2(2),
TEAM3(3),
TEAM4(4),
@@ -131,6 +131,7 @@ class TeamTournament(
"name" to name,
"players" to playerIds.toList().toJsonArray()
)
val teamOfIndividuals: Boolean get() = type.individual
}
fun teamFromJson(json: Json.Object, default: TeamTournament.Team? = null) = Team(

View File

@@ -0,0 +1,95 @@
package org.jeudego.pairgoth.pairing
import org.jeudego.pairgoth.model.Game
import org.jeudego.pairgoth.model.Pairable
import org.jeudego.pairgoth.model.TeamTournament
open class HistoryHelper(protected val history: List<Game>) {
open fun playedTogether(p1: Pairable, p2: Pairable) = paired.contains(Pair(p1.id, p2.id))
open fun colorBalance(p: Pairable) = colorBalance[p.id]
open fun score(p: Pairable) = score[p.id]
open fun sos(p: Pairable) = sos[p.id]
open fun sosos(p: Pairable) = sosos[p.id]
open fun sodos(p: Pairable) = sodos[p.id]
protected val paired: Set<Pair<Int, Int>> by lazy {
(history.map { game ->
Pair(game.black, game.white)
} + history.map { game ->
Pair(game.white, game.black)
}).toSet()
}
private val colorBalance: Map<Int, Int> by lazy {
history.flatMap { game ->
listOf(Pair(game.white, +1), Pair(game.black, -1))
}.groupingBy { it.first }.fold(0) { acc, next ->
acc + next.second
}
}
private val score: Map<Int, Double> by lazy {
mutableMapOf<Int, Double>().apply {
history.forEach { game ->
when (game.result) {
Game.Result.BLACK -> put(game.black, getOrDefault(game.black, 0.0) + 1.0)
Game.Result.WHITE -> put(game.white, getOrDefault(game.white, 0.0) + 1.0)
Game.Result.BOTHWIN -> {
put(game.black, getOrDefault(game.black, 0.0) + 0.5)
put(game.white, getOrDefault(game.white, 0.0) + 0.5)
}
else -> {}
}
}
}
}
private val sos by lazy {
(history.map { game ->
Pair(game.black, score[game.white] ?: 0.0)
} + history.map { game ->
Pair(game.white, score[game.black] ?: 0.0)
}).groupingBy { it.first }.fold(0.0) { acc, next ->
acc + next.second
}
}
private val sosos by lazy {
(history.map { game ->
Pair(game.black, sos[game.white] ?: 0.0)
} + history.map { game ->
Pair(game.white, sos[game.black] ?: 0.0)
}).groupingBy { it.first }.fold(0.0) { acc, next ->
acc + next.second
}
}
private val sodos by lazy {
(history.map { game ->
Pair(game.black, if (game.result == Game.Result.BLACK) score[game.white] ?: 0.0 else 0.0)
} + history.map { game ->
Pair(game.white, if (game.result == Game.Result.WHITE) score[game.black] ?: 0.0 else 0.0)
}).groupingBy { it.first }.fold(0.0) { acc, next ->
acc + next.second
}
}
}
// CB TODO - a big problem with the current naive implementation is that the team score is -for now- the sum of team members individual scores
class TeamOfIndividualsHistoryHelper(history: List<Game>): HistoryHelper(history) {
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 score(p: Pairable) = p.asTeam().teamPlayers.map { super.score(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()
}

View File

@@ -4,6 +4,7 @@ import org.jeudego.pairgoth.model.Game
import org.jeudego.pairgoth.model.Game.Result.*
import org.jeudego.pairgoth.model.Pairable
import org.jeudego.pairgoth.model.Pairing
import org.jeudego.pairgoth.model.TeamTournament
import org.jeudego.pairgoth.store.Store
import org.jgrapht.alg.matching.blossom.v5.KolmogorovWeightedPerfectMatching
import org.jgrapht.alg.matching.blossom.v5.ObjectiveSense
@@ -13,7 +14,15 @@ import org.jgrapht.graph.SimpleWeightedGraph
import org.jgrapht.graph.builder.GraphBuilder
import java.util.*
sealed class Solver(val history: List<Game>, val pairables: List<Pairable>, val weights: Pairing.Weights) {
interface HistoryDigester {
val colorBalance: Map<Int, Int>
val score: Map<Int, Double>
val sos: Map<Int, Double>
val sosos: Map<Int, Double>
val sodos: Map<Int, Double>
}
sealed class Solver(history: List<Game>, val pairables: List<Pairable>, val weights: Pairing.Weights) {
companion object {
val rand = Random(/* seed from properties - TODO */)
@@ -53,6 +62,10 @@ sealed class Solver(val history: List<Game>, val pairables: List<Pairable>, val
val n = pairables.size
private val historyHelper =
if (pairables.first().let { it is TeamTournament.Team && it.teamOfIndividuals }) TeamOfIndividualsHistoryHelper(history)
else HistoryHelper(history)
// pairables sorted using overloadable sort function
private val sortedPairables by lazy {
pairables.sortedWith(::sort)
@@ -79,77 +92,20 @@ sealed class Solver(val history: List<Game>, val pairables: List<Pairable>, val
}
// already paired players map
fun Pairable.played(other: Pairable) = _paired.contains(Pair(id, other.id))
private val _paired: Set<Pair<Int, Int>> by lazy {
(history.map { game ->
Pair(game.black, game.white)
} + history.map { game ->
Pair(game.white, game.black)
}).toSet()
}
fun Pairable.played(other: Pairable) = historyHelper.playedTogether(this, other)
// color balance (nw - nb)
val Pairable.colorBalance: Int get() = _colorBalance[id] ?: 0
private val _colorBalance: Map<Int, Int> by lazy {
history.flatMap { game ->
listOf(Pair(game.white, +1), Pair(game.black, -1))
}.groupingBy { it.first }.fold(0) { acc, next ->
acc + next.second
}
}
val Pairable.colorBalance: Int get() = historyHelper.colorBalance(this) ?: 0
// score (number of wins)
val Pairable.score: Double get() = _score[id] ?: 0.0
private val _score: Map<Int, Double> by lazy {
mutableMapOf<Int, Double>().apply {
history.forEach { game ->
when (game.result) {
BLACK -> put(game.black, getOrDefault(game.black, 0.0) + 1.0)
WHITE -> put(game.white, getOrDefault(game.white, 0.0) + 1.0)
BOTHWIN -> {
put(game.black, getOrDefault(game.black, 0.0) + 0.5)
put(game.white, getOrDefault(game.white, 0.0) + 0.5)
}
else -> {}
}
}
}
}
val Pairable.score: Double get() = historyHelper.score(this) ?: 0.0
// sos
val Pairable.sos: Double get() = _sos[id] ?: 0.0
private val _sos by lazy {
(history.map { game ->
Pair(game.black, _score[game.white] ?: 0.0)
} + history.map { game ->
Pair(game.white, _score[game.black] ?: 0.0)
}).groupingBy { it.first }.fold(0.0) { acc, next ->
acc + next.second
}
}
val Pairable.sos: Double get() = historyHelper.sos(this) ?: 0.0
// sosos
val Pairable.sosos: Double get() = _sosos[id] ?: 0.0
private val _sosos by lazy {
(history.map { game ->
Pair(game.black, _sos[game.white] ?: 0.0)
} + history.map { game ->
Pair(game.white, _sos[game.black] ?: 0.0)
}).groupingBy { it.first }.fold(0.0) { acc, next ->
acc + next.second
}
}
val Pairable.sosos: Double get() = historyHelper.sosos(this) ?: 0.0
// sodos
val Pairable.sodos: Double get() = _sodos[id] ?: 0.0
private val _sodos by lazy {
(history.map { game ->
Pair(game.black, if (game.result == BLACK) _score[game.white] ?: 0.0 else 0.0)
} + history.map { game ->
Pair(game.white, if (game.result == WHITE) _score[game.black] ?: 0.0 else 0.0)
}).groupingBy { it.first }.fold(0.0) { acc, next ->
acc + next.second
}
}
val Pairable.sodos: Double get() = historyHelper.sodos(this) ?: 0.0
}