Code cleaning: fix previous commit, simplify HistoryHelper creation
This commit is contained in:
@@ -27,7 +27,7 @@ fun Tournament<*>.getSortedPairables(round: Int, includePreliminary: Boolean = f
|
||||
return ArrayList(frozen!!.map { it -> it as Json.Object })
|
||||
}
|
||||
|
||||
val history = historyHelper(round)
|
||||
val history = historyHelper(round + 1)
|
||||
|
||||
val neededCriteria = ArrayList(pairing.placementParams.criteria)
|
||||
if (!neededCriteria.contains(Criterion.NBW)) neededCriteria.add(Criterion.NBW)
|
||||
@@ -162,10 +162,8 @@ fun TeamTournament.getSortedTeamMembers(round: Int, includePreliminary: Boolean
|
||||
val individualHistory = teamGames.map { roundTeamGames ->
|
||||
roundTeamGames.flatMap { game -> individualGames[game.id]?.toList() ?: listOf() }
|
||||
}
|
||||
val historyHelper = HistoryHelper(individualHistory) {
|
||||
pairables.mapValues {
|
||||
Pair(0.0, wins[it.key] ?: 0.0)
|
||||
}
|
||||
val historyHelper = HistoryHelper(individualHistory).apply {
|
||||
scoresFactory = { wins }
|
||||
}
|
||||
val neededCriteria = mutableListOf(Criterion.NBW, Criterion.RATING)
|
||||
val criteria = neededCriteria.map { crit ->
|
||||
|
@@ -4,7 +4,8 @@ import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.model.MainCritParams.SeedMethod.SPLIT_AND_SLIP
|
||||
import org.jeudego.pairgoth.model.PairingType.*
|
||||
import org.jeudego.pairgoth.pairing.solver.BaseSolver
|
||||
import org.jeudego.pairgoth.pairing.HistoryHelper
|
||||
import org.jeudego.pairgoth.pairing.solver.Solver
|
||||
import org.jeudego.pairgoth.pairing.solver.MacMahonSolver
|
||||
import org.jeudego.pairgoth.pairing.solver.SwissSolver
|
||||
import kotlin.math.min
|
||||
@@ -131,7 +132,7 @@ sealed class Pairing(
|
||||
val pairingParams: PairingParams,
|
||||
val placementParams: PlacementParams) {
|
||||
companion object {}
|
||||
abstract fun solver(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): BaseSolver
|
||||
abstract fun solver(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): Solver
|
||||
fun pair(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): List<Game> {
|
||||
return solver(tournament, round, pairables).pair()
|
||||
}
|
||||
@@ -140,19 +141,6 @@ sealed class Pairing(
|
||||
internal fun Tournament<*>.historyBefore(round: Int) =
|
||||
(1 until min(round, lastRound() + 1)).map { games(it).values.toList() }
|
||||
|
||||
/*private fun Tournament<*>.historyBefore(round: Int) : List<List<Game>> {
|
||||
println("Welcome to tournament.historyBefore !")
|
||||
println("lastround and round = "+lastRound().toString()+" "+round.toString())
|
||||
println((1 until round).map { it })
|
||||
println((1 until round).map { games(it).values.toList() })
|
||||
if (lastRound() == 1){
|
||||
return emptyList()
|
||||
}
|
||||
else {
|
||||
return (1 until round).map { games(it).values.toList() }
|
||||
}
|
||||
}*/
|
||||
|
||||
class Swiss(
|
||||
pairingParams: PairingParams = PairingParams(
|
||||
base = BaseCritParams(),
|
||||
@@ -175,7 +163,7 @@ class Swiss(
|
||||
): Pairing(SWISS, pairingParams, placementParams) {
|
||||
companion object {}
|
||||
override fun solver(tournament: Tournament<*>, round: Int, pairables: List<Pairable>) =
|
||||
SwissSolver(round, tournament.rounds, tournament.historyHelper(round), pairables, tournament.pairables, pairingParams, placementParams, tournament.usedTables(round))
|
||||
SwissSolver(round, tournament.rounds, HistoryHelper(tournament.historyBefore(round)), pairables, tournament.pairables, pairingParams, placementParams, tournament.usedTables(round))
|
||||
}
|
||||
|
||||
class MacMahon(
|
||||
@@ -203,14 +191,14 @@ class MacMahon(
|
||||
): Pairing(MAC_MAHON, pairingParams, placementParams) {
|
||||
companion object {}
|
||||
override fun solver(tournament: Tournament<*>, round: Int, pairables: List<Pairable>) =
|
||||
MacMahonSolver(round, tournament.rounds, tournament.historyHelper(round), pairables, tournament.pairables, pairingParams, placementParams, tournament.usedTables(round), mmFloor, mmBar)
|
||||
MacMahonSolver(round, tournament.rounds, HistoryHelper(tournament.historyBefore(round)), pairables, tournament.pairables, pairingParams, placementParams, tournament.usedTables(round), mmFloor, mmBar)
|
||||
}
|
||||
|
||||
class RoundRobin(
|
||||
pairingParams: PairingParams = PairingParams(),
|
||||
placementParams: PlacementParams = PlacementParams(Criterion.NBW, Criterion.RATING)
|
||||
): Pairing(ROUND_ROBIN, pairingParams, placementParams) {
|
||||
override fun solver(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): BaseSolver {
|
||||
override fun solver(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): Solver {
|
||||
TODO("not implemented")
|
||||
}
|
||||
}
|
||||
|
@@ -225,32 +225,7 @@ sealed class Tournament <P: Pairable>(
|
||||
}
|
||||
|
||||
fun historyHelper(round: Int): HistoryHelper {
|
||||
return HistoryHelper(historyBefore(round + 1)) {
|
||||
if (pairing.type == PairingType.SWISS) {
|
||||
pairables.mapValues {
|
||||
// In a Swiss tournament the main criterion is the number of wins
|
||||
Pair(0.0, wins[it.key] ?: 0.0)
|
||||
}
|
||||
}
|
||||
else {
|
||||
pairables.mapValues {
|
||||
// In a MacMahon tournament the main criterion is the mms
|
||||
it.value.let { pairable ->
|
||||
val mmBase = pairable.mmBase()
|
||||
val score = roundScore(mmBase +
|
||||
(nbW(pairable) ?: 0.0) +
|
||||
(1..round).sumOf { round ->
|
||||
if (playersPerRound.getOrNull(round - 1)?.contains(pairable.id) == true) 0.0 else 1.0
|
||||
} * pairing.pairingParams.main.mmsValueAbsent)
|
||||
Pair(
|
||||
if (pairing.pairingParams.main.sosValueAbsentUseBase) mmBase
|
||||
else roundScore(mmBase + round/2),
|
||||
score
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pairing.solver(this, round, emptyList()).history
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -11,14 +11,11 @@ abstract class BasePairingHelper(
|
||||
val placement: PlacementParams,
|
||||
) {
|
||||
|
||||
val scores get() = history.scores
|
||||
abstract val scoresX: Map<ID, Double>
|
||||
|
||||
// Extend pairables with members from all rounds
|
||||
|
||||
// The main criterion that will be used to define the groups should be defined by subclasses
|
||||
// SOS and variants will be computed based on this score
|
||||
val Pairable.main: Double get() = scores[id]?.second ?: 0.0
|
||||
val Pairable.main: Double get() = score ?: 0.0
|
||||
abstract val mainLimits: Pair<Double, Double>
|
||||
|
||||
// pairables sorted using overloadable sort function
|
||||
@@ -89,9 +86,9 @@ abstract class BasePairingHelper(
|
||||
|
||||
protected val Pairable.nbBye: Int get() = history.nbPlayedWithBye(this) ?: 0
|
||||
|
||||
// score (number of wins)
|
||||
val Pairable.score: Double get() = history.scores[id] ?: 0.0
|
||||
val Pairable.scoreX: Double get() = history.scoresX[id] ?: 0.0
|
||||
val Pairable.nbW: Double get() = history.nbW(this) ?: 0.0
|
||||
|
||||
val Pairable.sos: Double get() = history.sos[id] ?: 0.0
|
||||
val Pairable.sosm1: Double get() = history.sosm1[id] ?: 0.0
|
||||
val Pairable.sosm2: Double get() = history.sosm2[id] ?: 0.0
|
||||
|
@@ -2,22 +2,22 @@ package org.jeudego.pairgoth.pairing
|
||||
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import org.jeudego.pairgoth.model.Game.Result.*
|
||||
import org.jeudego.pairgoth.model.TeamTournament.Team
|
||||
import org.jeudego.pairgoth.pairing.solver.MacMahonSolver
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import org.jeudego.pairgoth.pairing.solver.Solver
|
||||
|
||||
/**
|
||||
* Map from a pairable ID to a pair of (missed rounds increment, main score).
|
||||
* The missed rounds increment is 0 for Swiss, and a function of the MMS base of the pairable for MacMahon.
|
||||
* The main score is the NBW for the Swiss, the MMS for MacMahon.
|
||||
*/
|
||||
typealias ScoreMapBuilder = HistoryHelper.()-> Map<ID, Pair<Double, Double>>
|
||||
typealias ScoreMap = Map<ID, Double>
|
||||
typealias ScoreMapFactory = () -> ScoreMap
|
||||
|
||||
open class HistoryHelper(
|
||||
protected val history: List<List<Game>>,
|
||||
// scoresGetter() returns Pair(sos value for missed rounds, score) where score is nbw for Swiss, mms for MM, ...
|
||||
scoresGetter: ScoreMapBuilder) {
|
||||
protected val history: List<List<Game>>
|
||||
) {
|
||||
|
||||
lateinit var scoresFactory: ScoreMapFactory
|
||||
lateinit var scoresXFactory: ScoreMapFactory
|
||||
lateinit var missedRoundsSosFactory: ScoreMapFactory
|
||||
|
||||
val scores by lazy { scoresFactory() }
|
||||
val scoresX by lazy { scoresXFactory() }
|
||||
val missedRoundsSos by lazy { missedRoundsSosFactory() }
|
||||
|
||||
private val Game.blackScore get() = when (result) {
|
||||
BLACK, BOTHWIN -> 1.0
|
||||
@@ -29,16 +29,6 @@ open class HistoryHelper(
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
val scores by lazy {
|
||||
scoresGetter()
|
||||
}
|
||||
|
||||
val scoresX by lazy {
|
||||
scoresGetter().mapValues { entry ->
|
||||
entry.value.first + (wins[entry.key] ?: 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
// 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]
|
||||
@@ -82,14 +72,14 @@ open class HistoryHelper(
|
||||
}
|
||||
}
|
||||
|
||||
// Set of all implied players for each round (warning: does comprise games with BIP)
|
||||
// Set of all implied players for each round
|
||||
val playersPerRound: List<Set<ID>> by lazy {
|
||||
history.map {
|
||||
it.fold(mutableSetOf<ID>()) { acc, next ->
|
||||
if(next.white != 0) acc.add(next.white)
|
||||
if (next.black != 0) acc.add(next.black)
|
||||
acc
|
||||
}
|
||||
history.map { roundGames ->
|
||||
roundGames.flatMap {
|
||||
game -> listOf(game.white, game.black)
|
||||
}.filter { id ->
|
||||
id != ByePlayer.id
|
||||
}.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,67 +100,89 @@ open class HistoryHelper(
|
||||
}
|
||||
|
||||
// define mms to be a synonym of scores
|
||||
val mms by lazy { scores.mapValues { it -> it.value.second } }
|
||||
val mms by lazy { scores }
|
||||
|
||||
val sos by lazy {
|
||||
// SOS for played games against a real opponent or BIP
|
||||
val historySos = (history.flatten().map { game ->
|
||||
Pair(
|
||||
game.black,
|
||||
if (game.white == 0) scores[game.black]?.first ?: 0.0
|
||||
else scores[game.white]?.second?.let { it - game.handicap } ?: 0.0
|
||||
if (game.white == 0) missedRoundsSos[game.black] ?: 0.0
|
||||
else scores[game.white]?.let { it - game.handicap } ?: 0.0
|
||||
)
|
||||
} + history.flatten().map { game ->
|
||||
Pair(
|
||||
game.white,
|
||||
if (game.black == 0) scores[game.white]?.first ?: 0.0
|
||||
else scores[game.black]?.second?.let { it + game.handicap } ?: 0.0
|
||||
if (game.black == 0) missedRoundsSos[game.white] ?: 0.0
|
||||
else scores[game.black]?.let { it + game.handicap } ?: 0.0
|
||||
)
|
||||
}).groupingBy {
|
||||
it.first
|
||||
}.fold(0.0) { acc, next ->
|
||||
acc + next.second
|
||||
}
|
||||
|
||||
scores.mapValues { (id, pair) ->
|
||||
// plus SOS for missed rounds
|
||||
missedRoundsSos.mapValues { (id, pseudoSos) ->
|
||||
(historySos[id] ?: 0.0) + playersPerRound.sumOf {
|
||||
if (it.contains(id)) 0.0 else pair.first
|
||||
if (it.contains(id)) 0.0 else pseudoSos
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// sos-1
|
||||
val sosm1 by lazy {
|
||||
// SOS for played games against a real opponent or BIP
|
||||
(history.flatten().map { game ->
|
||||
Pair(game.black, scores[game.white]?.second?.let { it - game.handicap } ?: 0.0)
|
||||
Pair(
|
||||
game.black,
|
||||
if (game.white == 0) missedRoundsSos[game.black] ?: 0.0
|
||||
else scores[game.white]?.let { it - game.handicap } ?: 0.0
|
||||
)
|
||||
} + history.flatten().map { game ->
|
||||
Pair(game.white, scores[game.black]?.second?.let { it + game.handicap } ?: 0.0)
|
||||
Pair(
|
||||
game.white,
|
||||
if (game.black == 0) missedRoundsSos[game.white] ?: 0.0
|
||||
else scores[game.black]?.let { it + game.handicap } ?: 0.0
|
||||
)
|
||||
}).groupBy {
|
||||
it.first
|
||||
}.mapValues { (id, pairs) ->
|
||||
val oppScores = pairs.map { it.second }.sortedDescending()
|
||||
// minus greatest SOS
|
||||
oppScores.sum() - (oppScores.firstOrNull() ?: 0.0) +
|
||||
// plus SOS for missed rounds
|
||||
playersPerRound.sumOf { players ->
|
||||
if (players.contains(id)) 0.0
|
||||
else scores[id]?.first ?: 0.0
|
||||
else missedRoundsSos[id] ?: 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sos-2
|
||||
val sosm2 by lazy {
|
||||
// SOS for played games against a real opponent or BIP
|
||||
(history.flatten().map { game ->
|
||||
Pair(game.black, scores[game.white]?.second?.let { it - game.handicap } ?: 0.0)
|
||||
Pair(
|
||||
game.black,
|
||||
if (game.white == 0) missedRoundsSos[game.black] ?: 0.0
|
||||
else scores[game.white]?.let { it - game.handicap } ?: 0.0
|
||||
)
|
||||
} + history.flatten().map { game ->
|
||||
Pair(game.white, scores[game.black]?.second?.let { it + game.handicap } ?: 0.0)
|
||||
Pair(
|
||||
game.white,
|
||||
if (game.black == 0) missedRoundsSos[game.white] ?: 0.0
|
||||
else scores[game.black]?.let { it + game.handicap } ?: 0.0
|
||||
)
|
||||
}).groupBy {
|
||||
it.first
|
||||
}.mapValues { (id, pairs) ->
|
||||
val oppScores = pairs.map { it.second }.sorted()
|
||||
val oppScores = pairs.map { it.second }.sortedDescending()
|
||||
// minus two greatest SOS
|
||||
oppScores.sum() - oppScores.getOrElse(0) { 0.0 } - oppScores.getOrElse(1) { 0.0 } +
|
||||
// plus SOS for missed rounds
|
||||
playersPerRound.sumOf { players ->
|
||||
if (players.contains(id)) 0.0
|
||||
else scores[id]?.first ?: 0.0
|
||||
else missedRoundsSos[id] ?: 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,16 +192,17 @@ open class HistoryHelper(
|
||||
(history.flatten().filter { game ->
|
||||
game.white != 0 // Remove games against byePlayer
|
||||
}.map { game ->
|
||||
Pair(game.black, if (game.result == Game.Result.BLACK) scores[game.white]?.second?.let { it - game.handicap } ?: 0.0 else 0.0)
|
||||
Pair(game.black, if (game.result == Game.Result.BLACK) scores[game.white]?.let { it - game.handicap } ?: 0.0 else 0.0)
|
||||
} + history.flatten().filter { game ->
|
||||
game.white != 0 // Remove games against byePlayer
|
||||
}.map { game ->
|
||||
Pair(game.white, if (game.result == Game.Result.WHITE) scores[game.black]?.second?.let { it + game.handicap } ?: 0.0 else 0.0)
|
||||
Pair(game.white, if (game.result == Game.Result.WHITE) scores[game.black]?.let { it + game.handicap } ?: 0.0 else 0.0)
|
||||
}).groupingBy { it.first }.fold(0.0) { acc, next ->
|
||||
acc + next.second
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sosos
|
||||
val sosos by lazy {
|
||||
val currentRound = history.size
|
||||
@@ -203,9 +216,9 @@ open class HistoryHelper(
|
||||
acc + next.second
|
||||
}
|
||||
|
||||
scores.mapValues { (id, pair) ->
|
||||
missedRoundsSos.mapValues { (id, missedRoundSos) ->
|
||||
(historySosos[id] ?: 0.0) + playersPerRound.sumOf {
|
||||
if (it.contains(id)) 0.0 else pair.first * currentRound
|
||||
if (it.contains(id)) 0.0 else missedRoundSos * currentRound
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package org.jeudego.pairgoth.pairing
|
||||
|
||||
import org.jeudego.pairgoth.model.Pairable
|
||||
import org.jeudego.pairgoth.pairing.solver.BaseSolver
|
||||
|
||||
fun detRandom(max: Double, p1: Pairable, p2: Pairable, symmetric: Boolean): Double {
|
||||
var inverse = false
|
||||
|
@@ -16,19 +16,31 @@ class MacMahonSolver(round: Int,
|
||||
placementParams: PlacementParams,
|
||||
usedTables: BitSet,
|
||||
private val mmFloor: Int, private val mmBar: Int) :
|
||||
BaseSolver(round, totalRounds, history, pairables, pairingParams, placementParams, usedTables) {
|
||||
Solver(round, totalRounds, history, pairables, allPairablesMap, pairingParams, placementParams, usedTables) {
|
||||
|
||||
override val scoresX: Map<ID, Double> by lazy {
|
||||
require (mmBar > mmFloor) { "MMFloor is higher than MMBar" }
|
||||
allPairablesMap.mapValues {
|
||||
it.value.let { pairable ->
|
||||
override fun mainScoreMapFactory() =
|
||||
allPairablesMap.mapValues { (id, pairable) ->
|
||||
roundScore(pairable.mmBase +
|
||||
pairable.nbW +
|
||||
pairable.missedRounds() * pairing.main.mmsValueAbsent)
|
||||
}
|
||||
|
||||
override fun scoreXMapFactory() =
|
||||
allPairablesMap.mapValues { (id, pairable) ->
|
||||
roundScore(pairable.mmBase + pairable.nbW)
|
||||
}
|
||||
|
||||
override fun missedRoundSosMapFactory() =
|
||||
allPairablesMap.mapValues { (id, pairable) ->
|
||||
if (pairing.main.sosValueAbsentUseBase) {
|
||||
pairable.mmBase
|
||||
} else {
|
||||
roundScore(pairable.mmBase + round/2)
|
||||
}
|
||||
}
|
||||
|
||||
override fun computeWeightForBye(p: Pairable): Double{
|
||||
return 2*scores[p.id]!!.second
|
||||
return 2 * p.score
|
||||
}
|
||||
|
||||
override fun SecondaryCritParams.apply(p1: Pairable, p2: Pairable): Double {
|
||||
@@ -69,8 +81,7 @@ class MacMahonSolver(round: Int,
|
||||
// mmBase: starting Mac-Mahon score of the pairable
|
||||
val Pairable.mmBase: Double get() = min(max(rank, mmFloor), mmBar) + mmsZero + mmsCorrection
|
||||
// mms: current Mac-Mahon score of the pairable
|
||||
val Pairable.mms: Double get() = scores[id]?.second ?: 0.0
|
||||
val Pairable.scoreX: Double get() = scoresX[id] ?: 0.0
|
||||
val Pairable.mms: Double get() = score
|
||||
|
||||
// CB TODO - configurable criteria
|
||||
val mainScoreMin = mmFloor + PLA_SMMS_SCORE_MIN - Pairable.MIN_RANK
|
||||
|
@@ -19,11 +19,12 @@ import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
import kotlin.math.*
|
||||
|
||||
sealed class BaseSolver(
|
||||
sealed class Solver(
|
||||
round: Int,
|
||||
totalRounds: Int,
|
||||
history: HistoryHelper, // History of all games played for each round
|
||||
pairables: List<Pairable>, // All pairables for this round, it may include the bye player
|
||||
history: HistoryHelper, // Digested history of all games played for each round
|
||||
pairables: List<Pairable>, // Pairables to pair together
|
||||
val allPairablesMap: Map<ID, Pairable>, // Map of all known pairables
|
||||
pairing: PairingParams,
|
||||
placement: PlacementParams,
|
||||
val usedTables: BitSet
|
||||
@@ -36,6 +37,27 @@ sealed class BaseSolver(
|
||||
var legacy_mode = false
|
||||
}
|
||||
|
||||
init {
|
||||
history.scoresFactory = this::mainScoreMapFactory
|
||||
history.scoresXFactory = this::scoreXMapFactory
|
||||
history.missedRoundsSosFactory = this::missedRoundSosMapFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Main score map factory (NBW for Swiss, MMS for MacMahon, ...).
|
||||
*/
|
||||
abstract fun mainScoreMapFactory(): Map<ID, Double>
|
||||
|
||||
/**
|
||||
* ScoreX map factory (NBW for Swiss, MMSBase + MMS for MacMahon, ...).
|
||||
*/
|
||||
abstract fun scoreXMapFactory(): Map<ID, Double>
|
||||
|
||||
/**
|
||||
* SOS for missed rounds factory (0 for Swiss, mmBase or mmBase+rounds/2 for MacMahon depending on pairing option sosValueAbsentUseBase)
|
||||
*/
|
||||
abstract fun missedRoundSosMapFactory(): Map<ID, Double>
|
||||
|
||||
open fun openGothaWeight(p1: Pairable, p2: Pairable) =
|
||||
1.0 + // 1 is minimum value because 0 means "no matching allowed"
|
||||
pairing.base.apply(p1, p2) +
|
||||
@@ -144,8 +166,8 @@ sealed class BaseSolver(
|
||||
for (p in sortedPairables) {
|
||||
logger.info(String.format("%-20s", p.name.substring(0, min(p.name.length, 18)))
|
||||
+ " " + String.format("%-4s", p.id)
|
||||
+ " " + String.format("%-4s", scores[p.id]?.first)
|
||||
+ " " + String.format("%-4s", scores[p.id]?.second)
|
||||
+ " " + String.format("%-4s", history.missedRoundsSos[p.id])
|
||||
+ " " + String.format("%-4s", history.scores[p.id])
|
||||
+ " " + String.format("%-4s", p.sos)
|
||||
)
|
||||
}
|
@@ -8,18 +8,28 @@ class SwissSolver(round: Int,
|
||||
totalRounds: Int,
|
||||
history: HistoryHelper,
|
||||
pairables: List<Pairable>,
|
||||
pairablesMap: Map<ID, Pairable>,
|
||||
allPairablesMap: Map<ID, Pairable>,
|
||||
pairingParams: PairingParams,
|
||||
placementParams: PlacementParams,
|
||||
usedTables: BitSet
|
||||
):
|
||||
BaseSolver(round, totalRounds, history, pairables, pairingParams, placementParams, usedTables) {
|
||||
Solver(round, totalRounds, history, pairables, allPairablesMap, pairingParams, placementParams, usedTables) {
|
||||
|
||||
override val scoresX: Map<ID, Double> get() = scores.mapValues { it.value.second }
|
||||
override fun mainScoreMapFactory() =
|
||||
allPairablesMap.mapValues { (id, pairable) ->
|
||||
history.wins[id] ?: 0.0
|
||||
}
|
||||
|
||||
override fun scoreXMapFactory() = mainScoreMapFactory()
|
||||
|
||||
override fun missedRoundSosMapFactory() =
|
||||
allPairablesMap.mapValues { (id, pairable) ->
|
||||
0.0
|
||||
}
|
||||
|
||||
override val mainLimits = Pair(0.0, round - 1.0)
|
||||
|
||||
override fun computeWeightForBye(p: Pairable): Double{
|
||||
return p.rank + 40*p.main
|
||||
return p.rank + 40 * p.main
|
||||
}
|
||||
}
|
||||
|
@@ -1,14 +1,12 @@
|
||||
package org.jeudego.pairgoth.test
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.pairing.solver.BaseSolver
|
||||
import org.jeudego.pairgoth.model.Game
|
||||
import org.jeudego.pairgoth.pairing.solver.Solver
|
||||
import org.jeudego.pairgoth.test.PairingTests.Companion.compare_weights
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.nio.charset.StandardCharsets
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
@@ -22,16 +20,16 @@ class BOSP2024Test: TestBase() {
|
||||
)!!.asObject()
|
||||
val resp = TestAPI.post("/api/tour", tournament).asObject()
|
||||
val tourId = resp.getInt("id")
|
||||
BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("bosp2024-weights.txt")))
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("bosp2024-weights.txt")))
|
||||
|
||||
BaseSolver.legacy_mode = true
|
||||
Solver.legacy_mode = true
|
||||
TestAPI.post("/api/tour/$tourId/pair/3", Json.Array("all")).asArray()
|
||||
|
||||
// compare weights
|
||||
assertTrue(compare_weights(getOutputFile("bosp2024-weights.txt"), getTestFile("opengotha/bosp2024/bosp2024_weights_R3.txt")), "Not matching opengotha weights for BOSP test")
|
||||
TestAPI.delete("/api/tour/$tourId/pair/3", Json.Array("all"))
|
||||
|
||||
BaseSolver.legacy_mode = false
|
||||
Solver.legacy_mode = false
|
||||
val games = TestAPI.post("/api/tour/$tourId/pair/3", Json.Array("all")).asArray()
|
||||
// Aksut Husrev is ID 18
|
||||
val solved = games.map { it as Json.Object }.filter { game ->
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package org.jeudego.pairgoth.test
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.pairing.solver.BaseSolver
|
||||
import org.jeudego.pairgoth.pairing.solver.Solver
|
||||
import org.jeudego.pairgoth.test.PairingTests.Companion.compare_weights
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.FileWriter
|
||||
@@ -19,7 +19,7 @@ class MalavasiTest: TestBase() {
|
||||
)!!.asObject()
|
||||
val resp = TestAPI.post("/api/tour", tournament).asObject()
|
||||
val tourId = resp.getInt("id")
|
||||
BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("malavasi-weights.txt")))
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("malavasi-weights.txt")))
|
||||
val games = TestAPI.post("/api/tour/$tourId/pair/2", Json.Array("all")).asArray()
|
||||
// Oceane is ID 548, Valentine 549
|
||||
val buggy = games.map { it as Json.Object }.filter { game ->
|
||||
|
@@ -2,7 +2,7 @@ package org.jeudego.pairgoth.test
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import org.jeudego.pairgoth.pairing.solver.BaseSolver
|
||||
import org.jeudego.pairgoth.pairing.solver.Solver
|
||||
import org.jeudego.pairgoth.store.MemoryStore
|
||||
import org.jeudego.pairgoth.store.lastPlayerId
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
@@ -59,7 +59,7 @@ class PairingTests: TestBase() {
|
||||
}
|
||||
|
||||
fun compare_weights(file1: File, file2: File, skipSeeding: Boolean = false):Boolean {
|
||||
BaseSolver.weightsLogger!!.flush()
|
||||
Solver.weightsLogger!!.flush()
|
||||
// Maps to store name pairs and costs
|
||||
val map1 = create_weights_map(file1)
|
||||
val map2 = create_weights_map(file2)
|
||||
@@ -172,7 +172,7 @@ class PairingTests: TestBase() {
|
||||
|
||||
fun test_from_XML_internal(name: String, forcePairing:List<Int>, legacy: Boolean) {
|
||||
// Let pairgoth use the legacy asymmetric detRandom()
|
||||
BaseSolver.legacy_mode = legacy
|
||||
Solver.legacy_mode = legacy
|
||||
// read tournament with pairing
|
||||
val file = getTestFile("opengotha/pairings/$name.xml")
|
||||
logger.info("read from file $file")
|
||||
@@ -203,7 +203,7 @@ class PairingTests: TestBase() {
|
||||
for (round in 1..tournament.getInt("rounds")!!) {
|
||||
val sumOfWeightsOG = compute_sumOfWeight_OG(getTestFile("opengotha/$name/$name" + "_weights_R$round.txt"), pairingsOG[round-1], players)
|
||||
|
||||
BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
// Call Pairgoth pairing solver to generate games
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
logger.info("sumOfWeightOG = " + dec.format(sumOfWeightsOG))
|
||||
@@ -273,7 +273,7 @@ class PairingTests: TestBase() {
|
||||
|
||||
@Test
|
||||
fun `SwissTest simpleSwiss`() {
|
||||
BaseSolver.legacy_mode = true
|
||||
Solver.legacy_mode = true
|
||||
// read tournament with pairing
|
||||
var file = getTestFile("opengotha/pairings/simpleswiss.xml")
|
||||
logger.info("read from file $file")
|
||||
@@ -315,7 +315,7 @@ class PairingTests: TestBase() {
|
||||
var firstGameID: Int
|
||||
|
||||
for (round in 1..7) {
|
||||
BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
logger.info("games for round $round: {}", games.toString().slice(0..50) + "...")
|
||||
assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/simpleswiss/simpleswiss_weights_R$round.txt")), "Not matching opengotha weights for round $round")
|
||||
@@ -354,7 +354,7 @@ class PairingTests: TestBase() {
|
||||
@Test
|
||||
fun `SwissTest KPMCSplitbug`() {
|
||||
// Let pairgoth use the legacy asymmetric detRandom()
|
||||
BaseSolver.legacy_mode = true
|
||||
Solver.legacy_mode = true
|
||||
// read tournament with pairing
|
||||
val name = "20240921-KPMC-Splitbug"
|
||||
val file = getTestFile("opengotha/pairings/$name.xml")
|
||||
@@ -391,7 +391,7 @@ class PairingTests: TestBase() {
|
||||
for (round in minRound..maxRound) {
|
||||
val sumOfWeightsOG = compute_sumOfWeight_OG(getTestFile("opengotha/$name/$name" + "_weights_R$round.txt"), pairingsOG[round - minRound], players)
|
||||
|
||||
BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
// Call Pairgoth pairing solver to generate games
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
|
||||
|
Reference in New Issue
Block a user