Refactoring criterion computation
This commit is contained in:
@@ -35,7 +35,7 @@ private const val MA_MAX_MAXIMIZE_SEEDING: Double = MA_MAX_MINIMIZE_SCORE_DIFFER
|
||||
|
||||
enum class SeedMethod { SPLIT_AND_FOLD, SPLIT_AND_RANDOM, SPLIT_AND_SLIP }
|
||||
|
||||
sealed class Pairing(val type: PairingType, val pairingParams: PairingParams = PairingParams()) {
|
||||
sealed class Pairing(val type: PairingType, val pairingParams: PairingParams = PairingParams(), val placementParams: PlacementParams) {
|
||||
companion object {}
|
||||
enum class PairingType { SWISS, MAC_MAHON, ROUND_ROBIN }
|
||||
data class PairingParams(
|
||||
@@ -61,9 +61,8 @@ sealed class Pairing(val type: PairingType, val pairingParams: PairingParams = P
|
||||
val maLastRoundForSeedSystem1: Int = 1,
|
||||
val maSeedSystem1: SeedMethod = SeedMethod.SPLIT_AND_RANDOM,
|
||||
val maSeedSystem2: SeedMethod = SeedMethod.SPLIT_AND_FOLD,
|
||||
// TODO get these parameters from Placement parameters
|
||||
//val maAdditionalPlacementCritSystem1: Int = PlacementParameterSet.PLA_CRIT_RATING,
|
||||
//val maAdditionalPlacementCritSystem2: Int = PlacementParameterSet.PLA_CRIT_NUL,
|
||||
val maAdditionalPlacementCritSystem1: PlacementCriterion = PlacementCriterion.RATING,
|
||||
val maAdditionalPlacementCritSystem2: PlacementCriterion = PlacementCriterion.NULL,
|
||||
|
||||
// Secondary criteria
|
||||
val seBarThresholdActive: Boolean = true, // Do not apply secondary criteria for players above bar
|
||||
@@ -127,9 +126,9 @@ class Swiss(): Pairing(SWISS, PairingParams(
|
||||
|
||||
geo = GeographicalParams.disabled(),
|
||||
hd = HandicapParams.disabled(),
|
||||
)) {
|
||||
), PlacementParams(PlacementCriterion.NBW, PlacementCriterion.SOSW, PlacementCriterion.SOSOSW)) {
|
||||
override fun pair(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): List<Game> {
|
||||
return SwissSolver(tournament.historyBefore(round), pairables, pairingParams).pair()
|
||||
return SwissSolver(round, tournament.historyBefore(round), pairables, pairingParams, placementParams).pair()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,15 +136,16 @@ class MacMahon(
|
||||
var bar: Int = 0,
|
||||
var minLevel: Int = -30,
|
||||
var reducer: Int = 1
|
||||
): Pairing(MAC_MAHON, PairingParams(seDefSecCrit = MA_MAX_MINIMIZE_SCORE_DIFFERENCE)) {
|
||||
): Pairing(MAC_MAHON, PairingParams(seDefSecCrit = MA_MAX_MINIMIZE_SCORE_DIFFERENCE),
|
||||
PlacementParams(PlacementCriterion.MMS, PlacementCriterion.SOSM, PlacementCriterion.SOSOSM)) {
|
||||
val groups = mutableListOf<Int>()
|
||||
|
||||
override fun pair(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): List<Game> {
|
||||
return MacMahonSolver(tournament.historyBefore(round), pairables, pairingParams, mmBase = minLevel, mmBar = bar, reducer = reducer).pair()
|
||||
return MacMahonSolver(round, tournament.historyBefore(round), pairables, pairingParams, placementParams, mmBase = minLevel, mmBar = bar, reducer = reducer).pair()
|
||||
}
|
||||
}
|
||||
|
||||
class RoundRobin: Pairing(ROUND_ROBIN) {
|
||||
class RoundRobin: Pairing(ROUND_ROBIN, PairingParams(), PlacementParams(PlacementCriterion.NBW, PlacementCriterion.RATING)) {
|
||||
override fun pair(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): List<Game> {
|
||||
TODO()
|
||||
}
|
||||
|
@@ -0,0 +1,58 @@
|
||||
package org.jeudego.pairgoth.model
|
||||
|
||||
enum class PlacementCriterion {
|
||||
NULL, // No ranking/tie-break
|
||||
|
||||
CATEGORY,
|
||||
RANK,
|
||||
RATING,
|
||||
NBW, // Number win
|
||||
MMS, // Macmahon score
|
||||
STS, // Strasbourg score
|
||||
CPS, // Cup score
|
||||
|
||||
SOSW, // Sum of opponents NBW
|
||||
SOSWM1, //-1
|
||||
SOSWM2, //-2
|
||||
SODOSW, // Sum of defeated opponents NBW
|
||||
SOSOSW, // Sum of opponenent SOS
|
||||
CUSSW, // Cumulative sum of scores (NBW)
|
||||
|
||||
SOSM, // Sum of opponents McMahon score
|
||||
SOSMM1, // Same as previous group but with McMahon score
|
||||
SOSMM2,
|
||||
SODOSM,
|
||||
SOSOSM,
|
||||
CUSSM,
|
||||
|
||||
SOSTS, // Sum of opponnents Strasbourg score
|
||||
|
||||
EXT, // Exploits tentes
|
||||
EXR, // Exploits reussis
|
||||
|
||||
// For the two criteria below see the user documentation
|
||||
SDC, // Simplified direct confrontation
|
||||
DC, // Direct confrontation
|
||||
}
|
||||
|
||||
class PlacementParams(vararg criteria: PlacementCriterion) {
|
||||
companion object {
|
||||
const val MAX_NUMBER_OF_CRITERIA: Int = 6
|
||||
}
|
||||
|
||||
private fun addNullCriteria(criteria: Array<out PlacementCriterion>): ArrayList<PlacementCriterion> {
|
||||
var criteria = arrayListOf(*criteria)
|
||||
while (criteria.size < MAX_NUMBER_OF_CRITERIA) {
|
||||
criteria.add(PlacementCriterion.NULL)
|
||||
}
|
||||
return criteria
|
||||
}
|
||||
|
||||
val criteria = addNullCriteria(criteria)
|
||||
|
||||
open fun checkWarnings(): String {
|
||||
// Returns a warning message if criteria are incoherent
|
||||
// TODO
|
||||
return ""
|
||||
}
|
||||
}
|
@@ -1,17 +1,30 @@
|
||||
package org.jeudego.pairgoth.pairing
|
||||
|
||||
import org.jeudego.pairgoth.model.Game
|
||||
import org.jeudego.pairgoth.model.Pairable
|
||||
import org.jeudego.pairgoth.model.TeamTournament
|
||||
import org.jeudego.pairgoth.model.*
|
||||
|
||||
open class HistoryHelper(protected val history: List<Game>) {
|
||||
open class HistoryHelper(protected val history: List<Game>, score: Map<ID, Double>) {
|
||||
|
||||
fun getCriterionValue(p: Pairable, crit: PlacementCriterion): Int {
|
||||
// Returns generic criterion
|
||||
// Specific criterion are computed by solvers directly
|
||||
return when (crit) {
|
||||
PlacementCriterion.NULL -> 0
|
||||
PlacementCriterion.CATEGORY -> TODO()
|
||||
PlacementCriterion.RANK -> p.rank
|
||||
PlacementCriterion.RATING -> p.rating
|
||||
|
||||
PlacementCriterion.EXT -> TODO()
|
||||
PlacementCriterion.EXR -> TODO()
|
||||
PlacementCriterion.SDC -> TODO()
|
||||
PlacementCriterion.DC -> TODO()
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
// Generic helper functions
|
||||
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]
|
||||
open fun nbW(p: Pairable) = numberWins[p.id]
|
||||
|
||||
|
||||
protected val paired: Set<Pair<Int, Int>> by lazy {
|
||||
(history.map { game ->
|
||||
@@ -34,7 +47,7 @@ open class HistoryHelper(protected val history: List<Game>) {
|
||||
}
|
||||
}
|
||||
|
||||
private val score: Map<Int, Double> by lazy {
|
||||
val numberWins: Map<Int, Double> by lazy {
|
||||
mutableMapOf<Int, Double>().apply {
|
||||
history.forEach { game ->
|
||||
when (game.result) {
|
||||
@@ -50,7 +63,8 @@ open class HistoryHelper(protected val history: List<Game>) {
|
||||
}
|
||||
}
|
||||
|
||||
private val sos by lazy {
|
||||
// SOS related functions given a score function
|
||||
val sos by lazy {
|
||||
(history.map { game ->
|
||||
Pair(game.black, score[game.white] ?: 0.0)
|
||||
} + history.map { game ->
|
||||
@@ -60,17 +74,18 @@ open class HistoryHelper(protected val history: List<Game>) {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// sos-1
|
||||
val sosm1: Map<ID, Double> by lazy {
|
||||
TODO()
|
||||
}
|
||||
|
||||
private val sodos by lazy {
|
||||
// sos-2
|
||||
val sosm2: Map<ID, Double> by lazy {
|
||||
TODO()
|
||||
}
|
||||
|
||||
// sodos
|
||||
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 ->
|
||||
@@ -80,12 +95,27 @@ open class HistoryHelper(protected val history: List<Game>) {
|
||||
}
|
||||
}
|
||||
|
||||
// sosos
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// cumulative score
|
||||
val cumscore: Map<ID, Double> by lazy {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
class TeamOfIndividualsHistoryHelper(history: List<Game>, score: Map<ID, Double>):
|
||||
HistoryHelper(history, score) {
|
||||
|
||||
private fun Pairable.asTeam() = this as TeamTournament.Team
|
||||
|
||||
@@ -93,8 +123,8 @@ class TeamOfIndividualsHistoryHelper(history: List<Game>): HistoryHelper(history
|
||||
(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()
|
||||
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()
|
||||
}
|
||||
|
@@ -1,16 +1,14 @@
|
||||
package org.jeudego.pairgoth.pairing
|
||||
|
||||
import org.jeudego.pairgoth.model.Game
|
||||
import org.jeudego.pairgoth.model.Pairable
|
||||
import org.jeudego.pairgoth.model.Pairing
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sign
|
||||
|
||||
class MacMahonSolver(history: List<Game>, pairables: List<Pairable>, pairingParams: Pairing.PairingParams, val mmBase: Int, val mmBar: Int, val reducer: Int): Solver(history, pairables, pairingParams) {
|
||||
class MacMahonSolver(round: Int, history: List<Game>, pairables: List<Pairable>, pairingParams: Pairing.PairingParams, placementParams: PlacementParams, val mmBase: Int, val mmBar: Int, val reducer: Int): Solver(round, history, pairables, pairingParams, placementParams) {
|
||||
|
||||
val Pairable.mms get() = mmBase + score
|
||||
val Pairable.mms get() = mmBase + nbW // TODO real calculation
|
||||
|
||||
// CB TODO - configurable criteria
|
||||
override fun mainCriterion(p1: Pairable): Int {
|
||||
@@ -20,10 +18,23 @@ class MacMahonSolver(history: List<Game>, pairables: List<Pairable>, pairingPara
|
||||
override fun mainCriterionMinMax(): Pair<Int, Int> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
override fun sort(p: Pairable, q: Pairable): Int =
|
||||
if (p.mms != q.mms) ((q.mms - p.mms) * 1000).toInt()
|
||||
else if (p.sos != q.sos) ((q.sos - p.sos) * 1000).toInt()
|
||||
else if (p.sosos != q.sosos) ((q.sosos - p.sosos) * 1000).toInt()
|
||||
else 0
|
||||
|
||||
override fun computeStandingScore(): Map<ID, Double> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getSpecificCriterionValue(p: Pairable, criterion: PlacementCriterion): Int {
|
||||
// TODO solve this double/int conflict
|
||||
return when (criterion) {
|
||||
PlacementCriterion.MMS -> TODO()
|
||||
PlacementCriterion.SOSM -> p.sos.toInt()
|
||||
PlacementCriterion.SOSMM1 -> p.sosm1.toInt()
|
||||
PlacementCriterion.SOSMM2 -> p.sosm2.toInt()
|
||||
PlacementCriterion.SODOSM -> p.sodos.toInt()
|
||||
PlacementCriterion.SOSOSM -> p.sosos.toInt()
|
||||
PlacementCriterion.CUSSM -> p.cums.toInt()
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,9 +1,6 @@
|
||||
package org.jeudego.pairgoth.pairing
|
||||
|
||||
import org.jeudego.pairgoth.model.Game
|
||||
import org.jeudego.pairgoth.model.Pairable
|
||||
import org.jeudego.pairgoth.model.Pairing
|
||||
import org.jeudego.pairgoth.model.TeamTournament
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import org.jgrapht.alg.matching.blossom.v5.KolmogorovWeightedPerfectMatching
|
||||
import org.jgrapht.alg.matching.blossom.v5.ObjectiveSense
|
||||
@@ -47,30 +44,88 @@ private fun nonDetRandom(max: Long): Long {
|
||||
return r.toLong()
|
||||
}
|
||||
|
||||
sealed class Solver(history: List<Game>, val pairables: List<Pairable>, val pairingParams: Pairing.PairingParams) {
|
||||
sealed class Solver(
|
||||
val round: Int,
|
||||
history: List<Game>,
|
||||
val pairables: List<Pairable>,
|
||||
val pairingParams: Pairing.PairingParams,
|
||||
val placementParams: PlacementParams) {
|
||||
|
||||
companion object {
|
||||
val rand = Random(/* seed from properties - TODO */)
|
||||
}
|
||||
|
||||
open fun sort(p: Pairable, q: Pairable): Int = 0 // no sort by default
|
||||
open fun sort(p: Pairable, q: Pairable): Int {
|
||||
for (criterion in placementParams.criteria) {
|
||||
val criterionP = getCriterionValue(p, criterion)
|
||||
val criterionQ = getCriterionValue(q, criterion)
|
||||
if (criterionP != criterionQ) {
|
||||
return criterionP - criterionQ
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
open fun weight(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 1L // 1 is minimum value because 0 means "no matching allowed"
|
||||
|
||||
score += applyBaseCriteria(p1, p2)
|
||||
|
||||
score += applyMainCriteria(p1, p2)
|
||||
|
||||
return score as Double
|
||||
}
|
||||
// The main criterion that will be used to define the groups should be defined by subclasses
|
||||
abstract fun mainCriterion(p1: Pairable): Int
|
||||
abstract fun mainCriterionMinMax(): Pair<Int, Int>
|
||||
// SOS and variants will be computed based on this score
|
||||
abstract fun computeStandingScore(): Map<ID, Double>
|
||||
// This function needs to be overridden for criterion specific to the current pairing mode
|
||||
open fun getSpecificCriterionValue(p1: Pairable, criterion: PlacementCriterion): Int {
|
||||
return -1
|
||||
}
|
||||
|
||||
private fun getCriterionValue(p1: Pairable, criterion: PlacementCriterion): Int {
|
||||
val genericCritVal = historyHelper.getCriterionValue(p1, criterion)
|
||||
// If the value from the history helper is > 0 it means that it is a generic criterion
|
||||
// Just returns the value
|
||||
if (genericCritVal != -1) {
|
||||
return genericCritVal
|
||||
}
|
||||
// Otherwise we have to delegate it to the solver
|
||||
val critVal = getSpecificCriterionValue(p1, criterion)
|
||||
if (critVal == -1) throw Error("Couldn't compute criterion value")
|
||||
return critVal
|
||||
}
|
||||
|
||||
fun pair(): List<Game> {
|
||||
// check that at this stage, we have an even number of pairables
|
||||
if (pairables.size % 2 != 0) throw Error("expecting an even number of pairables")
|
||||
val builder = GraphBuilder(SimpleDirectedWeightedGraph<Pairable, DefaultWeightedEdge>(DefaultWeightedEdge::class.java))
|
||||
for (i in sortedPairables.indices) {
|
||||
for (j in i + 1 until pairables.size) {
|
||||
val p = pairables[i]
|
||||
val q = pairables[j]
|
||||
weight(p, q).let { if (it != Double.NaN) builder.addEdge(p, q, it) }
|
||||
weight(q, p).let { if (it != Double.NaN) builder.addEdge(q, p, it) }
|
||||
}
|
||||
}
|
||||
val graph = builder.build()
|
||||
val matching = KolmogorovWeightedPerfectMatching(graph, ObjectiveSense.MINIMIZE)
|
||||
val solution = matching.matching
|
||||
|
||||
val result = solution.flatMap {
|
||||
games(black = graph.getEdgeSource(it) , white = graph.getEdgeTarget(it))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Weight score computation details
|
||||
// Base criteria
|
||||
open fun avoidDuplicatingGames(p1: Pairable, p2: Pairable): Long {
|
||||
if (historyHelper.playedTogether(p1, p2)) {
|
||||
return pairingParams.baseAvoidDuplGame
|
||||
if (p1.played(p2)) {
|
||||
return 0 // We get no score if pairables already played together
|
||||
} else {
|
||||
return 0
|
||||
return pairingParams.baseAvoidDuplGame
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,17 +184,18 @@ sealed class Solver(history: List<Game>, val pairables: List<Pairable>, val pair
|
||||
}
|
||||
|
||||
open fun minimizeScoreDifference(p1: Pairable, p2: Pairable): Long {
|
||||
var scoCost: Long = 0
|
||||
val scoRange: Int = numberGroups
|
||||
var score: Long = 0
|
||||
val scoreRange: Int = numberGroups
|
||||
// TODO check category equality if category are used in SwissCat
|
||||
val x = abs(groups[p1.id]!! - groups[p2.id]!!) as Double / scoRange.toDouble()
|
||||
val x = abs(p1.group - p2.group) as Double / scoreRange.toDouble()
|
||||
val k: Double = pairingParams.standardNX1Factor
|
||||
scoCost = (pairingParams.mainMinimizeScoreDifference * (1.0 - x) * (1.0 + k * x)) as Long
|
||||
score = (pairingParams.mainMinimizeScoreDifference * (1.0 - x) * (1.0 + k * x)) as Long
|
||||
|
||||
return scoCost
|
||||
return score
|
||||
}
|
||||
|
||||
// Handicap functions
|
||||
// Has to be overridden if handicap is not based on rank
|
||||
open fun handicap(p1: Pairable, p2: Pairable): Int {
|
||||
var hd = 0
|
||||
var pseudoRank1: Int = p1.rank
|
||||
@@ -152,8 +208,8 @@ sealed class Solver(history: List<Game>, val pairables: List<Pairable>, val pair
|
||||
return clampHandicap(hd)
|
||||
}
|
||||
|
||||
open fun clampHandicap(input_hd: Int): Int {
|
||||
var hd = input_hd
|
||||
open fun clampHandicap(inputHd: Int): Int {
|
||||
var hd = inputHd
|
||||
if (hd > 0) {
|
||||
hd -= pairingParams.hd.correction
|
||||
hd = min(hd, 0)
|
||||
@@ -174,47 +230,25 @@ sealed class Solver(history: List<Game>, val pairables: List<Pairable>, val pair
|
||||
return listOf(Game(id = Store.nextGameId, black = black.id, white = white.id, handicap = handicap(black, white)))
|
||||
}
|
||||
|
||||
fun pair(): List<Game> {
|
||||
// check that at this stage, we have an even number of pairables
|
||||
if (pairables.size % 2 != 0) throw Error("expecting an even number of pairables")
|
||||
val builder = GraphBuilder(SimpleDirectedWeightedGraph<Pairable, DefaultWeightedEdge>(DefaultWeightedEdge::class.java))
|
||||
for (i in sortedPairables.indices) {
|
||||
for (j in i + 1 until n) {
|
||||
val p = pairables[i]
|
||||
val q = pairables[j]
|
||||
weight(p, q).let { if (it != Double.NaN) builder.addEdge(p, q, it) }
|
||||
weight(q, p).let { if (it != Double.NaN) builder.addEdge(q, p, it) }
|
||||
}
|
||||
}
|
||||
val graph = builder.build()
|
||||
val matching = KolmogorovWeightedPerfectMatching(graph, ObjectiveSense.MINIMIZE)
|
||||
val solution = matching.matching
|
||||
// Generic parameters calculation
|
||||
private val standingScore = computeStandingScore()
|
||||
val historyHelper =
|
||||
if (pairables.first().let { it is TeamTournament.Team && it.teamOfIndividuals }) TeamOfIndividualsHistoryHelper(history, standingScore)
|
||||
else HistoryHelper(history, standingScore)
|
||||
|
||||
val result = solution.flatMap {
|
||||
games(black = graph.getEdgeSource(it) , white = graph.getEdgeTarget(it))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun computeGroups(): Pair<Map<Int, Int>, Int> {
|
||||
// Decide each pairable group based on the main criterion
|
||||
private fun computeGroups(): Pair<Map<ID, Int>, Int> {
|
||||
val (mainScoreMin, mainScoreMax) = mainCriterionMinMax()
|
||||
|
||||
// TODO categories
|
||||
val groups: Map<Int, Int> = pairables.associate { pairable -> Pair(pairable.id, mainCriterion(pairable)) }
|
||||
val groups: Map<ID, Int> = pairables.associate { pairable -> Pair(pairable.id, mainCriterion(pairable)) }
|
||||
|
||||
return Pair(groups, mainScoreMax - mainScoreMin)
|
||||
}
|
||||
|
||||
// Calculation parameters
|
||||
|
||||
val n = pairables.size
|
||||
|
||||
private val historyHelper =
|
||||
if (pairables.first().let { it is TeamTournament.Team && it.teamOfIndividuals }) TeamOfIndividualsHistoryHelper(history)
|
||||
else HistoryHelper(history)
|
||||
|
||||
private val groupsResult = computeGroups()
|
||||
private val groups = groupsResult.first
|
||||
private val _groups = groupsResult.first
|
||||
private val numberGroups = groupsResult.second
|
||||
|
||||
// pairables sorted using overloadable sort function
|
||||
@@ -234,7 +268,7 @@ sealed class Solver(history: List<Game>, val pairables: List<Pairable>, val pair
|
||||
val Pairable.placeInGroup: Pair<Int, Int> get() = _placeInGroup[id]!!
|
||||
private val _placeInGroup by lazy {
|
||||
sortedPairables.groupBy {
|
||||
it.score
|
||||
it.group
|
||||
}.values.flatMap { group ->
|
||||
group.mapIndexed { index, pairable ->
|
||||
Pair(pairable.id, Pair(index, group.size))
|
||||
@@ -243,20 +277,25 @@ sealed class Solver(history: List<Game>, val pairables: List<Pairable>, val pair
|
||||
}
|
||||
|
||||
// already paired players map
|
||||
fun Pairable.played(other: Pairable) = historyHelper.playedTogether(this, other)
|
||||
private fun Pairable.played(other: Pairable) = historyHelper.playedTogether(this, other)
|
||||
|
||||
// color balance (nw - nb)
|
||||
val Pairable.colorBalance: Int get() = historyHelper.colorBalance(this) ?: 0
|
||||
private val Pairable.colorBalance: Int get() = historyHelper.colorBalance(this) ?: 0
|
||||
|
||||
private val Pairable.group: Int get() = _groups[id]!!
|
||||
|
||||
// score (number of wins)
|
||||
val Pairable.score: Double get() = historyHelper.score(this) ?: 0.0
|
||||
val Pairable.nbW: Double get() = historyHelper.nbW(this) ?: 0.0
|
||||
|
||||
val Pairable.sos: Double get() = historyHelper.sos[id]!!
|
||||
|
||||
val Pairable.sosm1: Double get() = historyHelper.sosm1[id]!!
|
||||
val Pairable.sosm2: Double get() = historyHelper.sosm2[id]!!
|
||||
val Pairable.sosos: Double get() = historyHelper.sosos[id]!!
|
||||
val Pairable.sodos: Double get() = historyHelper.sodos[id]!!
|
||||
val Pairable.cums: Double get() = historyHelper.cumscore[id]!!
|
||||
|
||||
|
||||
// sos
|
||||
val Pairable.sos: Double get() = historyHelper.sos(this) ?: 0.0
|
||||
|
||||
// sosos
|
||||
val Pairable.sosos: Double get() = historyHelper.sosos(this) ?: 0.0
|
||||
|
||||
// sodos
|
||||
val Pairable.sodos: Double get() = historyHelper.sodos(this) ?: 0.0
|
||||
}
|
||||
|
@@ -1,23 +1,39 @@
|
||||
package org.jeudego.pairgoth.pairing
|
||||
|
||||
import org.jeudego.pairgoth.model.Game
|
||||
import org.jeudego.pairgoth.model.Pairable
|
||||
import org.jeudego.pairgoth.model.Pairing
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import kotlin.math.abs
|
||||
|
||||
class SwissSolver(history: List<Game>, pairables: List<Pairable>, pairingParams: Pairing.PairingParams): Solver(history, pairables, pairingParams) {
|
||||
|
||||
override fun sort(p: Pairable, q: Pairable): Int =
|
||||
when (p.score) {
|
||||
q.score -> q.rating - p.rating
|
||||
else -> ((q.score - p.score) * 1000).toInt()
|
||||
}
|
||||
class SwissSolver(round: Int,
|
||||
history: List<Game>,
|
||||
pairables: List<Pairable>,
|
||||
pairingParams: Pairing.PairingParams,
|
||||
placementParams: PlacementParams):
|
||||
Solver(round, history, pairables, pairingParams, placementParams) {
|
||||
|
||||
// In a Swiss tournament the main criterion is the number of wins and already computed
|
||||
override fun mainCriterion(p1: Pairable): Int {
|
||||
TODO("Not yet implemented")
|
||||
return p1.nbW.toInt() // Rounded Down TODO make it a parameter ?
|
||||
}
|
||||
|
||||
override fun mainCriterionMinMax(): Pair<Int, Int> {
|
||||
TODO("Not yet implemented")
|
||||
return Pair(0, round-1)
|
||||
}
|
||||
|
||||
override fun computeStandingScore(): Map<ID, Double> {
|
||||
return historyHelper.numberWins
|
||||
}
|
||||
|
||||
override fun getSpecificCriterionValue(p: Pairable, criterion: PlacementCriterion): Int {
|
||||
// TODO solve this double/int conflict
|
||||
return when (criterion) {
|
||||
PlacementCriterion.NBW -> p.nbW.toInt()
|
||||
PlacementCriterion.SOSW -> p.sos.toInt()
|
||||
PlacementCriterion.SOSWM1 -> p.sosm1.toInt()
|
||||
PlacementCriterion.SOSWM2 -> p.sosm2.toInt()
|
||||
PlacementCriterion.SODOSW -> p.sodos.toInt()
|
||||
PlacementCriterion.SOSOSW -> p.sosos.toInt()
|
||||
PlacementCriterion.CUSSW -> p.cums.toInt()
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user