Add weight to decide color of pairables
This commit is contained in:
@@ -13,7 +13,7 @@ sealed class Pairable(val id: ID, val name: String, open val rating: Int, open v
|
|||||||
abstract fun toJson(): Json.Object
|
abstract fun toJson(): Json.Object
|
||||||
abstract val club: String?
|
abstract val club: String?
|
||||||
abstract val country: String?
|
abstract val country: String?
|
||||||
open fun nameSeed(): String {
|
open fun nameSeed(separator: String =" "): String {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
val skip = mutableSetOf<Int>() // skipped rounds
|
val skip = mutableSetOf<Int>() // skipped rounds
|
||||||
@@ -69,8 +69,8 @@ class Player(
|
|||||||
).also {
|
).also {
|
||||||
if (skip.isNotEmpty()) it["skip"] = Json.Array(skip)
|
if (skip.isNotEmpty()) it["skip"] = Json.Array(skip)
|
||||||
}
|
}
|
||||||
override fun nameSeed(): String {
|
override fun nameSeed(separator: String): String {
|
||||||
return name + " " + firstname
|
return name + separator + firstname
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,10 +16,10 @@ import kotlin.math.abs
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
private fun detRandom(max: Double, p1: Pairable, p2: Pairable): Double {
|
fun detRandom(max: Double, p1: Pairable, p2: Pairable): Double {
|
||||||
var inverse = false
|
var inverse = false
|
||||||
var name1 = p1.nameSeed()
|
var name1 = p1.nameSeed("")
|
||||||
var name2 = p2.nameSeed()
|
var name2 = p2.nameSeed("")
|
||||||
if (name1 > name2) {
|
if (name1 > name2) {
|
||||||
name1 = name2.also { name2 = name1 }
|
name1 = name2.also { name2 = name1 }
|
||||||
inverse = true
|
inverse = true
|
||||||
@@ -109,12 +109,16 @@ sealed class Solver(
|
|||||||
return p.rating - q.rating
|
return p.rating - q.rating
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun weight(p1: Pairable, p2: Pairable) =
|
open fun openGothaWeight(p1: Pairable, p2: Pairable) =
|
||||||
1.0 + // 1 is minimum value because 0 means "no matching allowed"
|
1.0 + // 1 is minimum value because 0 means "no matching allowed"
|
||||||
pairing.base.apply(p1, p2) +
|
pairing.base.apply(p1, p2) +
|
||||||
pairing.main.apply(p1, p2) +
|
pairing.main.apply(p1, p2) +
|
||||||
pairing.secondary.apply(p1, p2) +
|
pairing.secondary.apply(p1, p2) +
|
||||||
pairing.geo.apply(p1, p2)
|
pairing.geo.apply(p1, p2)
|
||||||
|
|
||||||
|
open fun weight(p1: Pairable, p2: Pairable) =
|
||||||
|
openGothaWeight(p1, p2) +
|
||||||
|
pairing.handicap.color(p1, p2)
|
||||||
|
|
||||||
// The main criterion that will be used to define the groups should be defined by subclasses
|
// The main criterion that will be used to define the groups should be defined by subclasses
|
||||||
val Pairable.main: Double get() = scores[id] ?: 0.0
|
val Pairable.main: Double get() = scores[id] ?: 0.0
|
||||||
@@ -133,11 +137,11 @@ sealed class Solver(
|
|||||||
if (round==1) File(WEIGHTS_FILE).writeText("Round 1\n")
|
if (round==1) File(WEIGHTS_FILE).writeText("Round 1\n")
|
||||||
else File(WEIGHTS_FILE).appendText("Round "+round.toString()+"\n")
|
else File(WEIGHTS_FILE).appendText("Round "+round.toString()+"\n")
|
||||||
File(WEIGHTS_FILE).appendText("Costs\n")
|
File(WEIGHTS_FILE).appendText("Costs\n")
|
||||||
println("placement criteria" + placement.criteria.toString())
|
// println("placement criteria" + placement.criteria.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in nameSortedPairables.indices) {
|
for (i in nameSortedPairables.indices) {
|
||||||
println(nameSortedPairables[i].nameSeed() + " id="+nameSortedPairables[i].id.toString()+" clasmt="+nameSortedPairables[i].placeInGroup.toString())
|
// println(nameSortedPairables[i].nameSeed() + " id="+nameSortedPairables[i].id.toString()+" clasmt="+nameSortedPairables[i].placeInGroup.toString())
|
||||||
for (j in i + 1 until pairables.size) {
|
for (j in i + 1 until pairables.size) {
|
||||||
val p = nameSortedPairables[i]
|
val p = nameSortedPairables[i]
|
||||||
val q = nameSortedPairables[j]
|
val q = nameSortedPairables[j]
|
||||||
@@ -156,7 +160,7 @@ sealed class Solver(
|
|||||||
File(WEIGHTS_FILE).appendText("mainSeedCost="+dec.format(pairing.main.applySeeding(p, q))+"\n")
|
File(WEIGHTS_FILE).appendText("mainSeedCost="+dec.format(pairing.main.applySeeding(p, q))+"\n")
|
||||||
File(WEIGHTS_FILE).appendText("secHandiCost="+dec.format(pairing.handicap.handicap(p, q))+"\n")
|
File(WEIGHTS_FILE).appendText("secHandiCost="+dec.format(pairing.handicap.handicap(p, q))+"\n")
|
||||||
File(WEIGHTS_FILE).appendText("secGeoCost="+dec.format(pairing.geo.apply(p, q))+"\n")
|
File(WEIGHTS_FILE).appendText("secGeoCost="+dec.format(pairing.geo.apply(p, q))+"\n")
|
||||||
File(WEIGHTS_FILE).appendText("totalCost="+dec.format(weight(p,q))+"\n")
|
File(WEIGHTS_FILE).appendText("totalCost="+dec.format(openGothaWeight(p,q))+"\n")
|
||||||
|
|
||||||
logWeights("total", p, q, weight(p,q))
|
logWeights("total", p, q, weight(p,q))
|
||||||
}
|
}
|
||||||
@@ -172,6 +176,7 @@ sealed class Solver(
|
|||||||
listOf(graph.getEdgeSource(it), graph.getEdgeTarget(it))
|
listOf(graph.getEdgeSource(it), graph.getEdgeTarget(it))
|
||||||
}.sortedBy { gamesSort(it[0],it[1])}
|
}.sortedBy { gamesSort(it[0],it[1])}
|
||||||
|
|
||||||
|
|
||||||
var result = sorted.flatMap { games(white = it[0], black = it[1]) }
|
var result = sorted.flatMap { games(white = it[0], black = it[1]) }
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -424,6 +429,25 @@ sealed class Solver(
|
|||||||
return hd
|
return hd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun HandicapParams.color(p1: Pairable, p2: Pairable): Double {
|
||||||
|
var score = 0.0
|
||||||
|
val hd = pairing.handicap.handicap(p1,p2)
|
||||||
|
if(hd==0){
|
||||||
|
if (p1.colorBalance > p2.colorBalance) {
|
||||||
|
score = 1.0
|
||||||
|
} else if (p1.colorBalance < p2.colorBalance) {
|
||||||
|
score = -1.0
|
||||||
|
} else { // choose color from a det random
|
||||||
|
if (detRandom(1.0, p1, p2) === 0.0) {
|
||||||
|
score = 1.0
|
||||||
|
} else {
|
||||||
|
score = -1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
open fun games(black: Pairable, white: Pairable): List<Game> {
|
open fun games(black: Pairable, white: Pairable): List<Game> {
|
||||||
// CB TODO team of individuals pairing
|
// CB TODO team of individuals pairing
|
||||||
return listOf(Game(id = Store.nextGameId, black = black.id, white = white.id, handicap = pairing.handicap.handicap(black, white)))
|
return listOf(Game(id = Store.nextGameId, black = black.id, white = white.id, handicap = pairing.handicap.handicap(black, white)))
|
||||||
|
@@ -315,7 +315,6 @@ class BasicTests: TestBase() {
|
|||||||
assertNotNull(id_np)
|
assertNotNull(id_np)
|
||||||
val tournament_np = TestAPI.get("/api/tour/$id_np").asObject()
|
val tournament_np = TestAPI.get("/api/tour/$id_np").asObject()
|
||||||
logger.info(tournament_np.toString().slice(0..50) + "...")
|
logger.info(tournament_np.toString().slice(0..50) + "...")
|
||||||
logger.info(tournament_np.toString())
|
|
||||||
val players_np = TestAPI.get("/api/tour/$id_np/part").asArray()
|
val players_np = TestAPI.get("/api/tour/$id_np/part").asArray()
|
||||||
logger.info(players_np.toString().slice(0..50) + "...")
|
logger.info(players_np.toString().slice(0..50) + "...")
|
||||||
var games_np = TestAPI.post("/api/tour/$id_np/pair/1", Json.Array("all")).asArray()
|
var games_np = TestAPI.post("/api/tour/$id_np/pair/1", Json.Array("all")).asArray()
|
||||||
@@ -332,10 +331,11 @@ class BasicTests: TestBase() {
|
|||||||
val pairings_R4 = """[{"id":891,"w":506,"b":528,"h":0,"r":"?","dd":0},{"id":892,"w":517,"b":530,"h":0,"r":"?","dd":0},{"id":893,"w":518,"b":512,"h":0,"r":"?","dd":0},{"id":894,"w":511,"b":519,"h":0,"r":"?","dd":0},{"id":895,"w":508,"b":504,"h":0,"r":"?","dd":0},{"id":896,"w":533,"b":514,"h":0,"r":"?","dd":0},{"id":897,"w":529,"b":502,"h":0,"r":"?","dd":0},{"id":898,"w":520,"b":509,"h":0,"r":"?","dd":0},{"id":899,"w":531,"b":516,"h":0,"r":"?","dd":0},{"id":900,"w":507,"b":503,"h":0,"r":"?","dd":0},{"id":901,"w":510,"b":505,"h":0,"r":"?","dd":0},{"id":902,"w":523,"b":524,"h":0,"r":"?","dd":0},{"id":903,"w":532,"b":526,"h":0,"r":"?","dd":0},{"id":904,"w":515,"b":525,"h":0,"r":"?","dd":0},{"id":905,"w":522,"b":527,"h":0,"r":"?","dd":0},{"id":906,"w":513,"b":521,"h":0,"r":"?","dd":0}]"""
|
val pairings_R4 = """[{"id":891,"w":506,"b":528,"h":0,"r":"?","dd":0},{"id":892,"w":517,"b":530,"h":0,"r":"?","dd":0},{"id":893,"w":518,"b":512,"h":0,"r":"?","dd":0},{"id":894,"w":511,"b":519,"h":0,"r":"?","dd":0},{"id":895,"w":508,"b":504,"h":0,"r":"?","dd":0},{"id":896,"w":533,"b":514,"h":0,"r":"?","dd":0},{"id":897,"w":529,"b":502,"h":0,"r":"?","dd":0},{"id":898,"w":520,"b":509,"h":0,"r":"?","dd":0},{"id":899,"w":531,"b":516,"h":0,"r":"?","dd":0},{"id":900,"w":507,"b":503,"h":0,"r":"?","dd":0},{"id":901,"w":510,"b":505,"h":0,"r":"?","dd":0},{"id":902,"w":523,"b":524,"h":0,"r":"?","dd":0},{"id":903,"w":532,"b":526,"h":0,"r":"?","dd":0},{"id":904,"w":515,"b":525,"h":0,"r":"?","dd":0},{"id":905,"w":522,"b":527,"h":0,"r":"?","dd":0},{"id":906,"w":513,"b":521,"h":0,"r":"?","dd":0}]"""
|
||||||
val pairings_R5 = """[{"id":907,"w":528,"b":530,"h":0,"r":"?","dd":0},{"id":908,"w":512,"b":517,"h":0,"r":"?","dd":0},{"id":909,"w":504,"b":519,"h":0,"r":"?","dd":0},{"id":910,"w":514,"b":509,"h":0,"r":"?","dd":0},{"id":911,"w":506,"b":502,"h":0,"r":"?","dd":0},{"id":912,"w":516,"b":518,"h":0,"r":"?","dd":0},{"id":913,"w":511,"b":505,"h":0,"r":"?","dd":0},{"id":914,"w":520,"b":526,"h":0,"r":"?","dd":0},{"id":915,"w":525,"b":533,"h":0,"r":"?","dd":0},{"id":916,"w":524,"b":508,"h":0,"r":"?","dd":0},{"id":917,"w":503,"b":529,"h":0,"r":"?","dd":0},{"id":918,"w":531,"b":532,"h":0,"r":"?","dd":0},{"id":919,"w":527,"b":510,"h":0,"r":"?","dd":0},{"id":920,"w":523,"b":515,"h":0,"r":"?","dd":0},{"id":921,"w":507,"b":521,"h":0,"r":"?","dd":0},{"id":922,"w":513,"b":522,"h":0,"r":"?","dd":0}]"""
|
val pairings_R5 = """[{"id":907,"w":528,"b":530,"h":0,"r":"?","dd":0},{"id":908,"w":512,"b":517,"h":0,"r":"?","dd":0},{"id":909,"w":504,"b":519,"h":0,"r":"?","dd":0},{"id":910,"w":514,"b":509,"h":0,"r":"?","dd":0},{"id":911,"w":506,"b":502,"h":0,"r":"?","dd":0},{"id":912,"w":516,"b":518,"h":0,"r":"?","dd":0},{"id":913,"w":511,"b":505,"h":0,"r":"?","dd":0},{"id":914,"w":520,"b":526,"h":0,"r":"?","dd":0},{"id":915,"w":525,"b":533,"h":0,"r":"?","dd":0},{"id":916,"w":524,"b":508,"h":0,"r":"?","dd":0},{"id":917,"w":503,"b":529,"h":0,"r":"?","dd":0},{"id":918,"w":531,"b":532,"h":0,"r":"?","dd":0},{"id":919,"w":527,"b":510,"h":0,"r":"?","dd":0},{"id":920,"w":523,"b":515,"h":0,"r":"?","dd":0},{"id":921,"w":507,"b":521,"h":0,"r":"?","dd":0},{"id":922,"w":513,"b":522,"h":0,"r":"?","dd":0}]"""
|
||||||
|
|
||||||
logger.info(compare_string(pairings_R1, games_np.toString()))
|
//logger.info(compare_string(pairings_R1, games_np.toString()))
|
||||||
// val games = TestAPI.get("/api/tour/$id/res/1").asArray()
|
// val games = TestAPI.get("/api/tour/$id/res/1").asArray()
|
||||||
|
//logger.info("Compare pairings for round 1")
|
||||||
assertEquals(pairings_R1, games_np.toString(), "pairings for round 1 differ")
|
assertEquals(pairings_R1, games_np.toString(), "pairings for round 1 differ")
|
||||||
|
logger.info("Pairings for round 1 match OpenGotha")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
43
api-webapp/src/test/kotlin/UnitaryTests.kt
Normal file
43
api-webapp/src/test/kotlin/UnitaryTests.kt
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package org.jeudego.pairgoth.test
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
|
class UnitaryTests: TestBase() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `001 test detRandom`() {
|
||||||
|
|
||||||
|
fun detRandomCopy(p1:String, p2:String):Double{
|
||||||
|
var name1 = p1
|
||||||
|
var name2 = p2
|
||||||
|
if (name1 > name2) {
|
||||||
|
name1 = name2.also { name2 = name1 }
|
||||||
|
}
|
||||||
|
val s = "$name1$name2"
|
||||||
|
var nR = s.mapIndexed { i, c ->
|
||||||
|
c.code.toDouble() * (i + 1)
|
||||||
|
}.sum()
|
||||||
|
/* logger.info("nR = "+nR.toString())
|
||||||
|
var i = 0
|
||||||
|
nR = 0.0
|
||||||
|
for (i in 0..s.length-1) {
|
||||||
|
nR += s[i].code.toDouble()*(i+1)
|
||||||
|
logger.info(i.toString()+" "+s[i]+" "+nR)
|
||||||
|
}
|
||||||
|
logger.info("nR for string "+"$name1$name2"+" "+nR.toString())*/
|
||||||
|
return nR
|
||||||
|
}
|
||||||
|
|
||||||
|
var name1 = "MizessynFrançois"
|
||||||
|
var name2 = "BonisMichel"
|
||||||
|
|
||||||
|
assertEquals(42923.0, detRandomCopy(name1, name2))
|
||||||
|
|
||||||
|
/* nR = nR * 1234567 % (max + 1)
|
||||||
|
if (inverse) nR = max - nR
|
||||||
|
assertEquals(1.0, nR)*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user