mirror of
https://github.com/TheRealOwenRees/echecsfrance.git
synced 2026-07-23 04:26:57 +00:00
Fix elo calculations
This commit is contained in:
@@ -9,26 +9,10 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { SelectField } from "@/components/form/SelectField";
|
import { SelectField } from "@/components/form/SelectField";
|
||||||
import { TextField } from "@/components/form/TextField";
|
import { TextField } from "@/components/form/TextField";
|
||||||
|
import { getNewRating } from "@/utils/eloCalculator";
|
||||||
|
|
||||||
import { FetchResultsForm } from "./FetchResultsForm";
|
import { FetchResultsForm } from "./FetchResultsForm";
|
||||||
|
|
||||||
const getNewRating = (
|
|
||||||
rating: number,
|
|
||||||
opponentRating: number,
|
|
||||||
result: 1 | 0 | 0.5,
|
|
||||||
kFactor: number,
|
|
||||||
) => {
|
|
||||||
const myChanceToWin = 1 / (1 + Math.pow(10, (opponentRating - rating) / 400));
|
|
||||||
const delta =
|
|
||||||
Math.round((kFactor * (result - myChanceToWin) + Number.EPSILON) * 100) /
|
|
||||||
100;
|
|
||||||
|
|
||||||
return {
|
|
||||||
delta,
|
|
||||||
newRating: Math.round((rating + delta + Number.EPSILON) * 100) / 100,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const resultsSchema = z.object({
|
const resultsSchema = z.object({
|
||||||
currentElo: z.number().int().positive(),
|
currentElo: z.number().int().positive(),
|
||||||
kFactor: z.enum(["40", "30", "20", "15", "10"]),
|
kFactor: z.enum(["40", "30", "20", "15", "10"]),
|
||||||
@@ -88,16 +72,15 @@ export default function Elo() {
|
|||||||
const result =
|
const result =
|
||||||
game?.result === "win" ? 1 : game?.result === "loss" ? 0 : 0.5;
|
game?.result === "win" ? 1 : game?.result === "loss" ? 0 : 0.5;
|
||||||
|
|
||||||
const { delta, newRating } = getNewRating(
|
const { delta } = getNewRating(
|
||||||
acc.rating,
|
currentElo!,
|
||||||
game.opponentElo!,
|
game.opponentElo!,
|
||||||
result,
|
result,
|
||||||
parseInt(kFactor!, 10),
|
parseInt(kFactor!, 10),
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rating: newRating,
|
rating: acc.rating + delta,
|
||||||
deltas: [...acc.deltas, { rating: newRating, delta }],
|
deltas: [...acc.deltas, { rating: acc.rating + delta, delta }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +199,8 @@ export default function Elo() {
|
|||||||
deltas[i].delta! < 0 && "text-error",
|
deltas[i].delta! < 0 && "text-error",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{deltas[i]!.delta! >= 0 ? "+" : ""} {deltas[i].delta!}
|
{deltas[i]!.delta! >= 0 ? "+" : ""}
|
||||||
|
{deltas[i].delta!}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import { clamp, last } from "lodash";
|
||||||
|
|
||||||
|
// Fide uses teh following table to establish the chance of winning based on the difference in elo.
|
||||||
|
// https://handbook.fide.com/chapter/B022017
|
||||||
|
|
||||||
|
const differenceToProbability: [
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
winChance: number,
|
||||||
|
][] = [
|
||||||
|
[0, 3, 0.5],
|
||||||
|
[4, 10, 0.51],
|
||||||
|
[11, 17, 0.52],
|
||||||
|
[18, 25, 0.53],
|
||||||
|
[26, 32, 0.54],
|
||||||
|
[33, 39, 0.55],
|
||||||
|
[40, 46, 0.56],
|
||||||
|
[47, 53, 0.57],
|
||||||
|
[54, 61, 0.58],
|
||||||
|
[62, 68, 0.59],
|
||||||
|
[69, 76, 0.6],
|
||||||
|
[77, 83, 0.61],
|
||||||
|
[84, 91, 0.62],
|
||||||
|
[92, 98, 0.63],
|
||||||
|
[99, 106, 0.64],
|
||||||
|
[107, 113, 0.65],
|
||||||
|
[114, 121, 0.66],
|
||||||
|
[122, 129, 0.67],
|
||||||
|
[130, 137, 0.68],
|
||||||
|
[138, 145, 0.69],
|
||||||
|
[146, 153, 0.7],
|
||||||
|
[154, 162, 0.71],
|
||||||
|
[163, 170, 0.72],
|
||||||
|
[171, 179, 0.73],
|
||||||
|
[180, 188, 0.74],
|
||||||
|
[189, 197, 0.75],
|
||||||
|
[198, 206, 0.76],
|
||||||
|
[207, 215, 0.77],
|
||||||
|
[216, 225, 0.78],
|
||||||
|
[226, 235, 0.79],
|
||||||
|
[236, 245, 0.8],
|
||||||
|
[246, 256, 0.81],
|
||||||
|
[257, 267, 0.82],
|
||||||
|
[268, 278, 0.83],
|
||||||
|
[279, 290, 0.84],
|
||||||
|
[291, 302, 0.85],
|
||||||
|
[303, 315, 0.86],
|
||||||
|
[316, 328, 0.87],
|
||||||
|
[329, 344, 0.88],
|
||||||
|
[345, 357, 0.89],
|
||||||
|
[358, 374, 0.9],
|
||||||
|
[375, 391, 0.91],
|
||||||
|
[392, 411, 0.92],
|
||||||
|
[412, 432, 0.93],
|
||||||
|
[433, 456, 0.94],
|
||||||
|
[457, 484, 0.95],
|
||||||
|
[485, 517, 0.96],
|
||||||
|
[518, 559, 0.97],
|
||||||
|
[560, 619, 0.98],
|
||||||
|
[620, 735, 0.99],
|
||||||
|
[736, 1000, 1],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getNewRating = (
|
||||||
|
rating: number,
|
||||||
|
opponentRating: number,
|
||||||
|
result: 1 | 0 | 0.5,
|
||||||
|
kFactor: number,
|
||||||
|
) => {
|
||||||
|
const differenceInElo = clamp(opponentRating - rating, -400, 400);
|
||||||
|
const tableRow = (differenceToProbability.find(
|
||||||
|
([start, end]) =>
|
||||||
|
Math.abs(differenceInElo) >= start && Math.abs(differenceInElo) <= end,
|
||||||
|
) ?? last(differenceToProbability)!)[2];
|
||||||
|
|
||||||
|
const probabilityOfWinning = differenceInElo < 0 ? tableRow : 1 - tableRow;
|
||||||
|
const delta =
|
||||||
|
Math.round(
|
||||||
|
kFactor * (result - probabilityOfWinning + Number.EPSILON) * 100,
|
||||||
|
) / 100;
|
||||||
|
|
||||||
|
return {
|
||||||
|
delta,
|
||||||
|
newRating: Math.round((rating + delta + Number.EPSILON) * 100) / 100,
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user