Tackle solver reeng
This commit is contained in:
@@ -8,7 +8,7 @@ import org.jeudego.pairgoth.pairing.MacMahonSolver
|
||||
import org.jeudego.pairgoth.pairing.SwissSolver
|
||||
|
||||
// base pairing parameters
|
||||
data class BasePairingParams(
|
||||
data class BaseCritParams(
|
||||
// standard NX1 factor for concavity curves
|
||||
val nx1: Double = 0.5,
|
||||
val dupWeight: Double = MAX_AVOIDDUPGAME,
|
||||
@@ -27,7 +27,7 @@ data class BasePairingParams(
|
||||
const val MAX_AVOIDDUPGAME = 500000000000000.0 // 5e14
|
||||
const val MAX_RANDOM = 1000000000.0 // 1e9
|
||||
const val MAX_COLOR_BALANCE = 1000000.0 // 1e6
|
||||
val default = BasePairingParams()
|
||||
val default = BaseCritParams()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ data class HandicapParams(
|
||||
enum class PairingType { SWISS, MAC_MAHON, ROUND_ROBIN }
|
||||
|
||||
data class PairingParams(
|
||||
val base: BasePairingParams = BasePairingParams(),
|
||||
val base: BaseCritParams = BaseCritParams(),
|
||||
val main: MainCritParams = MainCritParams(),
|
||||
val secondary: SecondaryCritParams = SecondaryCritParams(),
|
||||
val geo: GeographicalParams = GeographicalParams(),
|
||||
@@ -123,11 +123,11 @@ sealed class Pairing(
|
||||
|
||||
private fun Tournament<*>.historyBefore(round: Int) =
|
||||
if (lastRound() == 0) emptyList()
|
||||
else (0 until round).flatMap { games(round).values }
|
||||
else (0 until round).map { games(round).values.toList() }
|
||||
|
||||
class Swiss(
|
||||
pairingParams: PairingParams = PairingParams(
|
||||
base = BasePairingParams(),
|
||||
base = BaseCritParams(),
|
||||
main = MainCritParams(
|
||||
seedSystem1 = SPLIT_AND_SLIP,
|
||||
seedSystem2 = SPLIT_AND_SLIP
|
||||
@@ -153,7 +153,7 @@ class Swiss(
|
||||
|
||||
class MacMahon(
|
||||
pairingParams: PairingParams = PairingParams(
|
||||
base = BasePairingParams(),
|
||||
base = BaseCritParams(),
|
||||
main = MainCritParams(),
|
||||
secondary = SecondaryCritParams(
|
||||
defSecCrit = MainCritParams.MAX_SCORE_WEIGHT
|
||||
@@ -184,7 +184,7 @@ class RoundRobin(
|
||||
|
||||
// Serialization
|
||||
|
||||
fun BasePairingParams.Companion.fromJson(json: Json.Object) = BasePairingParams(
|
||||
fun BaseCritParams.Companion.fromJson(json: Json.Object) = BaseCritParams(
|
||||
nx1 = json.getDouble("nx1") ?: default.nx1,
|
||||
dupWeight = json.getDouble("dupWeight") ?: default.dupWeight,
|
||||
random = json.getDouble("random") ?: default.random,
|
||||
@@ -192,7 +192,7 @@ fun BasePairingParams.Companion.fromJson(json: Json.Object) = BasePairingParams(
|
||||
colorBalance = json.getDouble("colorBalanceWeight") ?: default.colorBalance
|
||||
)
|
||||
|
||||
fun BasePairingParams.toJson() = Json.Object(
|
||||
fun BaseCritParams.toJson() = Json.Object(
|
||||
"nx1" to nx1,
|
||||
"dupWeight" to dupWeight,
|
||||
"random" to random,
|
||||
@@ -281,7 +281,7 @@ fun Pairing.Companion.fromJson(json: Json.Object): Pairing {
|
||||
MAC_MAHON -> MacMahon()
|
||||
ROUND_ROBIN -> RoundRobin()
|
||||
}
|
||||
val base = json.getObject("base")?.let { BasePairingParams.fromJson(it) } ?: defaultParams.pairingParams.base
|
||||
val base = json.getObject("base")?.let { BaseCritParams.fromJson(it) } ?: defaultParams.pairingParams.base
|
||||
val main = json.getObject("main")?.let { MainCritParams.fromJson(it) } ?: defaultParams.pairingParams.main
|
||||
val secondary = json.getObject("secondary")?.let { SecondaryCritParams.fromJson(it) } ?: defaultParams.pairingParams.secondary
|
||||
val geo = json.getObject("geo")?.let { GeographicalParams.fromJson(it) } ?: defaultParams.pairingParams.geo
|
||||
|
@@ -1,8 +1,19 @@
|
||||
package org.jeudego.pairgoth.pairing
|
||||
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import org.jeudego.pairgoth.model.Game.Result.*
|
||||
|
||||
open class HistoryHelper(protected val history: List<Game>, computeScore: () -> Map<ID, Double>) {
|
||||
open class HistoryHelper(protected val history: List<List<Game>>, computeScore: () -> Map<ID, Double>) {
|
||||
|
||||
private val Game.blackScore get() = when (result) {
|
||||
BLACK, BOTHWIN -> 1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
private val Game.whiteScore get() = when (result) {
|
||||
WHITE, BOTHWIN -> 1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
fun getCriterionValue(p: Pairable, crit: Criterion): Double {
|
||||
// Returns generic criterion
|
||||
@@ -23,13 +34,12 @@ open class HistoryHelper(protected val history: List<Game>, computeScore: () ->
|
||||
// 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 nbW(p: Pairable) = numberWins[p.id]
|
||||
|
||||
open fun nbW(p: Pairable) = wins[p.id]
|
||||
|
||||
protected val paired: Set<Pair<ID, ID>> by lazy {
|
||||
(history.map { game ->
|
||||
(history.flatten().map { game ->
|
||||
Pair(game.black, game.white)
|
||||
} + history.map { game ->
|
||||
} + history.flatten().map { game ->
|
||||
Pair(game.white, game.black)
|
||||
}).toSet()
|
||||
}
|
||||
@@ -37,25 +47,26 @@ open class HistoryHelper(protected val history: List<Game>, computeScore: () ->
|
||||
// Returns the number of games played as white
|
||||
// Only count games without handicap
|
||||
private val colorBalance: Map<ID, Int> by lazy {
|
||||
history.flatMap { game -> if (game.handicap == 0) {
|
||||
history.flatten().filter { game ->
|
||||
game.handicap == 0
|
||||
}.flatMap { game ->
|
||||
listOf(Pair(game.white, +1), Pair(game.black, -1))
|
||||
} else {
|
||||
listOf(Pair(game.white, 0), Pair(game.black, 0))
|
||||
}
|
||||
}.groupingBy { it.first }.fold(0) { acc, next ->
|
||||
}.groupingBy {
|
||||
it.first
|
||||
}.fold(0) { acc, next ->
|
||||
acc + next.second
|
||||
}
|
||||
}
|
||||
|
||||
val numberWins: Map<ID, Double> by lazy {
|
||||
val wins: Map<ID, Double> by lazy {
|
||||
mutableMapOf<ID, Double>().apply {
|
||||
history.forEach { game ->
|
||||
history.flatten().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)
|
||||
put(game.black, getOrDefault(game.black, 0.0) + 1.0)
|
||||
put(game.white, getOrDefault(game.white, 0.0) + 1.0)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
@@ -70,9 +81,9 @@ open class HistoryHelper(protected val history: List<Game>, computeScore: () ->
|
||||
|
||||
// SOS related functions given a score function
|
||||
val sos by lazy {
|
||||
(history.map { game ->
|
||||
(history.flatten().map { game ->
|
||||
Pair(game.black, score[game.white] ?: 0.0)
|
||||
} + history.map { game ->
|
||||
} + history.flatten().map { game ->
|
||||
Pair(game.white, score[game.black] ?: 0.0)
|
||||
}).groupingBy { it.first }.fold(0.0) { acc, next ->
|
||||
acc + next.second
|
||||
@@ -80,20 +91,38 @@ open class HistoryHelper(protected val history: List<Game>, computeScore: () ->
|
||||
}
|
||||
|
||||
// sos-1
|
||||
val sosm1: Map<ID, Double> by lazy {
|
||||
TODO()
|
||||
val sosm1 by lazy {
|
||||
(history.flatten().map { game ->
|
||||
Pair(game.black, score[game.white] ?: 0.0)
|
||||
} + history.flatten().map { game ->
|
||||
Pair(game.white, score[game.black] ?: 0.0)
|
||||
}).groupBy {
|
||||
it.first
|
||||
}.mapValues {
|
||||
val scores = it.value.map { it.second }.sorted()
|
||||
scores.sum() - (scores.firstOrNull() ?: 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
// sos-2
|
||||
val sosm2: Map<ID, Double> by lazy {
|
||||
TODO()
|
||||
val sosm2 by lazy {
|
||||
(history.flatten().map { game ->
|
||||
Pair(game.black, score[game.white] ?: 0.0)
|
||||
} + history.flatten().map { game ->
|
||||
Pair(game.white, score[game.black] ?: 0.0)
|
||||
}).groupBy {
|
||||
it.first
|
||||
}.mapValues {
|
||||
val scores = it.value.map { it.second }.sorted()
|
||||
scores.sum() - scores.getOrElse(0) { 0.0 } - scores.getOrElse(1) { 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
// sodos
|
||||
val sodos by lazy {
|
||||
(history.map { game ->
|
||||
(history.flatten().map { game ->
|
||||
Pair(game.black, if (game.result == Game.Result.BLACK) score[game.white] ?: 0.0 else 0.0)
|
||||
} + history.map { game ->
|
||||
} + history.flatten().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
|
||||
@@ -102,9 +131,9 @@ open class HistoryHelper(protected val history: List<Game>, computeScore: () ->
|
||||
|
||||
// sosos
|
||||
val sosos by lazy {
|
||||
(history.map { game ->
|
||||
(history.flatten().map { game ->
|
||||
Pair(game.black, sos[game.white] ?: 0.0)
|
||||
} + history.map { game ->
|
||||
} + history.flatten().map { game ->
|
||||
Pair(game.white, sos[game.black] ?: 0.0)
|
||||
}).groupingBy { it.first }.fold(0.0) { acc, next ->
|
||||
acc + next.second
|
||||
@@ -112,14 +141,25 @@ open class HistoryHelper(protected val history: List<Game>, computeScore: () ->
|
||||
}
|
||||
|
||||
// cumulative score
|
||||
val cumscore: Map<ID, Double> by lazy {
|
||||
TODO()
|
||||
val cumScore by lazy {
|
||||
history.map { games ->
|
||||
(games.groupingBy { it.black }.fold(0.0) { acc, next ->
|
||||
acc + next.blackScore
|
||||
}) +
|
||||
(games.groupingBy { it.white }.fold(0.0) { acc, next ->
|
||||
acc + next.whiteScore
|
||||
})
|
||||
}.reduce { acc, map ->
|
||||
(acc.keys + map.keys).associate<ID, ID, Double> { id ->
|
||||
Pair(id, acc.getOrDefault(id, 0.0) + acc.getOrDefault(id, 0.0) + map.getOrDefault(id, 0.0))
|
||||
}.toMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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>, computeScore: () -> Map<ID, Double>):
|
||||
class TeamOfIndividualsHistoryHelper(history: List<List<Game>>, computeScore: () -> Map<ID, Double>):
|
||||
HistoryHelper(history, computeScore) {
|
||||
|
||||
private fun Pairable.asTeam() = this as TeamTournament.Team
|
||||
|
@@ -2,7 +2,12 @@ package org.jeudego.pairgoth.pairing
|
||||
|
||||
import org.jeudego.pairgoth.model.*
|
||||
|
||||
class MacMahonSolver(round: Int, history: List<Game>, pairables: List<Pairable>, pairingParams: PairingParams, placementParams: PlacementParams): Solver(round, history, pairables, pairingParams, placementParams) {
|
||||
class MacMahonSolver(round: Int,
|
||||
history: List<List<Game>>,
|
||||
pairables: List<Pairable>,
|
||||
pairingParams: PairingParams,
|
||||
placementParams: PlacementParams):
|
||||
Solver(round, history, pairables, pairingParams, placementParams) {
|
||||
|
||||
// val Pairable.mms get() = mmBase + nbW // TODO real calculation
|
||||
|
||||
@@ -32,5 +37,4 @@ class MacMahonSolver(round: Int, history: List<Game>, pairables: List<Pairable>,
|
||||
else -> -1.0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -17,23 +17,15 @@ val DEBUG_EXPORT_WEIGHT = true
|
||||
|
||||
private fun detRandom(max: Double, p1: Pairable, p2: Pairable): Double {
|
||||
var inverse = false
|
||||
|
||||
val seed1 = p1.nameSeed()
|
||||
val seed2 = p2.nameSeed()
|
||||
|
||||
var name1 = seed1
|
||||
var name2 = seed2
|
||||
var name1 = p1.nameSeed()
|
||||
var name2 = p2.nameSeed()
|
||||
if (name1 > name2) {
|
||||
name1 = name2.also { name2 = name1 }
|
||||
inverse = true
|
||||
}
|
||||
val s = name1 + name2
|
||||
var nR = 0.0
|
||||
for (i in s.indices) {
|
||||
val c = s[i]
|
||||
nR += (c.code * (i + 1)).toDouble()
|
||||
}
|
||||
nR = nR * 1234567 % (max + 1)
|
||||
var nR = "$name1$name2".mapIndexed { i, c ->
|
||||
c.code.toDouble() * (i + 1)
|
||||
}.sum() * 1234567 % (max + 1)
|
||||
if (inverse) nR = max - nR
|
||||
return nR
|
||||
}
|
||||
@@ -43,18 +35,22 @@ private fun nonDetRandom(max: Double) =
|
||||
else Math.random() * (max + 1.0)
|
||||
|
||||
sealed class Solver(
|
||||
val round: Int,
|
||||
history: List<Game>,
|
||||
val pairables: List<Pairable>,
|
||||
val pairingParams: PairingParams,
|
||||
val placementParams: PlacementParams) {
|
||||
val round: Int,
|
||||
history: List<List<Game>>,
|
||||
val pairables: List<Pairable>,
|
||||
val pairing: PairingParams,
|
||||
val placement: PlacementParams
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val rand = Random(/* seed from properties - TODO */)
|
||||
}
|
||||
|
||||
val historyHelper = if (pairables.first().let { it is TeamTournament.Team && it.teamOfIndividuals }) TeamOfIndividualsHistoryHelper(history, ::computeStandingScore)
|
||||
else HistoryHelper(history, ::computeStandingScore)
|
||||
|
||||
open fun sort(p: Pairable, q: Pairable): Int {
|
||||
for (criterion in placementParams.criteria) {
|
||||
for (criterion in placement.criteria) {
|
||||
val criterionP = getCriterionValue(p, criterion)
|
||||
val criterionQ = getCriterionValue(q, criterion)
|
||||
if (criterionP != criterionQ) {
|
||||
@@ -65,9 +61,10 @@ sealed class Solver(
|
||||
}
|
||||
open fun weight(p1: Pairable, p2: Pairable) =
|
||||
1.0 + // 1 is minimum value because 0 means "no matching allowed"
|
||||
applyBaseCriteria(p1, p2) +
|
||||
applyMainCriteria(p1, p2) +
|
||||
applySecondaryCriteria(p1, p2)
|
||||
pairing.base.apply(p1, p2) +
|
||||
pairing.main.apply(p1, p2) +
|
||||
pairing.secondary.apply(p1, p2) +
|
||||
pairing.geo.apply(p1, p2)
|
||||
|
||||
// The main criterion that will be used to define the groups should be defined by subclasses
|
||||
abstract fun mainCriterion(p1: Pairable): Int
|
||||
@@ -114,22 +111,48 @@ sealed class Solver(
|
||||
return result
|
||||
}
|
||||
|
||||
open fun applyBaseCriteria(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
// base criteria
|
||||
|
||||
open fun BaseCritParams.apply(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
// Base Criterion 1 : Avoid Duplicating Game
|
||||
// Did p1 and p2 already play ?
|
||||
score += avoidDuplicatingGames(p1, p2)
|
||||
// Base Criterion 2 : Random
|
||||
score += applyRandom(p1, p2)
|
||||
// Base Criterion 3 : Balance W and B
|
||||
score += applyBalanceBW(p1, p2)
|
||||
score += applyColorBalance(p1, p2)
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
// Weight score computation details
|
||||
open fun BaseCritParams.avoidDuplicatingGames(p1: Pairable, p2: Pairable): Double {
|
||||
return if (p1.played(p2)) 0.0 // We get no score if pairables already played together
|
||||
else dupWeight
|
||||
}
|
||||
|
||||
open fun BaseCritParams.applyRandom(p1: Pairable, p2: Pairable): Double {
|
||||
return if (deterministic) detRandom(random, p1, p2)
|
||||
else nonDetRandom(random)
|
||||
}
|
||||
|
||||
open fun BaseCritParams.applyColorBalance(p1: Pairable, p2: Pairable): Double {
|
||||
// This cost is never applied if potential Handicap != 0
|
||||
// It is fully applied if wbBalance(sP1) and wbBalance(sP2) are strictly of different signs
|
||||
// It is half applied if one of wbBalance is 0 and the other is >=2
|
||||
val potentialHd: Int = pairing.handicap.handicap(p1, p2)
|
||||
if (potentialHd == 0) {
|
||||
val wb1: Int = p1.colorBalance
|
||||
val wb2: Int = p2.colorBalance
|
||||
if (wb1 * wb2 < 0) return colorBalance
|
||||
else if (wb1 == 0 && abs(wb2) >= 2 || wb2 == 0 && abs(wb1) >= 2) return colorBalance / 2
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
// Main criteria
|
||||
open fun applyMainCriteria(p1: Pairable, p2: Pairable): Double {
|
||||
open fun MainCritParams.apply(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
|
||||
// Main criterion 1 avoid mixing category is moved to Swiss with category
|
||||
@@ -147,76 +170,26 @@ sealed class Solver(
|
||||
return score
|
||||
}
|
||||
|
||||
open fun applySecondaryCriteria(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
// See Swiss with category for minimizing handicap criterion
|
||||
|
||||
// TODO understand where opengotha test if need to be applied
|
||||
|
||||
// Geographical criterion
|
||||
score += avoidSameGeo(p1, p2)
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
// Weight score computation details
|
||||
// Base criteria
|
||||
open fun avoidDuplicatingGames(p1: Pairable, p2: Pairable): Double {
|
||||
if (p1.played(p2)) {
|
||||
return 0.0 // We get no score if pairables already played together
|
||||
} else {
|
||||
return pairingParams.base.dupWeight
|
||||
}
|
||||
}
|
||||
|
||||
open fun applyRandom(p1: Pairable, p2: Pairable): Double {
|
||||
if (pairingParams.base.deterministic) {
|
||||
return detRandom(pairingParams.base.random, p1, p2)
|
||||
} else {
|
||||
return nonDetRandom(pairingParams.base.random)
|
||||
}
|
||||
}
|
||||
|
||||
open fun applyBalanceBW(p1: Pairable, p2: Pairable): Double {
|
||||
// This cost is never applied if potential Handicap != 0
|
||||
// It is fully applied if wbBalance(sP1) and wbBalance(sP2) are strictly of different signs
|
||||
// It is half applied if one of wbBalance is 0 and the other is >=2
|
||||
val potentialHd: Int = handicap(p1, p2)
|
||||
if (potentialHd == 0) {
|
||||
val wb1: Int = p1.colorBalance
|
||||
val wb2: Int = p2.colorBalance
|
||||
if (wb1 * wb2 < 0) {
|
||||
return pairingParams.base.colorBalance
|
||||
} else if (wb1 == 0 && abs(wb2) >= 2) {
|
||||
return pairingParams.base.colorBalance / 2
|
||||
} else if (wb2 == 0 && abs(wb1) >= 2) {
|
||||
return pairingParams.base.colorBalance / 2
|
||||
}
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
open fun minimizeScoreDifference(p1: Pairable, p2: Pairable): Double {
|
||||
open fun MainCritParams.minimizeScoreDifference(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
val scoreRange: Int = numberGroups
|
||||
// TODO check category equality if category are used in SwissCat
|
||||
val x = abs(p1.group - p2.group) as Double / scoreRange.toDouble()
|
||||
val k: Double = pairingParams.base.nx1
|
||||
score = pairingParams.main.scoreWeight * (1.0 - x) * (1.0 + k * x)
|
||||
val x = abs(p1.group - p2.group).toDouble() / scoreRange.toDouble()
|
||||
val k: Double = pairing.base.nx1
|
||||
score = scoreWeight * (1.0 - x) * (1.0 + k * x)
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
fun applySeeding(p1: Pairable, p2: Pairable): Double {
|
||||
fun MainCritParams.applySeeding(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
// 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.main.seedingWeight
|
||||
val maxSeedingWeight = seedingWeight
|
||||
|
||||
val currentSeedSystem: MainCritParams.SeedMethod = if (round <= pairingParams.main.lastRoundForSeedSystem1)
|
||||
pairingParams.main.seedSystem1 else pairingParams.main.seedSystem2
|
||||
val currentSeedSystem= if (round <= lastRoundForSeedSystem1) seedSystem1 else seedSystem2
|
||||
|
||||
score += when(currentSeedSystem) {
|
||||
// The best is to get 2 * |Cla1 - Cla2| - groupSize close to 0
|
||||
@@ -245,21 +218,30 @@ sealed class Solver(
|
||||
return score
|
||||
}
|
||||
|
||||
open fun doNeedToApplySecondaryCriteria(p1: Pairable, p2: Pairable) {
|
||||
open fun SecondaryCritParams.apply(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
// See Swiss with category for minimizing handicap criterion
|
||||
|
||||
// TODO understand where opengotha test if need to be applied
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
open fun SecondaryCritParams.notNeeded(p1: Pairable, p2: Pairable) {
|
||||
// secCase = 0 : No player is above thresholds
|
||||
// secCase = 1 : One player is above thresholds
|
||||
// secCase = 2 : Both players are above thresholds
|
||||
// TODO understand where it is used
|
||||
}
|
||||
|
||||
fun avoidSameGeo(p1: Pairable, p2: Pairable): Double {
|
||||
fun GeographicalParams.apply(p1: Pairable, p2: Pairable): Double {
|
||||
val placementScoreRange = numberGroups
|
||||
|
||||
val geoMaxCost = pairingParams.geo.avoidSameGeo
|
||||
val geoMaxCost = avoidSameGeo
|
||||
|
||||
val countryFactor = pairingParams.geo.preferMMSDiffRatherThanSameCountry
|
||||
val clubFactor: Int = pairingParams.geo.preferMMSDiffRatherThanSameClub
|
||||
//val groupFactor: Int = pairingParams.geo.preferMMSDiffRatherThanSameClubsGroup
|
||||
val countryFactor = preferMMSDiffRatherThanSameCountry
|
||||
val clubFactor: Int = preferMMSDiffRatherThanSameClub
|
||||
//val groupFactor: Int = preferMMSDiffRatherThanSameClubsGroup
|
||||
|
||||
// Same country
|
||||
val countryRatio = if (p1.country != p2.country && countryFactor != 0) {
|
||||
@@ -298,12 +280,12 @@ sealed class Solver(
|
||||
|
||||
var geoRatio = mainPart + secPart / 2.0
|
||||
if (geoRatio > 0.0) {
|
||||
geoRatio += 0.5 / placementScoreRange as Double
|
||||
geoRatio += 0.5 / placementScoreRange.toDouble()
|
||||
}
|
||||
|
||||
// The concavity function is applied to geoRatio to get geoCost
|
||||
val dbGeoCost: Double = geoMaxCost.toDouble() * (1.0 - geoRatio) * (1.0 + pairingParams.base.nx1 * geoRatio)
|
||||
var score = pairingParams.main.scoreWeight - dbGeoCost
|
||||
val dbGeoCost: Double = geoMaxCost.toDouble() * (1.0 - geoRatio) * (1.0 + pairing.base.nx1 * geoRatio)
|
||||
var score = pairing.main.scoreWeight - dbGeoCost
|
||||
score = min(score, geoMaxCost)
|
||||
|
||||
return score
|
||||
@@ -311,48 +293,37 @@ sealed class Solver(
|
||||
|
||||
// Handicap functions
|
||||
// Has to be overridden if handicap is not based on rank
|
||||
open fun handicap(p1: Pairable, p2: Pairable): Int {
|
||||
open fun HandicapParams.handicap(p1: Pairable, p2: Pairable): Int {
|
||||
var hd = 0
|
||||
var pseudoRank1: Int = p1.rank
|
||||
var pseudoRank2: Int = p2.rank
|
||||
|
||||
pseudoRank1 = min(pseudoRank1, pairingParams.handicap.rankThreshold)
|
||||
pseudoRank2 = min(pseudoRank2, pairingParams.handicap.rankThreshold)
|
||||
pseudoRank1 = min(pseudoRank1, rankThreshold)
|
||||
pseudoRank2 = min(pseudoRank2, rankThreshold)
|
||||
hd = pseudoRank1 - pseudoRank2
|
||||
|
||||
return clampHandicap(hd)
|
||||
return clamp(hd)
|
||||
}
|
||||
|
||||
open fun clampHandicap(inputHd: Int): Int {
|
||||
var hd = inputHd
|
||||
if (hd > 0) {
|
||||
hd -= pairingParams.handicap.correction
|
||||
hd = min(hd, 0)
|
||||
}
|
||||
if (hd < 0) {
|
||||
hd += pairingParams.handicap.correction
|
||||
hd = max(hd, 0)
|
||||
}
|
||||
open fun HandicapParams.clamp(input: Int): Int {
|
||||
var hd = input
|
||||
if (hd >= correction) hd -= correction
|
||||
if (hd < 0) hd = max(hd + correction, 0)
|
||||
// Clamp handicap with ceiling
|
||||
hd = min(hd, pairingParams.handicap.ceiling)
|
||||
hd = max(hd, -pairingParams.handicap.ceiling)
|
||||
hd = min(hd, ceiling)
|
||||
hd = max(hd, -ceiling)
|
||||
|
||||
return hd
|
||||
}
|
||||
|
||||
open fun games(black: Pairable, white: Pairable): List<Game> {
|
||||
// CB TODO team of individuals pairing
|
||||
return listOf(Game(id = Store.nextGameId, black = black.id, white = white.id, handicap = handicap(black, white)))
|
||||
return listOf(Game(id = Store.nextGameId, black = black.id, white = white.id, handicap = pairing.handicap.handicap(black, white)))
|
||||
}
|
||||
|
||||
// Generic parameters calculation
|
||||
//private val standingScore by lazy { computeStandingScore() }
|
||||
|
||||
val historyHelper = if (pairables.first().let { it is TeamTournament.Team && it.teamOfIndividuals }) TeamOfIndividualsHistoryHelper(history, ::computeStandingScore)
|
||||
else HistoryHelper(history, ::computeStandingScore)
|
||||
|
||||
|
||||
|
||||
// Decide each pairable group based on the main criterion
|
||||
private val numberGroups by lazy {
|
||||
val (mainScoreMin, mainScoreMax) = mainCriterionMinMax()
|
||||
@@ -401,9 +372,5 @@ sealed class Solver(
|
||||
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]!!
|
||||
|
||||
|
||||
|
||||
|
||||
val Pairable.cums: Double get() = historyHelper.cumScore[id]!!
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ package org.jeudego.pairgoth.pairing
|
||||
import org.jeudego.pairgoth.model.*
|
||||
|
||||
class SwissSolver(round: Int,
|
||||
history: List<Game>,
|
||||
history: List<List<Game>>,
|
||||
pairables: List<Pairable>,
|
||||
pairingParams: PairingParams,
|
||||
placementParams: PlacementParams):
|
||||
@@ -19,7 +19,7 @@ class SwissSolver(round: Int,
|
||||
}
|
||||
|
||||
override fun computeStandingScore(): Map<ID, Double> {
|
||||
return historyHelper.numberWins
|
||||
return historyHelper.wins
|
||||
}
|
||||
|
||||
override fun getSpecificCriterionValue(p: Pairable, criterion: Criterion): Double {
|
||||
|
Reference in New Issue
Block a user