1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Alcalyn\Elo; |
4
|
|
|
|
5
|
|
|
use Alcalyn\Elo\Exception\EloCoefficientException; |
6
|
|
|
|
7
|
|
|
class EloSystem |
8
|
|
|
{ |
9
|
|
|
/** |
10
|
|
|
* F Factor. Determines how quickly elo scores change. |
11
|
|
|
* Default is 16 |
12
|
|
|
* |
13
|
|
|
* @var integer |
14
|
|
|
*/ |
15
|
|
|
private $kFactor; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Defines the range of elo. |
19
|
|
|
* Default is 400 |
20
|
|
|
* |
21
|
|
|
* @var integer |
22
|
|
|
*/ |
23
|
|
|
private $interval; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Defines approximatively the power between intervals. |
27
|
|
|
* Default is 10 |
28
|
|
|
* |
29
|
|
|
* @var integer |
30
|
|
|
*/ |
31
|
|
|
private $pow; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Constructor |
35
|
|
|
* |
36
|
|
|
* @param integer $kFactor |
37
|
|
|
* @param integer $interval |
38
|
|
|
* @param integer $pow |
39
|
|
|
*/ |
40
|
|
|
public function __construct($kFactor = 16, $interval = 400, $pow = 10) |
41
|
|
|
{ |
42
|
|
|
$this->kFactor = $kFactor; |
43
|
|
|
$this->interval = $interval; |
44
|
|
|
$this->pow = $pow; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Calculate new elo scores |
49
|
|
|
* |
50
|
|
|
* @param double $elo0 elo score of player 0 |
51
|
|
|
* @param double $elo1 elo score of player 1 |
52
|
|
|
* @param double $win win coef for player 0. Set 1 to say that player 0 won |
53
|
|
|
* @param double $reliability0 elo reliability for player 0 |
54
|
|
|
* @param double $reliability1 elo reliability for player 1 |
55
|
|
|
* @param integer $kFactor0 override k factor for player 0 |
56
|
|
|
* @param integer $kFactor1 override k factor for player 1 |
57
|
|
|
* |
58
|
|
|
* @return double[] with new elo score at indexes 0 and 1 for player 0 and 1 |
59
|
|
|
*/ |
60
|
|
|
public function calculate($elo0, $elo1, $win, $reliability0 = 1.0, $reliability1 = 1.0, $kFactor0 = null, $kFactor1 = null) |
61
|
|
|
{ |
62
|
|
|
self::checkCoef($reliability0, 'reliability0'); |
63
|
|
|
self::checkCoef($reliability1, 'reliability1'); |
64
|
|
|
self::checkCoef($win, 'win'); |
65
|
|
|
|
66
|
|
|
$kFactor0 = (null === $kFactor0) ? $this->kFactor : $kFactor0; |
67
|
|
|
$kFactor1 = (null === $kFactor1) ? $this->kFactor : $kFactor1; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Calculate probability 0 have to beat 1 |
71
|
|
|
*/ |
72
|
|
|
$proba = $this->proba($elo0, $elo1); |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Calculate elo changement |
76
|
|
|
*/ |
77
|
|
|
$eloUpdate0 = $win - $proba; |
78
|
|
|
$eloUpdate1 = -$eloUpdate0; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Calculate local reliability to avoid 0 and 0 reliability for new players |
82
|
|
|
* (if two new players have 0 and 0.1 reliability, they are rectified to 0.9 and 1) |
83
|
|
|
*/ |
84
|
|
|
self::rectifyReliabilityCoefs($reliability0, $reliability1); |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Apply coefs K-factor and reliability of each other |
88
|
|
|
*/ |
89
|
|
|
$eloUpdate0 *= $kFactor0 * $reliability1 ; |
90
|
|
|
$eloUpdate1 *= $kFactor1 * $reliability0 ; |
91
|
|
|
|
92
|
|
|
return array($elo0 + $eloUpdate0, $elo1 + $eloUpdate1); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Player 0 beat player 1 |
97
|
|
|
* |
98
|
|
|
* @param double $elo0 elo score of player 0 |
99
|
|
|
* @param double $elo1 elo score of player 1 |
100
|
|
|
* @param double $reliability0 elo reliability for player 0 |
101
|
|
|
* @param double $reliability1 elo reliability for player 1 |
102
|
|
|
* |
103
|
|
|
* @return double[] with new elo score at indexes 0 and 1 for player 0 and 1 |
104
|
|
|
*/ |
105
|
|
|
public function win($elo0, $elo1, $reliability0 = 1.0, $reliability1 = 1.0, $kFactor0 = null, $kFactor1 = null) |
106
|
|
|
{ |
107
|
|
|
return $this->calculate($elo0, $elo1, 1, $reliability0, $reliability1, $kFactor0, $kFactor1); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Player 0 lose against player 1 |
112
|
|
|
* |
113
|
|
|
* @param double $elo0 elo score of player 0 |
114
|
|
|
* @param double $elo1 elo score of player 1 |
115
|
|
|
* @param double $reliability0 elo reliability for player 0 |
116
|
|
|
* @param double $reliability1 elo reliability for player 1 |
117
|
|
|
* |
118
|
|
|
* @return double[] with new elo score at indexes 0 and 1 for player 0 and 1 |
119
|
|
|
*/ |
120
|
|
|
public function lose($elo0, $elo1, $reliability0 = 1.0, $reliability1 = 1.0, $kFactor0 = null, $kFactor1 = null) |
121
|
|
|
{ |
122
|
|
|
return $this->calculate($elo0, $elo1, 0, $reliability0, $reliability1, $kFactor0, $kFactor1); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Player 0 and player 1 have made a draw game |
127
|
|
|
* |
128
|
|
|
* @param double $elo0 elo score of player 0 |
129
|
|
|
* @param double $elo1 elo score of player 1 |
130
|
|
|
* @param double $reliability0 elo reliability for player 0 |
131
|
|
|
* @param double $reliability1 elo reliability for player 1 |
132
|
|
|
* |
133
|
|
|
* @return double[] with new elo score at indexes 0 and 1 for player 0 and 1 |
134
|
|
|
*/ |
135
|
|
|
public function draw($elo0, $elo1, $reliability0 = 1.0, $reliability1 = 1.0, $kFactor0 = null, $kFactor1 = null) |
136
|
|
|
{ |
137
|
|
|
return $this->calculate($elo0, $elo1, 0.5, $reliability0, $reliability1, $kFactor0, $kFactor1); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Return probability rate that $elo0 beat $elo1 |
142
|
|
|
* |
143
|
|
|
* @param double $elo0 |
144
|
|
|
* @param double $elo1 |
145
|
|
|
* |
146
|
|
|
* @return double |
147
|
|
|
*/ |
148
|
|
|
public function proba($elo0, $elo1) |
149
|
|
|
{ |
150
|
|
|
return 1 / (1 + pow($this->pow, ($elo1 - $elo0) / $this->interval)) ; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* Increase coefficients the same value so one of them reaches 1 |
155
|
|
|
* |
156
|
|
|
* @param double $coef0 |
157
|
|
|
* @param double $coef1 |
158
|
|
|
*/ |
159
|
|
|
private static function rectifyReliabilityCoefs(&$coef0, &$coef1) |
160
|
|
|
{ |
161
|
|
|
$reliabilityRectification = 1 - max($coef0, $coef1); |
162
|
|
|
|
163
|
|
|
$coef0 += $reliabilityRectification; |
164
|
|
|
$coef1 += $reliabilityRectification; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Check if $coef is in range [0;1] |
169
|
|
|
* |
170
|
|
|
* @param double $coef |
171
|
|
|
* @param string $variableName |
172
|
|
|
*/ |
173
|
|
|
private static function checkCoef($coef, $variableName) |
174
|
|
|
{ |
175
|
|
|
if (($coef < 0) || ($coef > 1)) { |
176
|
|
|
throw new EloCoefficientException($coef, $variableName); |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
|