Partial handling of teams of individuals
This commit is contained in:
3
run.sh
3
run.sh
@@ -1,4 +1,3 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
mvn package
|
mvn package && java -jar application/target/pairgoth-engine.war
|
||||||
java -jar application/target/pairgoth-engine.war
|
|
||||||
|
@@ -25,11 +25,11 @@ sealed class Tournament <P: Pairable>(
|
|||||||
val komi: Double = 7.5
|
val komi: Double = 7.5
|
||||||
) {
|
) {
|
||||||
companion object {}
|
companion object {}
|
||||||
enum class Type(val playersNumber: Int) {
|
enum class Type(val playersNumber: Int, val individual: Boolean = true) {
|
||||||
INDIVIDUAL(1),
|
INDIVIDUAL(1),
|
||||||
PAIRGO(2),
|
PAIRGO(2, false),
|
||||||
RENGO2(2),
|
RENGO2(2, false),
|
||||||
RENGO3(3),
|
RENGO3(3, false),
|
||||||
TEAM2(2),
|
TEAM2(2),
|
||||||
TEAM3(3),
|
TEAM3(3),
|
||||||
TEAM4(4),
|
TEAM4(4),
|
||||||
@@ -131,6 +131,7 @@ class TeamTournament(
|
|||||||
"name" to name,
|
"name" to name,
|
||||||
"players" to playerIds.toList().toJsonArray()
|
"players" to playerIds.toList().toJsonArray()
|
||||||
)
|
)
|
||||||
|
val teamOfIndividuals: Boolean get() = type.individual
|
||||||
}
|
}
|
||||||
|
|
||||||
fun teamFromJson(json: Json.Object, default: TeamTournament.Team? = null) = Team(
|
fun teamFromJson(json: Json.Object, default: TeamTournament.Team? = null) = Team(
|
||||||
|
@@ -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()
|
||||||
|
}
|
@@ -4,6 +4,7 @@ import org.jeudego.pairgoth.model.Game
|
|||||||
import org.jeudego.pairgoth.model.Game.Result.*
|
import org.jeudego.pairgoth.model.Game.Result.*
|
||||||
import org.jeudego.pairgoth.model.Pairable
|
import org.jeudego.pairgoth.model.Pairable
|
||||||
import org.jeudego.pairgoth.model.Pairing
|
import org.jeudego.pairgoth.model.Pairing
|
||||||
|
import org.jeudego.pairgoth.model.TeamTournament
|
||||||
import org.jeudego.pairgoth.store.Store
|
import org.jeudego.pairgoth.store.Store
|
||||||
import org.jgrapht.alg.matching.blossom.v5.KolmogorovWeightedPerfectMatching
|
import org.jgrapht.alg.matching.blossom.v5.KolmogorovWeightedPerfectMatching
|
||||||
import org.jgrapht.alg.matching.blossom.v5.ObjectiveSense
|
import org.jgrapht.alg.matching.blossom.v5.ObjectiveSense
|
||||||
@@ -13,7 +14,15 @@ import org.jgrapht.graph.SimpleWeightedGraph
|
|||||||
import org.jgrapht.graph.builder.GraphBuilder
|
import org.jgrapht.graph.builder.GraphBuilder
|
||||||
import java.util.*
|
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 {
|
companion object {
|
||||||
val rand = Random(/* seed from properties - TODO */)
|
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
|
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
|
// pairables sorted using overloadable sort function
|
||||||
private val sortedPairables by lazy {
|
private val sortedPairables by lazy {
|
||||||
pairables.sortedWith(::sort)
|
pairables.sortedWith(::sort)
|
||||||
@@ -79,77 +92,20 @@ sealed class Solver(val history: List<Game>, val pairables: List<Pairable>, val
|
|||||||
}
|
}
|
||||||
|
|
||||||
// already paired players map
|
// already paired players map
|
||||||
fun Pairable.played(other: Pairable) = _paired.contains(Pair(id, other.id))
|
fun Pairable.played(other: Pairable) = historyHelper.playedTogether(this, other)
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
// color balance (nw - nb)
|
// color balance (nw - nb)
|
||||||
val Pairable.colorBalance: Int get() = _colorBalance[id] ?: 0
|
val Pairable.colorBalance: Int get() = historyHelper.colorBalance(this) ?: 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// score (number of wins)
|
// score (number of wins)
|
||||||
val Pairable.score: Double get() = _score[id] ?: 0.0
|
val Pairable.score: Double get() = historyHelper.score(this) ?: 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 -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sos
|
// sos
|
||||||
val Pairable.sos: Double get() = _sos[id] ?: 0.0
|
val Pairable.sos: Double get() = historyHelper.sos(this) ?: 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sosos
|
// sosos
|
||||||
val Pairable.sosos: Double get() = _sosos[id] ?: 0.0
|
val Pairable.sosos: Double get() = historyHelper.sosos(this) ?: 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sodos
|
// sodos
|
||||||
val Pairable.sodos: Double get() = _sodos[id] ?: 0.0
|
val Pairable.sodos: Double get() = historyHelper.sodos(this) ?: 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user