forked from JSpeedie/G2ME-legacy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathglicko2.c
251 lines (218 loc) · 8.83 KB
/
glicko2.c
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/*
Copyright (c) 2009 Ryan Kirkman
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
typedef struct player {
/* The system constant, which constrains the change in
* volatility over time. */
double _tau;
double __rating;
double __rd;
double vol;
}Player;
/* Function prototypes because this is not python */
double _delta(struct player*, double*, int, double*, double*, double);
double _E(struct player*, double, double);
double _g(double);
double _newVol(struct player*, double*, int, double*, double*, double);
double _v(struct player*, double*, int, double*);
/** Returns the value of a given struct player's rating (known in the
* glicko2 formulas as μ (mu))
*
* \param '*P' the struct player whose rating will be returned
* \return double representing '*P's rating
*/
double getRating(struct player* P) {
return (P->__rating * 173.7178) + 1500;
}
/** Sets the given struct player's rating to the given value
*
* \param '*P' the struct player whose rating will be changed
* \param 'rating' the value to set '*P's rating to
* \return modifies the struct player '*P', returns void
*/
void setRating(struct player* P, double rating) {
P->__rating = (rating - 1500) / 173.7178;
}
/** Returns the value of a given struct player's rating deviation (known
* in the glicko2 formulas as Φ (phi))
*
* \param '*P' the struct player whose RD will be returned
* \return double representing '*P's rating deviation
*/
double getRd(struct player* P) {
return P->__rd * 173.7178;
}
/** Sets the given struct player's rating deviation to the given value
*
* \param '*P' the struct player whose RD will be changed
* \param 'rd' the value to set '*P's rating deviation to
* \return modifies the struct player '*P', returns void
*/
void setRd(struct player* P, double rd) {
P->__rd = rd / 173.7178;
}
/** Calculates and updates a struct player's rating deviation for the
* beginning of a rating period.
*
* \param '*P' a struct player representing the player whose RD is to be changed
* \return modifies struct player '*P', returns void
*/
void _preRatingRD(struct player* P) {
P->__rd = sqrt(pow(P->__rd, 2) + pow(P->vol, 2));
}
/** Updates a sturct player's rating assuming that player undergoes
* 'rating_list_size' games with the given outcomes and given opponent ratings
* and RDs.
*
* \param '*P' a struct player representing the player whose rating is to be changed
* \param '*rating_list' an array of doubles containing (in order) all the ratings of
* the opponents '*P' played
* \param 'rating_list_size' the size of the '*rating_list' array
* \param '*RD_list' an array od doubles containing (in order) the RDs of the opponents
* '*P' played
* \param '*outcome_list' an array of doubles containing (in order) the outcomes
* of the games '*P' played. 1 for '*P' winning, 0.5 for a draw and 0
* for a '*P' loss
* \return modifies the given struct player '*P', returns void
*/
void update_player(struct player *P, \
double *rating_list, int rating_list_size, \
double *RD_list, double *outcome_list) {
double v = _v(P, rating_list, rating_list_size, RD_list);
P->vol = _newVol(P, rating_list, rating_list_size, RD_list, outcome_list, v);
_preRatingRD(P);
P->__rd = 1 / sqrt((1 / pow(P->__rd, 2)) + (1 / v));
double tempSum = 0;
for (int i = 0; i < rating_list_size; i++) {
tempSum += _g(RD_list[i]) * (outcome_list[i] - _E(P, rating_list[i], RD_list[i]));
}
P->__rating += pow(P->__rd, 2) * tempSum;
}
/** Calculates the new volatility of a player
*
* \param '*P' the struct player who will have their volatility recalculated
* \param '*rating_list' an array of doubles representing the ratings of the
* opponents '*P' played
* \param 'rating_list_size' int representing the size of the '*rating_list'
* array
* \param '*RD_list' an array of doubles representing the rating deviations
* of the opponents '*P' played
* \param '*outcome_list' an array of doubles containing (in order) the outcomes
* of the games '*P' played. 1 for '*P' winning, 0.5 for a draw and 0
* for a '*P' loss
* \param // TODO fill out this param
* \return a double representing '*P's new volatility
*/
double _newVol(struct player* P, double *rating_list, int rating_list_size, \
double *RD_list, double *outcome_list, double v) {
double delta = _delta(P, rating_list, rating_list_size, RD_list, outcome_list, v);
double a = log(pow(P->vol, 2));
double tau = P->_tau;
double x0 = a;
double x1 = 0;
while (x0 != x1) {
// New iteration, so x(i) becomes x(i-1)
x0 = x1;
double d = pow(P->__rating, 2) + v + exp(x0);
double h1 = -(x0 - a) / pow(tau, 2) - 0.5 * exp(x0) / d + 0.5 * exp(x0) * pow(delta / d, 2);
double h2 = -1 / pow(tau, 2) - 0.5 * exp(x0) * (pow(P->__rating, 2) + v) / pow(d, 2) + 0.5 * pow(delta, 2) * exp(x0) * (pow(P->__rating, 2) + v - exp(x0)) / pow(d, 3);
x1 = x0 - (h1 / h2);
}
return exp(x1 / 2);
}
/** Calculates the delta value which represents the "estimated improvement in
* rating by comparing the pre-period rating to the performance rating based
* only on game outcomes"
*
* \param '*P' the struct player the function will calculate the delta of
* \param '*rating_list' an array of doubles representing the ratings of the
* opponents '*P' played
* \param 'rating_list_size' int representing the size of the '*rating_list'
* array
* \param '*RD_list' an array of doubles representing the rating deviations
* of the opponents '*P' played
* \param '*outcome_list' an array of doubles containing (in order) the outcomes
* of the games '*P' played. 1 for '*P' winning, 0.5 for a draw and 0
* for a '*P' loss
* \param 'v' a double representing the result of a '_v' function call
* \return a double containing the estimated improvement in '*P's rating
*/
double _delta(struct player* P, double *rating_list, int rating_list_size,
double *RD_list, double *outcome_list, double v) {
double tempSum = 0;
for (int i = 0; i < rating_list_size; i++) {
tempSum += _g(RD_list[i]) * (outcome_list[i] - _E(P, rating_list[i], RD_list[i]));
}
return v * tempSum;
}
/** Calculates the "estimated variance of a the team's/player's rating
* based only on game outcomes"
*
* \param '*P' the struct player who will have their estimated variance
* calculated
* \param '*rating_list' an array of doubles representing the ratings of the
* opponents '*P' played
* \param 'rating_list_size' int representing the size of the '*rating_list'
* array
* \param '*RD_list' an array of doubles representing the rating deviations
* of the opponents '*P' played
* \return a double representing the estimated variance of '*P' based on
* game outcomes
*/
double _v(struct player* P, double *rating_list, int rating_list_size, double *RD_list) {
double tempSum = 0;
for (int i = 0; i < rating_list_size; i++) {
double tempE = _E(P, rating_list[i], RD_list[i]);
tempSum += pow(_g(RD_list[i]), 2) * tempE * (1 - tempE);
}
return 1 / tempSum;
}
/** The Glicko2 E function which is a helper function for '_v'
*
* \param '*P' the struct player who will have their estimated variance
* calculated
* \param 'p2rating' a double representing the rating of '*P's opponent
* \param 'p2RD' a double representing the rating deviation of '*P's opponent
* \return //TODO write this return
*/
double _E(struct player* P, double p2rating, double p2RD) {
return 1 / (1 + exp(-1 * _g(p2RD) * (P->__rating - p2rating)));
}
/** The Glicko2 g function which is a helper function for '_E'
*
* \param 'RD' a double representing a player's rating deviation
* \return //TODO write this return
*/
double _g(double RD) {
return 1.00 / sqrt(1 + 3 * pow(RD, 2) / pow(M_PI, 2));
}
/* Applies Step 6 of the algorithm. Use this for players who did not compete
* in the rating period.
*
* \param '*P' the struct player who did not compete
* \return void
*/
void did_not_compete(struct player* P) {
_preRatingRD(P);
}