From abd9fdf8370cc9a98f6c0033f24c5853eae31b8f Mon Sep 17 00:00:00 2001 From: Claude Brisson Date: Sat, 23 Dec 2023 02:56:25 +0100 Subject: [PATCH] Handicap wrongly chosen in MM --- .../org/jeudego/pairgoth/model/Pairing.kt | 49 ++++++++++++------- .../pairgoth/pairing/solver/BaseSolver.kt | 12 ++--- api-webapp/src/test/kotlin/BasicTests.kt | 47 ++++++++++++++++-- 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairing.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairing.kt index 4de0168..a28d772 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairing.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairing.kt @@ -97,11 +97,17 @@ data class HandicapParams( val ceiling: Int = 9, // Possible values are between 0 and 9 ) { companion object { - val default = HandicapParams( - weight = 0.0, // default disables handicap + val swissDefault = HandicapParams( + weight = 0.0, // TODO 'default disables handicap' => seems wrong, not used useMMS = false, rankThreshold = -30, // 30k ceiling = 0) + val mmDefault = HandicapParams( + weight = 0.0, + useMMS = true, + rankThreshold = 0, // 1D + ceiling = 9) + fun default(type: PairingType) = if (type == MAC_MAHON) mmDefault else swissDefault } } @@ -154,7 +160,7 @@ class Swiss( defSecCrit = MainCritParams.MAX_CATEGORIES_WEIGHT ), geo = GeographicalParams.disabled, - handicap = HandicapParams.default + handicap = HandicapParams.default(SWISS) ), placementParams: PlacementParams = PlacementParams( Criterion.NBW, Criterion.SOSW, Criterion.SOSOSW @@ -176,7 +182,12 @@ class MacMahon( geo = GeographicalParams( avoidSameGeo = MainCritParams.MAX_SCORE_WEIGHT ), - handicap = HandicapParams() + handicap = HandicapParams( + weight = MainCritParams.MAX_SCORE_WEIGHT, // TODO - contradictory with the comment above (not used anyway ?!) + useMMS = true, + rankThreshold = 0, + ceiling = 9 + ) ), placementParams: PlacementParams = PlacementParams( Criterion.NBW, Criterion.SOSW, Criterion.SOSOSW @@ -247,16 +258,16 @@ fun MainCritParams.toJson() = Json.Object( ) fun SecondaryCritParams.Companion.fromJson(json: Json.Object) = SecondaryCritParams( - barThresholdActive = json.getBoolean("barTreshold") ?: default.barThresholdActive, - rankThreshold = json.getInt("rankTreshold") ?: default.rankThreshold, - nbWinsThresholdActive = json.getBoolean("winsTreshold") ?: default.nbWinsThresholdActive, + barThresholdActive = json.getBoolean("barThreshold") ?: default.barThresholdActive, + rankThreshold = json.getInt("rankThreshold") ?: default.rankThreshold, + nbWinsThresholdActive = json.getBoolean("winsThreshold") ?: default.nbWinsThresholdActive, defSecCrit = json.getDouble("secWeight") ?: default.defSecCrit ) fun SecondaryCritParams.toJson() = Json.Object( - "barTreshold" to barThresholdActive, - "rankTreshold" to rankThreshold, - "winsTreshold" to nbWinsThresholdActive, + "barThreshold" to barThresholdActive, + "rankThreshold" to rankThreshold, + "winsThreshold" to nbWinsThresholdActive, "secWeight" to defSecCrit ) @@ -274,18 +285,18 @@ fun GeographicalParams.toJson() = Json.Object( "mmsDiffClub" to preferMMSDiffRatherThanSameClub ) -fun HandicapParams.Companion.fromJson(json: Json.Object) = HandicapParams( - weight = json.getDouble("weight") ?: default.weight, - useMMS = json.getBoolean("useMMS") ?: default.useMMS, - rankThreshold = json.getInt("treshold") ?: default.rankThreshold, - correction = json.getInt("correction") ?: default.correction, - ceiling = json.getInt("ceiling") ?: default.ceiling +fun HandicapParams.Companion.fromJson(json: Json.Object, type: PairingType) = HandicapParams( + weight = json.getDouble("weight") ?: default(type).weight, + useMMS = json.getBoolean("useMMS") ?: default(type).useMMS, + rankThreshold = json.getInt("threshold") ?: default(type).rankThreshold, + correction = json.getInt("correction") ?: default(type).correction, + ceiling = json.getInt("ceiling") ?: default(type).ceiling ) fun HandicapParams.toJson() = Json.Object( "weight" to weight, "useMMS" to useMMS, - "treshold" to rankThreshold, + "threshold" to rankThreshold, "correction" to correction, "ceiling" to ceiling ) @@ -302,7 +313,7 @@ fun Pairing.Companion.fromJson(json: Json.Object): Pairing { 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 - val hd = json.getObject("handicap")?.let { HandicapParams.fromJson(it) } ?: defaultParams.pairingParams.handicap + val hd = json.getObject("handicap")?.let { HandicapParams.fromJson(it, type) } ?: defaultParams.pairingParams.handicap val pairingParams = PairingParams(base, main, secondary, geo, hd) val placementParams = json.getArray("placement")?.let { PlacementParams.fromJson(it) } ?: defaultParams.placementParams return when (type) { @@ -319,7 +330,7 @@ fun Pairing.toJson(): Json.Object = Json.MutableObject( "type" to type.name, "base" to pairingParams.base.toJson(), "main" to pairingParams.main.toJson(), - "secondary" to pairingParams.main.toJson(), + "secondary" to pairingParams.secondary.toJson(), "geo" to pairingParams.geo.toJson(), "handicap" to pairingParams.handicap.toJson(), "placement" to placementParams.toJson() diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/solver/BaseSolver.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/solver/BaseSolver.kt index a4b9528..ccb759b 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/solver/BaseSolver.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/solver/BaseSolver.kt @@ -486,14 +486,14 @@ sealed class BaseSolver( // Handicap functions // Has to be overridden if handicap is not based on rank - open fun HandicapParams.handicap(p1: Pairable, p2: Pairable): Int { + open fun HandicapParams.handicap(white: Pairable, black: Pairable): Int { var hd = 0 - var pseudoRank1: Int = p1.rank - var pseudoRank2: Int = p2.rank + var pseudoRankWhite: Int = white.rank + var pseudoRankBlack: Int = black.rank - pseudoRank1 = min(pseudoRank1, rankThreshold) - pseudoRank2 = min(pseudoRank2, rankThreshold) - hd = pseudoRank1 - pseudoRank2 + pseudoRankWhite = min(pseudoRankWhite, rankThreshold) + pseudoRankBlack = min(pseudoRankBlack, rankThreshold) + hd = pseudoRankWhite - pseudoRankBlack return clamp(hd) } diff --git a/api-webapp/src/test/kotlin/BasicTests.kt b/api-webapp/src/test/kotlin/BasicTests.kt index e51300b..2069120 100644 --- a/api-webapp/src/test/kotlin/BasicTests.kt +++ b/api-webapp/src/test/kotlin/BasicTests.kt @@ -81,7 +81,7 @@ class BasicTests: TestBase() { val aPlayer = Json.Object( "name" to "Burma", "firstname" to "Nestor", - "rating" to 1600, + "rating" to -500, "rank" to -5, "country" to "FR", "club" to "13Ma" @@ -90,12 +90,36 @@ class BasicTests: TestBase() { val anotherPlayer = Json.Object( "name" to "Poirot", "firstname" to "Hercule", - "rating" to 1700, + "rating" to -100, "rank" to -1, "country" to "FR", "club" to "75Op" ) + val aMMTournament = Json.Object( + "type" to "INDIVIDUAL", + "name" to "Mon Tournoi", + "shortName" to "mon-tournoi", + "startDate" to "2023-05-10", + "endDate" to "2023-05-12", + "country" to "FR", + "location" to "Marseille", + "online" to false, + "timeSystem" to Json.Object( + "type" to "FISCHER", + "mainTime" to 1200, + "increment" to 10 + ), + "rounds" to 2, + "pairing" to Json.Object( + "type" to "MAC_MAHON", + "handicap" to Json.Object( + "correction" to 1 + ) + ) + ) + + var aTournamentID: ID? = null var aTeamTournamentID: ID? = null var aPlayerID: ID? = null @@ -174,7 +198,21 @@ class BasicTests: TestBase() { } @Test - fun `007 team tournament, MacMahon`() { + fun `007 Mac Mahon handicap`() { + var resp = TestAPI.post("/api/tour", aMMTournament).asObject() + val tourId = resp.getInt("id") ?: throw Error("tournament creation failed") + resp = TestAPI.post("/api/tour/$tourId/part", aPlayer).asObject().also { assertTrue(it.getBoolean("success")!!) } + val p1 = resp.getInt("id")!! + resp = TestAPI.post("/api/tour/$tourId/part", anotherPlayer).asObject().also { assertTrue(it.getBoolean("success")!!) } + val p2 = resp.getInt("id")!! + val game = TestAPI.post("/api/tour/$tourId/pair/1", Json.Array("all")).asArray().getObject(0) ?: throw Error("pairing failed") + assertEquals(p2, game.getInt("w")) + assertEquals(p1, game.getInt("b")) + assertEquals(3, game.getInt("h")) + } + + @Test + fun `008 team tournament, MacMahon`() { var resp = TestAPI.post("/api/tour", aTeamTournament).asObject() assertTrue(resp.getBoolean("success") == true, "expecting success") aTeamTournamentID = resp.getInt("id") @@ -204,5 +242,4 @@ class BasicTests: TestBase() { // TODO check pairing // val expected = """"["id":1,"w":5,"b":6,"h":3,"r":"?"]""" } - -} \ No newline at end of file +}