-
-
Notifications
You must be signed in to change notification settings - Fork 17
/
rating.ts
89 lines (75 loc) · 2.41 KB
/
rating.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
* ArtistAssistApp
* Copyright (C) 2023-2024 Eugene Khyst
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const SCALING_FACTOR = 32;
export type Score = [number, number];
export class Player<T = any> {
constructor(
public data: T,
public id = 0,
public rating = 1500
) {}
}
const comparePlayersByRating = ({rating: a}: Player, {rating: b}: Player) => a - b;
function probability({rating: rating1}: Player, {rating: rating2}: Player) {
return 1.0 / (1.0 + Math.pow(10, (rating1 - rating2) / 400));
}
function game(player1: Player, player2: Player, [scoreA, scoreB]: Score): void {
const probabilityB = probability(player1, player2);
const probabilityA = probability(player2, player1);
player1.rating = player1.rating + SCALING_FACTOR * (scoreA - probabilityA);
player2.rating = player2.rating + SCALING_FACTOR * (scoreB - probabilityB);
}
export class Game<T> {
finished = false;
constructor(
public player1: Player<T>,
public player2: Player<T>
) {}
setScore(score: Score): void {
if (this.finished) {
return;
}
this.finished = true;
game(this.player1, this.player2, score);
}
}
export class Tournament<T> {
players: Player<T>[] = [];
games: Game<T>[] = [];
private nextId = 0;
constructor(players: Player<T>[] = []) {
for (const player of players) {
this.addPlayer(player);
}
}
addPlayer(player: Player<T>) {
player.id = this.nextId++;
this.players.push(player);
for (const player1 of this.players) {
if (player1 !== player) {
this.games.push(new Game<T>(player1, player));
}
}
}
getPlayersByRating(): Player<T>[] {
return [...this.players].sort(comparePlayersByRating).reverse();
}
getUnfinishedGames(): Game<T>[] {
return this.games.filter(({finished}) => !finished);
}
}