From db456a1a1b6b5fc81409b538f0cf1e1831ef8e96 Mon Sep 17 00:00:00 2001 From: Theo Barollet Date: Mon, 12 Jun 2023 15:51:52 +0200 Subject: [PATCH] Refactoring criterion computation --- .../org/jeudego/pairgoth/model/Pairing.kt | 18 +-- .../org/jeudego/pairgoth/model/Placement.kt | 58 +++++++ .../jeudego/pairgoth/pairing/HistoryHelper.kt | 78 ++++++--- .../pairgoth/pairing/MacMahonSolver.kt | 31 ++-- .../org/jeudego/pairgoth/pairing/Solver.kt | 153 +++++++++++------- .../jeudego/pairgoth/pairing/SwissSolver.kt | 40 +++-- 6 files changed, 266 insertions(+), 112 deletions(-) create mode 100644 api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Placement.kt diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairing.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairing.kt index 3b94dec..d4e9aea 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairing.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairing.kt @@ -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): List { - 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() override fun pair(tournament: Tournament<*>, round: Int, pairables: List): List { - 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): List { TODO() } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Placement.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Placement.kt new file mode 100644 index 0000000..a4121c0 --- /dev/null +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Placement.kt @@ -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): ArrayList { + 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 "" + } +} \ No newline at end of file diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/HistoryHelper.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/HistoryHelper.kt index 55c18e5..18c54dc 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/HistoryHelper.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/HistoryHelper.kt @@ -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) { +open class HistoryHelper(protected val history: List, score: Map) { + 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> by lazy { (history.map { game -> @@ -34,7 +47,7 @@ open class HistoryHelper(protected val history: List) { } } - private val score: Map by lazy { + val numberWins: Map by lazy { mutableMapOf().apply { history.forEach { game -> when (game.result) { @@ -50,7 +63,8 @@ open class HistoryHelper(protected val history: List) { } } - 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) { } } - 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 by lazy { + TODO() } - private val sodos by lazy { + // sos-2 + val sosm2: Map 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) { } } + // 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 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): HistoryHelper(history) { +class TeamOfIndividualsHistoryHelper(history: List, score: Map): + HistoryHelper(history, score) { private fun Pairable.asTeam() = this as TeamTournament.Team @@ -93,8 +123,8 @@ class TeamOfIndividualsHistoryHelper(history: List): 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() } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/MacMahonSolver.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/MacMahonSolver.kt index ae55d14..435157a 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/MacMahonSolver.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/MacMahonSolver.kt @@ -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, pairables: List, pairingParams: Pairing.PairingParams, val mmBase: Int, val mmBar: Int, val reducer: Int): Solver(history, pairables, pairingParams) { +class MacMahonSolver(round: Int, history: List, pairables: List, 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, pairables: List, pairingPara override fun mainCriterionMinMax(): Pair { 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 { + 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 + } + } } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/Solver.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/Solver.kt index c770e73..0ec22f0 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/Solver.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/Solver.kt @@ -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, val pairables: List, val pairingParams: Pairing.PairingParams) { +sealed class Solver( + val round: Int, + history: List, + val pairables: List, + 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 + // SOS and variants will be computed based on this score + abstract fun computeStandingScore(): Map + // 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 { + // 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(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, val pairables: List, 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, val pairables: List, 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, val pairables: List, val pair return listOf(Game(id = Store.nextGameId, black = black.id, white = white.id, handicap = handicap(black, white))) } - fun pair(): List { - // 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(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, Int> { + // Decide each pairable group based on the main criterion + private fun computeGroups(): Pair, Int> { val (mainScoreMin, mainScoreMax) = mainCriterionMinMax() // TODO categories - val groups: Map = pairables.associate { pairable -> Pair(pairable.id, mainCriterion(pairable)) } + val groups: Map = 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, val pairables: List, val pair val Pairable.placeInGroup: Pair 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, val pairables: List, 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 } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/SwissSolver.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/SwissSolver.kt index 50d024a..539c158 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/SwissSolver.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/SwissSolver.kt @@ -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, pairables: List, 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, + pairables: List, + 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 { - TODO("Not yet implemented") + return Pair(0, round-1) + } + + override fun computeStandingScore(): Map { + 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 + } } }