Untitled diff
186 lines
<?php
<?php
/*******************************************************************************
/*******************************************************************************
glicko-2 ranking system
glicko-2 ranking system
Written by Noah Smith 2011, June 7
Written by Noah Smith 2011, June 7
megiddo ( @t ) thirdform ( dot ) com
megiddo ( @t ) thirdform ( dot ) com
Based on http://www.glicko.net/glicko/glicko2.doc/example.html
Based on http://www.glicko.net/glicko/glicko2.pdf
Usage
Usage
Glicko2Player([$rating = 1500 [, $rd = 350 [, $volatility = 0.06 [, $mu [, $phi [, $sigma [, $systemconstant = 0.75 ]]]]]]]
Glicko2Player([$rating = 1500 [, $rd = 350 [, $volatility = 0.06 [, $mu [, $phi [, $systemconstant = 0.50 ]]]]]]]
For new players, use the default values for rating, rd, and volatility.
For new players, use the default values for rating, rd, and volatility.
The systemconstant should be between 0.3 and 1.2, depending on system itself (this is game dependent, and must be set
The systemconstant should be between 0.3 and 1.2, depending on system itself (this is game dependent, and must be set
by estimation or experimentation)
by estimation or experimentation)
Updating a Glicko2Player
Updating a Glicko2Player
Add wins, losses, and draws to a player:
Add wins, losses, and draws to a player:
$Alice = new Glicko2Player();
$Todd = new Glicko2Player(1500,200);
$Bob = new Glicko2Player();
$Alice = new Glicko2Player(1400,30);
$Charlie = new Glicko2Player();
$Bob = new Glicko2Player(1550,100);
$Charlie = new Glicko2Player(1700,300);
$Alice->AddWin($Bob);
$Alice->AddWin($Charlie)
$Bob->AddLoss($Alice);
$Bob->AddWin($Charlie);
$Charlie->AddLoss($Alice);
$Todd->AddWin($Alice);
$Charlie->AddLoss($Bob);
$Todd->AddLoss($Bob);
$Todd->AddLoss($Charlie);
$Todd->Update();
$Alice->Update();
var_dump($Todd);
$Bob->Update();
$Charlie->Update();
This message and the following may not be removed or modified:
This message and the following may not be removed or modified:
Caveat Emptor
Caveat Emptor
I make no assertions that either Glicko-2 or this code are correct. Use at your own risk.
I make no assertions that either Glicko-2 or this code are correct. Use at your own risk.
*******************************************************************************/
*******************************************************************************/
class Glicko2Player {
class Glicko2Player {
public $rating;
public $rating;
public $rd;
public $rd;
public $sigma;
public $vol;
public $mu;
public $phi;
public $tau;
private $pi2;
public $mu;
public $phi;
public $tau;
private $M;
private $M = array();
function __construct($rating = 1500, $rd = 350, $volatility = 0.06, $mu = null, $phi = null, $sigma = null, $systemconstant = 0.75) {
function __construct($rating = 1500, $rd = 350, $volatility = 0.06, $mu = null, $phi = null, $systemconstant = 0.5) {
$pi2 = pi() * pi();
$M = array();
// Step 1
// Step 1
$this->rating = $rating;
$this->rating = $rating;
$this->rd = $rd;
$this->rd = $rd;
// volatility
// volatility
if (isnull($sigma)) {
$this->vol = $volatility;
$this->sigma = $volatility;
} else {
$this->sigma = $sigma;
}
// System Constant
// System Constant
$this->tau = $systemconstant;
$this->tau = $systemconstant;
// Step 2
// Step 2
// Rating
// Rating
if (isnull($mu)) {
if (is_null($mu)) {
$this->mu = ( $this->rating - 1500 ) / 173.7178;
$this->mu = ( $this->rating - 1500 ) / 173.7178;
} else {
} else {
$this->mu = $mu;
$this->mu = $mu;
}
}
// Rating Deviation
// Rating Deviation
if (isnull($phi)) {
if (is_null($phi)) {
$this->phi = $this->rd / 173.7178;
$this->phi = $this->rd / 173.7178;
} else {
} else {
$this->phi = $phi;
$this->phi = $phi;
}
}
}
}
function AddWin($OtherPlayer) {
function AddWin($OtherPlayer) {
$M[] = $OtherPlayer->MatchElement(1);
array_push($this->M, $OtherPlayer->MatchElement(1));
}
}
function AddLoss($OtherPlayer) {
function AddLoss($OtherPlayer) {
$M[] = $OtherPlayer->MatchElement(0);
array_push($this->M, $OtherPlayer->MatchElement(0));
}
}
function AddDraw($OtherPlayer) {
function AddDraw($OtherPlayer) {
$M[] = $OtherPlayer->MatchElement(0.5);
array_push($this->M, $OtherPlayer->MatchElement(0.5));
$this->draws++;
}
}
function Update($M) {
function Update() {
if (isnull($M)) {
$Results = $this->AddMatches();
$M = $this->M;
}
$Results = $this->AddMatches($M);
$this->rating = $Results['r'];
$this->rating = $Results['r'];
$this->rd = $Results['RD'];
$this->rd = $Results['RD'];
$this->mu = $Results['mu'];
$this->mu = $Results['mu'];
$this->phi = $Results['phi'];
$this->phi = $Results['phi'];
$this->sigma = $Results['sigma'];
$this->vol = $Results['vol'];
$this->M = array();
$this->M = array();
}
}
function MatchElement($score) {
function MatchElement($score) {
return array( 'mu' => $this->mu, 'phi' => $this->phi, 'score' => $score );
return array( 'mu' => $this->mu, 'phi' => $this->phi, 'score' => $score );
}
}
function AddMatches($M) {
function AddMatches() {
if (isnull($M)) {
global $tausq;
$M = $this->M;
global $phsq;
}
global $deltasq;
if (count($M) == 0) {
global $a;
$phi_p = sqrt( ( $this->phi * $this->phi ) + ( $this->sigma + $this->sigma ) );
global $v;
return array( 'r' => $this->rating, 'RD' => 173.7178 * $phi_p, 'mu' => $this->mu, 'phi' => $phi_p, 'sigma' => $this->sigma ) ;
if (count($this->M) == 0) {
$phi_p = sqrt( ( $this->phi * $this->phi ) + ( $this->vol + $this->vol ) );
return array( 'r' => $this->rating, 'RD' => 173.7178 * $phi_p, 'mu' => $this->mu, 'phi' => $phi_p, 'vol' => $this->vol ) ;
}
}
// Step 3 & 4 & 7
// Step 3 & 4 & 7
// Estimated variance
// Estimated variance
$v = 0;
$v = 0;
$v_summation = 0;
// Estimated improvment in rating
// Estimated improvment in rating
$delta = 0;
$delta = 0;
// New mu
// New mu
$mu_p = 0;
$mu_p = 0;
for ($j = 0; $j < count($M); $j++) {
$E = $this->E( $this->mu, $m[$j]['mu'], $m[$j]['phi'] );
$delta_and_mu_p_summation = 0;
$g = $this->g( $M[$j]['phi'] );
$v += 1.0 / ( $g * $g * $E * ( 1 - $E ) );
for ($j = 0; $j < count($this->M); $j++) {
$E = $this->E( $this->mu, $this->M[$j]['mu'], $this->M[$j]['phi'] );
$delta += $g * ( $M['score'] - $E );
$g = $this->g( $this->M[$j]['phi'] );
$v_summation += ( $g * $g * $E * ( 1 - $E ) );
$mu_p += $g * ( $M[$j]['score'] - $E );
$delta_and_mu_p_summation += $g * ( $this->M[$j]['score'] - $E );
}
}
$v = 1 / $v_summation;
// Step 4 (finalize)
// Step 4 (finalize)
$delta *= $v;
$delta = $v * $delta_and_mu_p_summation;
// Step 5
// Step 5
$a = log( $this->sigma * $this->sigma );
$x_prev = $a;
$x = $x_prev;
$tausq = $this->tau * $this->tau;
$tausq = $this->tau * $this->tau;
$phsq = $this->phi * $this->phi;
$phsq = $this->phi * $this->phi;
$deltasq = $delta * $delta;
$deltasq = $delta * $delta;
do {
$exp_xp = exp( $x_prev );
$A = $a = log($this->vol * $this->vol);
$d = $this->phi * $this->phi + $exp_xp;
if($deltasq > ($phsq + $v)) {
$deltadsq = ( $delta / $d ) * ( $delta / $d );
$B = log($deltasq - $phsq - $v);
$h1 = -( $x_prev - $a ) / ( $tausq ) - ( 0.5 * $exp_xp / $d ) + ( 0.5 * $exp_xp * $deltadq );
} else {
$h2 = ( -1.0 / $tausq ) - ( ( 0.5 * $exp_xp ) * ( $phisq + $v ) / ( $d * $d ) ) + ( 0.5 * $deltasq * $exp_xp * ( $phisq + $v - $exp_xp ) / ( $d * $d * $d ) );
$k = 1;
$tmp_x = $x;
while($this->f($a - $k * sqrt($tausq)) < 0) {
$x = $x_prev - ( $h1 / $h2 );
$k++;
$x_prev = $tmp_x;
}
} while (abs($x - $x_prev) > 0.1);
$B = $a - $k * sqrt($tausq);
}
$sigma_p = exp( $x / 2 );
$fA = $this->f($A);
$fB = $this->f($B);
$epi = 0.000001;
while(abs($B-$A) > $epi) {
$C = $A + $fA *($A-$B) / ($fB - $fA);
$fC = $this->f($C);
if($fC*$fB < 0) {
$A = $B;
$fA = $fB;
} else {
$fA = $fA / 2;
}
$B = $C;
$fB = $fC;
}
$vol_p = exp( $A / 2 );
// Step 6
// Step 6
$phi_star = sqrt( $phsq + ( $sigma_p * $sigma_p ) );
$phi_star = sqrt( $phsq + ( $vol_p * $vol_p ) );
// Step 7
// Step 7
$phi_p = 1.0 / ( sqrt( ( 1.0 / ( $phi_star * $phi_star ) ) + ( 1.0 / $v ) ) );
$phi_p = 1.0 / ( sqrt( ( 1.0 / ( $phi_star * $phi_star ) ) + ( 1.0 / $v ) ) );
$mu_p = $mu + $phi_p * $phi_p * $mu_p;
return array( 'r' => ( 173.7178 * $mu_p ) + 1500, 'RD' => 173.7178 * $phi_p, 'mu' => $mu_p, 'phi' => $phi_p, 'sigma' => $sigma_p ) ;
$mu_p = $this->mu + $phi_p * $phi_p * $delta_and_mu_p_summation;
return array( 'r' => ( 173.7178 * $mu_p ) + 1500, 'RD' => 173.7178 * $phi_p, 'mu' => $mu_p, 'phi' => $phi_p, 'vol' => $vol_p ) ;
}
function f($x) {
global $tausq;
global $phsq;
global $deltasq;
global $a;
global $v;
return ((
(exp($x)*($deltasq - $phsq - $v - exp($x)))
/
(2* pow( ($phsq + $v + exp($x) ),2))
) - (
($x - $a) / $tausq
));
}
}
function g($phi) {
function g($phi) {
return 1.0 / ( sqrt( 1.0 + ( 3.0 * $phi * $phi) / ( $this->pi2 ) ) );
return 1.0 / ( sqrt( 1.0 + ( 3.0 * $phi * $phi) / ( pi() * pi() ) ) );
}
}
function E($mu, $mu_j, $phi_j) {
function E($mu, $mu_j, $phi_j) {
return 1.0 / ( 1.0 + exp( -$this->g($phi_j) * ( $mu - $mu_j ) ) );
return 1.0 / ( 1.0 + exp( -$this->g($phi_j) * ( $mu - $mu_j ) ) );
}
}
}
}
?>
?>