Swiss ready to be tested
This commit is contained in:
@@ -12,9 +12,7 @@ import java.util.Random
|
|||||||
// TODO - this is only an early draft
|
// TODO - this is only an early draft
|
||||||
|
|
||||||
sealed class Pairing(val type: PairingType) {
|
sealed class Pairing(val type: PairingType) {
|
||||||
companion object {
|
companion object {}
|
||||||
val rand = Random(/* seed from properties - TODO */)
|
|
||||||
}
|
|
||||||
enum class PairingType { SWISS, MACMAHON, ROUNDROBIN }
|
enum class PairingType { SWISS, MACMAHON, ROUNDROBIN }
|
||||||
|
|
||||||
abstract fun pair(tournament: Tournament, round: Int, pairables: List<Pairable>): List<Game>
|
abstract fun pair(tournament: Tournament, round: Int, pairables: List<Pairable>): List<Game>
|
||||||
@@ -30,7 +28,7 @@ class Swiss(
|
|||||||
val history =
|
val history =
|
||||||
if (tournament.games.isEmpty()) emptyList()
|
if (tournament.games.isEmpty()) emptyList()
|
||||||
else tournament.games.slice(0 until round).flatMap { it.values }
|
else tournament.games.slice(0 until round).flatMap { it.values }
|
||||||
return SwissSolver(history, actualMethod).pair(pairables)
|
return SwissSolver(history, pairables, actualMethod).pair()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,19 +9,23 @@ import org.jgrapht.alg.matching.blossom.v5.ObjectiveSense
|
|||||||
import org.jgrapht.graph.DefaultWeightedEdge
|
import org.jgrapht.graph.DefaultWeightedEdge
|
||||||
import org.jgrapht.graph.SimpleWeightedGraph
|
import org.jgrapht.graph.SimpleWeightedGraph
|
||||||
import org.jgrapht.graph.builder.GraphBuilder
|
import org.jgrapht.graph.builder.GraphBuilder
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
sealed class Solver(private val history: List<Game>) {
|
sealed class Solver(protected val history: List<Game>, protected val pairables: List<Pairable>) {
|
||||||
|
|
||||||
|
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 = 0 // no sort by default
|
||||||
abstract fun weight(p: Pairable, q: Pairable): Double
|
abstract fun weight(p: Pairable, q: Pairable): Double
|
||||||
|
|
||||||
fun pair(pairables: List<Pairable>): List<Game> {
|
fun pair(): List<Game> {
|
||||||
// check that at this stage, we have an even number of pairables
|
// 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")
|
if (pairables.size % 2 != 0) throw Error("expecting an even number of pairables")
|
||||||
val sorted = pairables.sortedWith(::sort)
|
|
||||||
val builder = GraphBuilder(SimpleWeightedGraph<Pairable, DefaultWeightedEdge>(DefaultWeightedEdge::class.java))
|
val builder = GraphBuilder(SimpleWeightedGraph<Pairable, DefaultWeightedEdge>(DefaultWeightedEdge::class.java))
|
||||||
for (i in sorted.indices) {
|
for (i in sortedPairables.indices) {
|
||||||
for (j in i + 1 until sorted.size) {
|
for (j in i + 1 until n) {
|
||||||
val p = pairables[i]
|
val p = pairables[i]
|
||||||
val q = pairables[j]
|
val q = pairables[j]
|
||||||
builder.addEdge(p, q, weight(p, q))
|
builder.addEdge(p, q, weight(p, q))
|
||||||
@@ -38,6 +42,35 @@ sealed class Solver(private val history: List<Game>) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculation parameters
|
||||||
|
|
||||||
|
val n = pairables.size
|
||||||
|
|
||||||
|
// pairables sorted using overloadable sort function
|
||||||
|
private val sortedPairables by lazy {
|
||||||
|
pairables.sortedWith(::sort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// place (among sorted pairables)
|
||||||
|
val Pairable.place: Int get() = _place[id]!!
|
||||||
|
private val _place by lazy {
|
||||||
|
sortedPairables.mapIndexed { index, pairable ->
|
||||||
|
Pair(pairable.id, index)
|
||||||
|
}.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// placeInGroup (of same score) : Pair(place, groupSize)
|
||||||
|
val Pairable.placeInGroup: Pair<Int, Int> get() = _placeInGroup[id]!!
|
||||||
|
private val _placeInGroup by lazy {
|
||||||
|
sortedPairables.groupBy {
|
||||||
|
it.score
|
||||||
|
}.values.flatMap { group ->
|
||||||
|
group.mapIndexed { index, pairable ->
|
||||||
|
Pair(pairable.id, Pair(index, group.size))
|
||||||
|
}
|
||||||
|
}.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
// already paired players map
|
// already paired players map
|
||||||
fun Pairable.played(other: Pairable) = _paired.contains(Pair(id, other.id))
|
fun Pairable.played(other: Pairable) = _paired.contains(Pair(id, other.id))
|
||||||
private val _paired: Set<Pair<Int, Int>> by lazy {
|
private val _paired: Set<Pair<Int, Int>> by lazy {
|
||||||
|
@@ -3,9 +3,14 @@ package org.jeudego.pairgoth.pairing
|
|||||||
import org.jeudego.pairgoth.model.Game
|
import org.jeudego.pairgoth.model.Game
|
||||||
import org.jeudego.pairgoth.model.Pairable
|
import org.jeudego.pairgoth.model.Pairable
|
||||||
import org.jeudego.pairgoth.model.Swiss
|
import org.jeudego.pairgoth.model.Swiss
|
||||||
|
import org.jeudego.pairgoth.model.Swiss.Method.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
class SwissSolver(history: List<Game>, method: Swiss.Method): Solver(history) {
|
class SwissSolver(history: List<Game>, pairables: List<Pairable>, val method: Swiss.Method): Solver(history, pairables) {
|
||||||
|
|
||||||
|
val PLAYED_WEIGHT = 1_000_000.0 // weight if players already met
|
||||||
|
val SCORE_WEIGHT = 10_000.0 // weight per difference of score
|
||||||
|
val PLACE_WEIGHT = 1_000.0 // weight per difference of place
|
||||||
|
|
||||||
override fun sort(p: Pairable, q: Pairable): Int =
|
override fun sort(p: Pairable, q: Pairable): Int =
|
||||||
when (p.score) {
|
when (p.score) {
|
||||||
@@ -14,8 +19,20 @@ class SwissSolver(history: List<Game>, method: Swiss.Method): Solver(history) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun weight(p: Pairable, q: Pairable) = when {
|
override fun weight(p: Pairable, q: Pairable) = when {
|
||||||
p.played(q) -> 100_000.0
|
p.played(q) -> PLAYED_WEIGHT
|
||||||
p.score != q.score -> abs(p.score - q.score) * 10_000.0
|
p.score != q.score -> {
|
||||||
else -> abs(p.rating - q.rating) * 10.0
|
val placeWeight =
|
||||||
|
if (p.score > q.score) (p.placeInGroup.second + q.placeInGroup.first) * PLACE_WEIGHT
|
||||||
|
else (q.placeInGroup.second + p.placeInGroup.first) * PLACE_WEIGHT
|
||||||
|
abs(p.score - q.score) * SCORE_WEIGHT + placeWeight
|
||||||
|
}
|
||||||
|
else -> when (method) {
|
||||||
|
SPLIT_AND_FOLD ->
|
||||||
|
if (p.placeInGroup.first > q.placeInGroup.first) abs(p.placeInGroup.first - (q.placeInGroup.second - q.placeInGroup.first)) * PLACE_WEIGHT
|
||||||
|
else abs(q.placeInGroup.first - (p.placeInGroup.second - p.placeInGroup.first)) * PLACE_WEIGHT
|
||||||
|
SPLIT_AND_RANDOM -> rand.nextDouble(p.placeInGroup.second.toDouble()) * PLACE_WEIGHT
|
||||||
|
SPLIT_AND_SLIP -> abs(abs(p.placeInGroup.first - q.placeInGroup.first) - p.placeInGroup.second) * PLACE_WEIGHT
|
||||||
|
else -> throw Error("unhandled case")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user