File Coverage

blib/lib/Games/Ratings/Chess/FIDE.pm
Criterion Covered Total %
statement 196 212 92.4
branch 10 16 62.5
condition n/a
subroutine 15 17 88.2
pod 4 4 100.0
total 225 249 90.3


line stmt bran cond sub pod time code
1             package Games::Ratings::Chess::FIDE;
2              
3             ## TODO: check arguments for subroutines (use Data::Checker)?
4             ## TODO: Error handling
5             ## * croak()
6             ## * perldoc anpassen
7              
8 14     14   27222 use strict;
  14         31  
  14         1049  
9 14     14   84 use warnings;
  14         25  
  14         396  
10 14     14   73 use Carp;
  14         25  
  14         1971  
11              
12 14     14   307 use 5.6.1; # 'our' was introduced in perl 5.6
  14         52  
  14         1785  
13 14     14   19572 use version; our $VERSION = qv('0.0.5');
  14         53427  
  14         96  
14              
15             ## look in Games::Ratings for methods not provide by this package
16 14     14   1739 use base qw ( Games::Ratings );
  14         27  
  14         17124  
17              
18             ## calculate rating change
19             sub get_rating_change {
20 15     15 1 128 my ($self) = @_;
21              
22             ## get own rating and own coefficient
23 15         128 my $own_rating = $self->get_rating();
24 15         168 my $own_coefficient = $self->get_coefficient();
25              
26 15         28 my $rating_change_total;
27             ## calculate rating change for each game separately
28 15         93 foreach my $game_ref ( $self->get_all_games() ) {
29             ## add rating change for single game to total rating change
30 121         315 $rating_change_total += _calc_rating_change_for_single_game(
31             $own_rating,
32             $own_coefficient,
33             $game_ref->{opponent_rating},
34             $game_ref->{result},
35             );
36             }
37              
38             ## return total rating change
39 15         309 return $rating_change_total;
40             }
41              
42             ## calculate new rating
43             sub get_new_rating {
44 0     0 1 0 my ($self) = @_;
45              
46             ## $R_o -- old rating
47 0         0 my $R_o = $self->get_rating();
48              
49             ## $R_n -- new rating (rounded)
50 0         0 my $R_n = sprintf( "%.f", $R_o + $self->get_rating_change() );
51              
52             ## return new rating
53 0         0 return $R_n;
54             }
55              
56             ## calculate expected points
57             sub get_points_expected {
58 12     12 1 102 my ($self) = @_;
59            
60             ## $W_e -- expected points
61 12         23 my $W_e;
62              
63             ## $own_rating -- own rating
64 12         73 my $own_rating = $self->get_rating();
65              
66             ## sum up expected points for all games
67 12         73 foreach my $game_ref ( $self->get_all_games() ) {
68 117         305 $W_e += _get_scoring_probability_for_single_game(
69             $own_rating,
70             $game_ref->{opponent_rating},
71             );
72             }
73              
74             ## return expected points
75 12         66 return $W_e;
76             }
77              
78             ## calculate performance
79             sub get_performance {
80 10     10 1 69 my ($self) = @_;
81              
82             ## $R_h -- performance (independent from old rating)
83 10         184 my $R_h;
84              
85             ## average rating of opponents
86 10         262 my $R_c = $self->get_average_rating_of_opponents();
87              
88             ## $P -- percentage score (two digits needed)
89 10         453 my $P = sprintf( "%.2f", $self->get_percentage_score() );
90              
91             ## if player scored 100 % or 0 % it's not possible to calc. performance
92 10 50       71 if ($P == 1) {
93 0         0 $R_h = $R_c + 667;
94 0         0 return $R_h;
95             }
96 10 50       41 if ($P == 0) {
97 0         0 $R_h = $R_c - 667;
98 0         0 return $R_h;
99             }
100              
101             ## lookup $D rating difference according to $P from probability table
102 10         41 my $D = _get_rating_difference_matching_percentage_score($P);
103              
104             ## calculate performance
105 10         28 $R_h = $R_c + $D;
106              
107             ## return performance
108 10         41 return $R_h;
109             }
110              
111             ########################
112             ## internal functions ##
113             ########################
114              
115             ## scoring probabilities depending from rating difference (FIDE B0210.1b)
116             our %scoring_probability_lookup_table;
117             _set_scoring_probability_lookup_table();
118              
119             ## lookup table needed to determine performance (FIDE B0210.1a)
120             our %reverse_scoring_probability_lookup_table;
121             _set_reverse_scoring_probability_lookup_table();
122              
123             ## calculate rating change for single game
124             sub _calc_rating_change_for_single_game {
125 121     121   192 my ($A_rating, $A_coefficient, $B_rating, $result) = @_;
126            
127             ## get numerical result ( win=>1 draw=>0.5 loss=>0 )
128 121         289 my $numerical_result = Games::Ratings::_get_numerical_result($result);
129              
130             ## check whether development coefficient is provided -- guess otherwise
131 121 50       286 if (! defined $A_coefficient) {
132 0         0 $A_coefficient = _guess_coefficient($A_rating);
133             }
134              
135             ## get scoring probability for player A
136 121         217 my $A_exp = _get_scoring_probability_for_single_game($A_rating,$B_rating);
137              
138             ## compute rating changes for player A
139 121         294 my $A_rating_change = $A_coefficient * ($numerical_result-$A_exp);
140            
141             ## return rating changes for player A
142 121         291 return ($A_rating_change);
143             }
144              
145             ## try to guess development coefficient
146             sub _guess_coefficient {
147 0     0   0 my ($player_rating) = @_;
148              
149             ## guess coefficient according to rating (cmp. FIDE handbook B0210.52)
150 0         0 my $player_coefficient;
151 0 0       0 if ($player_rating >= 2400) {
152 0         0 $player_coefficient = 10;
153             }
154             else {
155 0         0 $player_coefficient = 15;
156             }
157              
158             ## return guessed coefficient
159 0         0 return $player_coefficient;
160             }
161              
162             ## calculate scoring probability for a single game
163             sub _get_scoring_probability_for_single_game {
164 238     238   293 my ($A_rating,$B_rating) = @_;
165              
166 238         370 my $rating_difference = _get_rating_difference($A_rating,$B_rating);
167              
168             ## get scoring probability of player A from lookup table
169 238         269 my $A_exp;
170 238 100       434 if ($rating_difference >= 0) {
171 165         379 $A_exp = $scoring_probability_lookup_table{$rating_difference};
172             }
173             else {
174 73         180 $A_exp = 1 - $scoring_probability_lookup_table{0-$rating_difference};
175             }
176              
177             ## return scoring probability for player A
178 238         1240 return ($A_exp);
179             }
180              
181             ## calculate rating difference which is used to calc the scoring probability
182             sub _get_rating_difference {
183 238     238   267 my ($A, $B) = @_;
184              
185             ## compute real rating difference
186 238         304 my $rating_difference = ( $A-$B );
187              
188             ## large rating differences are cut (FIDE handbook B0210.51, 2nd sentence)
189 238 100       489 if ($rating_difference > '350') {
190 5         7 $rating_difference = '350';
191             }
192 238 50       522 if ($rating_difference < '-350') {
193 0         0 $rating_difference = '-350';
194             }
195              
196             ## return rating difference used for rating calculations
197 238         416 return $rating_difference;
198             }
199              
200             ## calculate rating differences matching percentage score
201             sub _get_rating_difference_matching_percentage_score {
202 10     10   25 my ($P) = @_;
203              
204             ## lookup $D (rating difference) from lookup table
205 10         16 my $D;
206 10 100       98 if ($P lt 0.5) {
207             ## percentage score negated -- so we can use our lookup table
208 2         13 my $P_negated = sprintf("%.2f", 1-$P);
209 2         9 $D = -($reverse_scoring_probability_lookup_table{$P_negated});
210             }
211             else {
212 8         34 $D = $reverse_scoring_probability_lookup_table{$P};
213             }
214              
215             ## return $D
216 10         28 return $D;
217             }
218              
219             ## use hash as lookup table for scoring probability (cmp. FIDE B0210.1b)
220             sub _set_scoring_probability_lookup_table {
221 14     14   63 foreach my $rating_difference (0..3) {
222 56         331 $scoring_probability_lookup_table{$rating_difference} = 0.50;
223             }
224 14         46 foreach my $rating_difference (4..10) {
225 98         311 $scoring_probability_lookup_table{$rating_difference} = 0.51;
226             }
227 14         44 foreach my $rating_difference (11..17) {
228 98         319 $scoring_probability_lookup_table{$rating_difference} = 0.52;
229             }
230 14         49 foreach my $rating_difference (18..25) {
231 112         350 $scoring_probability_lookup_table{$rating_difference} = 0.53;
232             }
233 14         41 foreach my $rating_difference (26..32) {
234 98         229 $scoring_probability_lookup_table{$rating_difference} = 0.54;
235             }
236 14         38 foreach my $rating_difference (33..39) {
237 98         195 $scoring_probability_lookup_table{$rating_difference} = 0.55;
238             }
239 14         42 foreach my $rating_difference (40..46) {
240 98         199 $scoring_probability_lookup_table{$rating_difference} = 0.56;
241             }
242 14         38 foreach my $rating_difference (47..53) {
243 98         225 $scoring_probability_lookup_table{$rating_difference} = 0.57;
244             }
245 14         33 foreach my $rating_difference (54..61) {
246 112         249 $scoring_probability_lookup_table{$rating_difference} = 0.58;
247             }
248 14         39 foreach my $rating_difference (62..68) {
249 98         248 $scoring_probability_lookup_table{$rating_difference} = 0.59;
250             }
251 14         34 foreach my $rating_difference (69..76) {
252 112         290 $scoring_probability_lookup_table{$rating_difference} = 0.60;
253             }
254 14         36 foreach my $rating_difference (77..83) {
255 98         692 $scoring_probability_lookup_table{$rating_difference} = 0.61;
256             }
257 14         36 foreach my $rating_difference (84..91) {
258 112         288 $scoring_probability_lookup_table{$rating_difference} = 0.62;
259             }
260 14         34 foreach my $rating_difference (92..98) {
261 98         200 $scoring_probability_lookup_table{$rating_difference} = 0.63;
262             }
263 14         43 foreach my $rating_difference (99..106) {
264 112         347 $scoring_probability_lookup_table{$rating_difference} = 0.64;
265             }
266 14         36 foreach my $rating_difference (107..113) {
267 98         191 $scoring_probability_lookup_table{$rating_difference} = 0.65;
268             }
269 14         33 foreach my $rating_difference (114..121) {
270 112         220 $scoring_probability_lookup_table{$rating_difference} = 0.66;
271             }
272 14         48 foreach my $rating_difference (122..129) {
273 112         400 $scoring_probability_lookup_table{$rating_difference} = 0.67;
274             }
275 14         44 foreach my $rating_difference (130..137) {
276 112         651 $scoring_probability_lookup_table{$rating_difference} = 0.68;
277             }
278 14         44 foreach my $rating_difference (138..145) {
279 112         217 $scoring_probability_lookup_table{$rating_difference} = 0.69;
280             }
281 14         32 foreach my $rating_difference (146..153) {
282 112         229 $scoring_probability_lookup_table{$rating_difference} = 0.70;
283             }
284 14         36 foreach my $rating_difference (154..162) {
285 126         252 $scoring_probability_lookup_table{$rating_difference} = 0.71;
286             }
287 14         33 foreach my $rating_difference (163..170) {
288 112         223 $scoring_probability_lookup_table{$rating_difference} = 0.72;
289             }
290 14         301 foreach my $rating_difference (171..179) {
291 126         289 $scoring_probability_lookup_table{$rating_difference} = 0.73;
292             }
293 14         29 foreach my $rating_difference (180..188) {
294 126         234 $scoring_probability_lookup_table{$rating_difference} = 0.74;
295             }
296 14         35 foreach my $rating_difference (189..197) {
297 126         289 $scoring_probability_lookup_table{$rating_difference} = 0.75;
298             }
299 14         36 foreach my $rating_difference (198..206) {
300 126         249 $scoring_probability_lookup_table{$rating_difference} = 0.76;
301             }
302 14         40 foreach my $rating_difference (207..215) {
303 126         328 $scoring_probability_lookup_table{$rating_difference} = 0.77;
304             }
305 14         41 foreach my $rating_difference (216..225) {
306 140         317 $scoring_probability_lookup_table{$rating_difference} = 0.78;
307             }
308 14         35 foreach my $rating_difference (226..235) {
309 140         394 $scoring_probability_lookup_table{$rating_difference} = 0.79;
310             }
311 14         35 foreach my $rating_difference (236..245) {
312 140         401 $scoring_probability_lookup_table{$rating_difference} = 0.80;
313             }
314 14         50 foreach my $rating_difference (246..256) {
315 154         605 $scoring_probability_lookup_table{$rating_difference} = 0.81;
316             }
317 14         36 foreach my $rating_difference (257..267) {
318 154         293 $scoring_probability_lookup_table{$rating_difference} = 0.82;
319             }
320 14         36 foreach my $rating_difference (268..278) {
321 154         384 $scoring_probability_lookup_table{$rating_difference} = 0.83;
322             }
323 14         39 foreach my $rating_difference (279..290) {
324 168         545 $scoring_probability_lookup_table{$rating_difference} = 0.84;
325             }
326 14         35 foreach my $rating_difference (291..302) {
327 168         745 $scoring_probability_lookup_table{$rating_difference} = 0.85;
328             }
329 14         52 foreach my $rating_difference (303..315) {
330 182         361 $scoring_probability_lookup_table{$rating_difference} = 0.86;
331             }
332 14         42 foreach my $rating_difference (316..328) {
333 182         328 $scoring_probability_lookup_table{$rating_difference} = 0.87;
334             }
335 14         32 foreach my $rating_difference (329..344) {
336 224         469 $scoring_probability_lookup_table{$rating_difference} = 0.88;
337             }
338 14         41 foreach my $rating_difference (345..350) {
339 84         174 $scoring_probability_lookup_table{$rating_difference} = 0.89;
340             }
341             }
342              
343             ## use hash as lookup table (rating differences given a percentage score)
344             ## (cmp. FIDE B0210.1a)
345             sub _set_reverse_scoring_probability_lookup_table {
346 14     14   33 $reverse_scoring_probability_lookup_table{'0.50'} = '0';
347 14         33 $reverse_scoring_probability_lookup_table{'0.51'} = '7';
348 14         29 $reverse_scoring_probability_lookup_table{'0.52'} = '14';
349 14         30 $reverse_scoring_probability_lookup_table{'0.53'} = '21';
350 14         35 $reverse_scoring_probability_lookup_table{'0.54'} = '29';
351 14         86 $reverse_scoring_probability_lookup_table{'0.55'} = '36';
352 14         45 $reverse_scoring_probability_lookup_table{'0.56'} = '43';
353 14         74 $reverse_scoring_probability_lookup_table{'0.57'} = '50';
354 14         51 $reverse_scoring_probability_lookup_table{'0.58'} = '57';
355 14         31 $reverse_scoring_probability_lookup_table{'0.59'} = '65';
356 14         31 $reverse_scoring_probability_lookup_table{'0.60'} = '72';
357 14         37 $reverse_scoring_probability_lookup_table{'0.61'} = '80';
358 14         23 $reverse_scoring_probability_lookup_table{'0.62'} = '87';
359 14         26 $reverse_scoring_probability_lookup_table{'0.63'} = '95';
360 14         32 $reverse_scoring_probability_lookup_table{'0.64'} = '102';
361 14         43 $reverse_scoring_probability_lookup_table{'0.65'} = '110';
362 14         27 $reverse_scoring_probability_lookup_table{'0.66'} = '117';
363 14         26 $reverse_scoring_probability_lookup_table{'0.67'} = '125';
364 14         26 $reverse_scoring_probability_lookup_table{'0.68'} = '133';
365 14         26 $reverse_scoring_probability_lookup_table{'0.69'} = '141';
366 14         32 $reverse_scoring_probability_lookup_table{'0.70'} = '149';
367 14         26 $reverse_scoring_probability_lookup_table{'0.71'} = '158';
368 14         23 $reverse_scoring_probability_lookup_table{'0.72'} = '166';
369 14         25 $reverse_scoring_probability_lookup_table{'0.73'} = '175';
370 14         25 $reverse_scoring_probability_lookup_table{'0.74'} = '184';
371 14         22 $reverse_scoring_probability_lookup_table{'0.75'} = '193';
372 14         27 $reverse_scoring_probability_lookup_table{'0.76'} = '202';
373 14         22 $reverse_scoring_probability_lookup_table{'0.77'} = '211';
374 14         26 $reverse_scoring_probability_lookup_table{'0.78'} = '220';
375 14         24 $reverse_scoring_probability_lookup_table{'0.79'} = '230';
376 14         23 $reverse_scoring_probability_lookup_table{'0.80'} = '240';
377 14         49 $reverse_scoring_probability_lookup_table{'0.81'} = '251';
378 14         26 $reverse_scoring_probability_lookup_table{'0.82'} = '262';
379 14         24 $reverse_scoring_probability_lookup_table{'0.83'} = '273';
380 14         23 $reverse_scoring_probability_lookup_table{'0.84'} = '284';
381 14         27 $reverse_scoring_probability_lookup_table{'0.85'} = '296';
382 14         25 $reverse_scoring_probability_lookup_table{'0.86'} = '309';
383 14         28 $reverse_scoring_probability_lookup_table{'0.87'} = '322';
384 14         42 $reverse_scoring_probability_lookup_table{'0.88'} = '336';
385 14         26 $reverse_scoring_probability_lookup_table{'0.89'} = '351';
386 14         22 $reverse_scoring_probability_lookup_table{'0.90'} = '366';
387 14         37 $reverse_scoring_probability_lookup_table{'0.91'} = '383';
388 14         34 $reverse_scoring_probability_lookup_table{'0.92'} = '401';
389 14         37 $reverse_scoring_probability_lookup_table{'0.93'} = '422';
390 14         27 $reverse_scoring_probability_lookup_table{'0.94'} = '444';
391 14         30 $reverse_scoring_probability_lookup_table{'0.95'} = '470';
392 14         30 $reverse_scoring_probability_lookup_table{'0.96'} = '501';
393 14         26 $reverse_scoring_probability_lookup_table{'0.97'} = '538';
394 14         40 $reverse_scoring_probability_lookup_table{'0.98'} = '589';
395 14         50 $reverse_scoring_probability_lookup_table{'0.99'} = '677';
396             }
397              
398              
399             1; # Magic true value required at end of module
400             __END__