-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
rating-calculator.ts
94 lines (79 loc) · 2.33 KB
/
rating-calculator.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
90
91
92
93
94
import type { RatingUsers } from "./rating-user";
import { RatingUser } from "./rating-user";
// https://www.wikiwand.com/en/Elo_rating_system
export class RatingCalculator {
users: RatingUsers;
constructor() {
this.users = [];
}
calcP(userA: RatingUser, userB: RatingUser) {
return 1.0 / (1.0 + 10 ** ((userB.oldRating - userA.oldRating) / 400.0));
}
getExSeed(users: RatingUsers, rating: number, ownUser: RatingUser) {
const exUser = new RatingUser();
let res = 0;
users.forEach((user) => {
if (user.id !== ownUser.id) {
res += this.calcP(user, exUser);
}
});
return res;
}
calcRating(users: RatingUsers, rank: number, user: RatingUser) {
let left = 1;
let right = 8000;
while (right - left > 1) {
const mid = Math.floor((left + right) / 2);
if (this.getExSeed(users, mid, user) < rank) {
right = mid;
} else {
left = mid;
}
}
return left;
}
calculate() {
// Calculate seed
for (let i = 0; i < this.users.length; i++) {
const u = this.users[i];
u.seed = 1.0;
for (let j = 0; j < this.users.length; j++) {
if (i !== j) {
const otherUser = this.users[j];
u.seed += this.calcP(otherUser, u);
}
}
}
// Calculate initial delta and sumDelta
let sumDelta = 0;
for (let i = 0; i < this.users.length; i++) {
const u = this.users[i];
u.delta = Math.floor(
(this.calcRating(this.users, Math.sqrt(u.rank * u.seed), u)
- u.oldRating)
/ 2,
);
sumDelta += u.delta;
}
// Calculate first inc
let inc = Math.floor(-sumDelta / this.users.length) - 1;
for (let i = 0; i < this.users.length; i++) {
const u = this.users[i];
u.delta += inc;
}
// Calculate second inc
this.users = this.users.sort((a, b) => b.oldRating - a.oldRating);
const s = Math.min(this.users.length, Math.floor(4 * Math.round(Math.sqrt(this.users.length))));
let sumS = 0;
for (let i = 0; i < s; i++) {
sumS += this.users[i].delta;
}
inc = Math.min(Math.max(Math.floor(-sumS / s), -10), 0);
// Calculate new rating
this.users.forEach((u) => {
u.delta += inc;
u.newRating = u.oldRating + u.delta;
});
this.users = this.users.sort((a, b) => a.rank - b.rank);
}
}