Seeding with split fold/split/random
This commit is contained in:
@@ -25,13 +25,13 @@ private const val BA_MAX_AVOIDDUPLGAME: Long = 500000000000000L // 5e14
|
|||||||
private const val BA_MAX_RANDOM: Long = 1000000000L // 2e9
|
private const val BA_MAX_RANDOM: Long = 1000000000L // 2e9
|
||||||
private const val BA_MAX_BALANCEWB: Long = 1000000L // 1e6
|
private const val BA_MAX_BALANCEWB: Long = 1000000L // 1e6
|
||||||
|
|
||||||
private const val MA_MAX_AVOID_MIXING_CATEGORIES: Double = 2e13
|
private const val MA_MAX_AVOID_MIXING_CATEGORIES: Long = 20000000000000L // 2e13
|
||||||
// Ratio between MA_MAX_MINIMIZE_SCORE_DIFFERENCE and MA_MAX_AVOID_MIXING_CATEGORIES should stay below 1/ nbcat^2
|
// Ratio between MA_MAX_MINIMIZE_SCORE_DIFFERENCE and MA_MAX_AVOID_MIXING_CATEGORIES should stay below 1/ nbcat^2
|
||||||
private const val MA_MAX_MINIMIZE_SCORE_DIFFERENCE: Double = 1e11
|
private const val MA_MAX_MINIMIZE_SCORE_DIFFERENCE: Long = 100000000000L // 1e11
|
||||||
private const val MA_MAX_DUDD_WEIGHT: Double = MA_MAX_MINIMIZE_SCORE_DIFFERENCE / 1000; // Draw-ups Draw-downs
|
private const val MA_MAX_DUDD_WEIGHT: Long = MA_MAX_MINIMIZE_SCORE_DIFFERENCE / 1000; // Draw-ups Draw-downs
|
||||||
enum class MA_DUDD {TOP, MID, BOT}
|
enum class MA_DUDD {TOP, MID, BOT}
|
||||||
|
|
||||||
private const val MA_MAX_MAXIMIZE_SEEDING: Double = MA_MAX_MINIMIZE_SCORE_DIFFERENCE / 20000;
|
private const val MA_MAX_MAXIMIZE_SEEDING: Long = MA_MAX_MINIMIZE_SCORE_DIFFERENCE / 20000;
|
||||||
|
|
||||||
enum class SeedMethod { SPLIT_AND_FOLD, SPLIT_AND_RANDOM, SPLIT_AND_SLIP }
|
enum class SeedMethod { SPLIT_AND_FOLD, SPLIT_AND_RANDOM, SPLIT_AND_SLIP }
|
||||||
|
|
||||||
@@ -50,14 +50,14 @@ sealed class Pairing(val type: PairingType, val pairingParams: PairingParams = P
|
|||||||
// Main criteria
|
// Main criteria
|
||||||
// TODO move avoidmixingcategories to swiss with category
|
// TODO move avoidmixingcategories to swiss with category
|
||||||
//val maAvoidMixingCategories: Double = MA_MAX_AVOID_MIXING_CATEGORIES,
|
//val maAvoidMixingCategories: Double = MA_MAX_AVOID_MIXING_CATEGORIES,
|
||||||
val mainMinimizeScoreDifference: Double = MA_MAX_MINIMIZE_SCORE_DIFFERENCE,
|
val mainMinimizeScoreDifference: Long = MA_MAX_MINIMIZE_SCORE_DIFFERENCE,
|
||||||
|
|
||||||
val maDUDDWeight: Double = MA_MAX_DUDD_WEIGHT,
|
val maDUDDWeight: Long = MA_MAX_DUDD_WEIGHT,
|
||||||
val maCompensateDUDD: Boolean = true,
|
val maCompensateDUDD: Boolean = true,
|
||||||
val maDUDDUpperMode: MA_DUDD = MA_DUDD.MID,
|
val maDUDDUpperMode: MA_DUDD = MA_DUDD.MID,
|
||||||
val maDUDDLowerMode: MA_DUDD = MA_DUDD.MID,
|
val maDUDDLowerMode: MA_DUDD = MA_DUDD.MID,
|
||||||
|
|
||||||
val maMaximizeSeeding: Double = MA_MAX_MAXIMIZE_SEEDING, // 5 *10^6
|
val maMaximizeSeeding: Long = MA_MAX_MAXIMIZE_SEEDING, // 5 *10^6
|
||||||
val maLastRoundForSeedSystem1: Int = 1,
|
val maLastRoundForSeedSystem1: Int = 1,
|
||||||
val maSeedSystem1: SeedMethod = SeedMethod.SPLIT_AND_RANDOM,
|
val maSeedSystem1: SeedMethod = SeedMethod.SPLIT_AND_RANDOM,
|
||||||
val maSeedSystem2: SeedMethod = SeedMethod.SPLIT_AND_FOLD,
|
val maSeedSystem2: SeedMethod = SeedMethod.SPLIT_AND_FOLD,
|
||||||
@@ -68,7 +68,7 @@ sealed class Pairing(val type: PairingType, val pairingParams: PairingParams = P
|
|||||||
val seBarThresholdActive: Boolean = true, // Do not apply secondary criteria for players above bar
|
val seBarThresholdActive: Boolean = true, // Do not apply secondary criteria for players above bar
|
||||||
val seRankThreshold: Int = 0, // Do not apply secondary criteria above 1D rank
|
val seRankThreshold: Int = 0, // Do not apply secondary criteria above 1D rank
|
||||||
val seNbWinsThresholdActive: Boolean = true, // Do not apply secondary criteria when nbWins >= nbRounds / 2
|
val seNbWinsThresholdActive: Boolean = true, // Do not apply secondary criteria when nbWins >= nbRounds / 2
|
||||||
val seDefSecCrit: Double = MA_MAX_AVOID_MIXING_CATEGORIES, // Should be MA_MAX_MINIMIZE_SCORE_DIFFERENCE for MM, MA_MAX_AVOID_MIXING_CATEGORIES for others
|
val seDefSecCrit: Long = MA_MAX_AVOID_MIXING_CATEGORIES, // Should be MA_MAX_MINIMIZE_SCORE_DIFFERENCE for MM, MA_MAX_AVOID_MIXING_CATEGORIES for others
|
||||||
|
|
||||||
// Geographical params
|
// Geographical params
|
||||||
val geo: GeographicalParams = GeographicalParams(avoidSameGeo = seDefSecCrit),
|
val geo: GeographicalParams = GeographicalParams(avoidSameGeo = seDefSecCrit),
|
||||||
@@ -82,19 +82,19 @@ sealed class Pairing(val type: PairingType, val pairingParams: PairingParams = P
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class GeographicalParams(
|
data class GeographicalParams(
|
||||||
val avoidSameGeo: Double, // Should be SeDefSecCrit for SwCat and MM, 0 for Swiss
|
val avoidSameGeo: Long, // Should be SeDefSecCrit for SwCat and MM, 0 for Swiss
|
||||||
val preferMMSDiffRatherThanSameCountry: Int = 1, // Typically = 1
|
val preferMMSDiffRatherThanSameCountry: Int = 1, // Typically = 1
|
||||||
val preferMMSDiffRatherThanSameClubsGroup: Int = 2, // Typically = 2
|
val preferMMSDiffRatherThanSameClubsGroup: Int = 2, // Typically = 2
|
||||||
val preferMMSDiffRatherThanSameClub: Int = 3, // Typically = 3
|
val preferMMSDiffRatherThanSameClub: Int = 3, // Typically = 3
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun disabled() = GeographicalParams(avoidSameGeo = 0.0)
|
fun disabled() = GeographicalParams(avoidSameGeo = 0L)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HandicapParams(
|
data class HandicapParams(
|
||||||
// minimizeHandicap is a secondary criteria but moved here
|
// minimizeHandicap is a secondary criteria but moved here
|
||||||
val minimizeHandicap: Double, // Should be paiSeDefSecCrit for SwCat, 0 for others
|
val minimizeHandicap: Long, // Should be paiSeDefSecCrit for SwCat, 0 for others
|
||||||
val basedOnMMS: Boolean = true, // if hdBasedOnMMS is false, hd will be based on rank
|
val basedOnMMS: Boolean = true, // if hdBasedOnMMS is false, hd will be based on rank
|
||||||
// When one player in the game has a rank of at least hdNoHdRankThreshold,
|
// When one player in the game has a rank of at least hdNoHdRankThreshold,
|
||||||
// then the game will be without handicap
|
// then the game will be without handicap
|
||||||
@@ -104,7 +104,7 @@ data class HandicapParams(
|
|||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun disabled() = HandicapParams(
|
fun disabled() = HandicapParams(
|
||||||
minimizeHandicap = 0.0,
|
minimizeHandicap = 0L,
|
||||||
basedOnMMS = false,
|
basedOnMMS = false,
|
||||||
noHdRankThreshold=-30, // 30k
|
noHdRankThreshold=-30, // 30k
|
||||||
ceiling=0)
|
ceiling=0)
|
||||||
@@ -161,7 +161,7 @@ fun HandicapParams.toJson() = Json.Object(
|
|||||||
"ceiling" to ceiling, )
|
"ceiling" to ceiling, )
|
||||||
|
|
||||||
fun HandicapParams.fromJson(json: Json.Object) = HandicapParams(
|
fun HandicapParams.fromJson(json: Json.Object) = HandicapParams(
|
||||||
minimizeHandicap=json.getDouble("minimize_hd")!!,
|
minimizeHandicap=json.getLong("minimize_hd")!!,
|
||||||
basedOnMMS=json.getBoolean("mms_based")!!,
|
basedOnMMS=json.getBoolean("mms_based")!!,
|
||||||
noHdRankThreshold=json.getInt("no_hd_thresh")!!,
|
noHdRankThreshold=json.getInt("no_hd_thresh")!!,
|
||||||
correction=json.getInt("correction")!!,
|
correction=json.getInt("correction")!!,
|
||||||
@@ -175,7 +175,7 @@ fun GeographicalParams.toJson() = Json.Object(
|
|||||||
"club" to preferMMSDiffRatherThanSameClub,)
|
"club" to preferMMSDiffRatherThanSameClub,)
|
||||||
|
|
||||||
fun GeographicalParams.fromJson(json: Json.Object) = GeographicalParams(
|
fun GeographicalParams.fromJson(json: Json.Object) = GeographicalParams(
|
||||||
avoidSameGeo=json.getDouble("avoid_same_geo")!!,
|
avoidSameGeo=json.getLong("avoid_same_geo")!!,
|
||||||
preferMMSDiffRatherThanSameCountry=json.getInt("country")!!,
|
preferMMSDiffRatherThanSameCountry=json.getInt("country")!!,
|
||||||
preferMMSDiffRatherThanSameClubsGroup=json.getInt("club_group")!!,
|
preferMMSDiffRatherThanSameClubsGroup=json.getInt("club_group")!!,
|
||||||
preferMMSDiffRatherThanSameClub=json.getInt("club")!!,
|
preferMMSDiffRatherThanSameClub=json.getInt("club")!!,
|
||||||
|
@@ -180,11 +180,17 @@ sealed class Solver(
|
|||||||
// Main criterion 2 minimize score difference
|
// Main criterion 2 minimize score difference
|
||||||
score += minimizeScoreDifference(p1, p2)
|
score += minimizeScoreDifference(p1, p2)
|
||||||
|
|
||||||
|
// Main criterion 3 If different groups, make a directed Draw-up/Draw-down
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Main criterion 4 seeding
|
||||||
|
score += applySeeding(p1, p2)
|
||||||
|
|
||||||
return score
|
return score
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun minimizeScoreDifference(p1: Pairable, p2: Pairable): Long {
|
open fun minimizeScoreDifference(p1: Pairable, p2: Pairable): Long {
|
||||||
var score: Long = 0
|
var score = 0L
|
||||||
val scoreRange: Int = numberGroups
|
val scoreRange: Int = numberGroups
|
||||||
// TODO check category equality if category are used in SwissCat
|
// TODO check category equality if category are used in SwissCat
|
||||||
val x = abs(p1.group - p2.group) as Double / scoreRange.toDouble()
|
val x = abs(p1.group - p2.group) as Double / scoreRange.toDouble()
|
||||||
@@ -194,6 +200,44 @@ sealed class Solver(
|
|||||||
return score
|
return score
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun applySeeding(p1: Pairable, p2: Pairable): Long {
|
||||||
|
var score = 0L
|
||||||
|
// Apply seeding for players in the same group
|
||||||
|
if (p1.group == p2.group) {
|
||||||
|
val (cla1, groupSize) = p1.placeInGroup
|
||||||
|
val cla2 = p2.placeInGroup.first
|
||||||
|
val maxSeedingWeight = pairingParams.maMaximizeSeeding
|
||||||
|
|
||||||
|
val currentSeedSystem: SeedMethod = if (round <= pairingParams.maLastRoundForSeedSystem1)
|
||||||
|
pairingParams.maSeedSystem1 else pairingParams.maSeedSystem2
|
||||||
|
|
||||||
|
score += when(currentSeedSystem) {
|
||||||
|
// The best is to get 2 * |Cla1 - Cla2| - groupSize close to 0
|
||||||
|
SeedMethod.SPLIT_AND_SLIP -> {
|
||||||
|
val x = 2 * abs(cla1 - cla2) - groupSize
|
||||||
|
maxSeedingWeight - maxSeedingWeight * x / groupSize * x / groupSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// The best is to get cla1 + cla2 - (groupSize - 1) close to 0
|
||||||
|
SeedMethod.SPLIT_AND_FOLD -> {
|
||||||
|
val x = cla1 + cla2 - (groupSize - 1)
|
||||||
|
maxSeedingWeight - maxSeedingWeight * x / (groupSize - 1) * x / (groupSize - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
SeedMethod.SPLIT_AND_RANDOM -> {
|
||||||
|
if ((2 * cla1 < groupSize && 2 * cla2 >= groupSize) || (2 * cla1 >= groupSize && 2 * cla2 < groupSize)) {
|
||||||
|
val randRange = (maxSeedingWeight * 0.2).toLong()
|
||||||
|
val rand = detRandom(randRange, p1, p2)
|
||||||
|
maxSeedingWeight - rand
|
||||||
|
} else {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
// Handicap functions
|
// Handicap functions
|
||||||
// Has to be overridden if handicap is not based on rank
|
// Has to be overridden if handicap is not based on rank
|
||||||
open fun handicap(p1: Pairable, p2: Pairable): Int {
|
open fun handicap(p1: Pairable, p2: Pairable): Int {
|
||||||
@@ -237,19 +281,13 @@ sealed class Solver(
|
|||||||
else HistoryHelper(history, standingScore)
|
else HistoryHelper(history, standingScore)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Decide each pairable group based on the main criterion
|
// Decide each pairable group based on the main criterion
|
||||||
private fun computeGroups(): Pair<Map<ID, Int>, Int> {
|
private val numberGroups by lazy {
|
||||||
val (mainScoreMin, mainScoreMax) = mainCriterionMinMax()
|
val (mainScoreMin, mainScoreMax) = mainCriterionMinMax()
|
||||||
|
mainScoreMax - mainScoreMin
|
||||||
// TODO categories
|
|
||||||
val groups: Map<ID, Int> = pairables.associate { pairable -> Pair(pairable.id, mainCriterion(pairable)) }
|
|
||||||
|
|
||||||
return Pair(groups, mainScoreMax - mainScoreMin)
|
|
||||||
}
|
}
|
||||||
|
private val _groups = pairables.associate { pairable -> Pair(pairable.id, mainCriterion(pairable)) }
|
||||||
private val groupsResult = computeGroups()
|
|
||||||
private val _groups = groupsResult.first
|
|
||||||
private val numberGroups = groupsResult.second
|
|
||||||
|
|
||||||
// pairables sorted using overloadable sort function
|
// pairables sorted using overloadable sort function
|
||||||
private val sortedPairables by lazy {
|
private val sortedPairables by lazy {
|
||||||
@@ -265,7 +303,7 @@ sealed class Solver(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// placeInGroup (of same score) : Pair(place, groupSize)
|
// placeInGroup (of same score) : Pair(place, groupSize)
|
||||||
val Pairable.placeInGroup: Pair<Int, Int> get() = _placeInGroup[id]!!
|
private val Pairable.placeInGroup: Pair<Int, Int> get() = _placeInGroup[id]!!
|
||||||
private val _placeInGroup by lazy {
|
private val _placeInGroup by lazy {
|
||||||
sortedPairables.groupBy {
|
sortedPairables.groupBy {
|
||||||
it.group
|
it.group
|
||||||
|
Reference in New Issue
Block a user