Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4167b05bc0
|
||
|
|
51ca8a90eb
|
||
|
|
c5fa604d86 | ||
|
|
e470e1ec76 | ||
|
|
e8a0474c8b | ||
|
|
73bc8ffa23 | ||
|
|
f4b06ae191 | ||
|
|
a8d34856b2 | ||
|
|
cbe79afd1f | ||
|
|
6d99d00885 | ||
|
|
26d92ea617 | ||
|
|
fbce23608e | ||
|
|
59537258f7 | ||
|
|
b240814147 | ||
|
|
723952b178 | ||
|
|
5b64f13b89 |
No files matched your search
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright 2013 Stephen Waits <steve@waits.net>
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
An implementation of the Glicko2 algorithm in several languages (ruby,
|
||||||
|
python, c++).
|
||||||
+21
-21
@@ -33,33 +33,33 @@
|
|||||||
|
|
||||||
class Glicko2_impl
|
class Glicko2_impl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// constructors
|
// constructors
|
||||||
Glicko2_impl();
|
Glicko2_impl();
|
||||||
Glicko2_impl(const Glicko2_impl& rhs);
|
Glicko2_impl(const Glicko2_impl& rhs);
|
||||||
|
|
||||||
// copy assignment
|
// copy assignment
|
||||||
Glicko2_impl& operator=(const Glicko2_impl& rhs);
|
Glicko2_impl& operator=(const Glicko2_impl& rhs);
|
||||||
|
|
||||||
// destructor
|
// destructor
|
||||||
virtual ~Glicko2_impl();
|
virtual ~Glicko2_impl();
|
||||||
|
|
||||||
// system constants
|
// system constants
|
||||||
static const double dvolatility;
|
static const double dvolatility;
|
||||||
|
|
||||||
// rating data
|
// rating data
|
||||||
double rating;
|
double rating;
|
||||||
double deviation;
|
double deviation;
|
||||||
double volatility;
|
double volatility;
|
||||||
|
|
||||||
// result data (copy of each opponent and results, 0.0, 0.5, or 1.0)
|
// result data (copy of each opponent and results, 0.0, 0.5, or 1.0)
|
||||||
std::vector<Glicko2> opponents;
|
std::vector<Glicko2> opponents;
|
||||||
std::vector<double> results;
|
std::vector<double> results;
|
||||||
|
|
||||||
// utility functions
|
// utility functions
|
||||||
double g(const double& deviation);
|
double g(const double& deviation);
|
||||||
double E(const double& rating, const double& rating_opponent, const double& deviation_opponent);
|
double E(const double& rating, const double& rating_opponent, const double& deviation_opponent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ Glicko2_impl::~Glicko2_impl()
|
|||||||
|
|
||||||
double Glicko2_impl::g(const double& deviation)
|
double Glicko2_impl::g(const double& deviation)
|
||||||
{
|
{
|
||||||
#define PI_SQUARED (9.86960440108935861883)
|
#define PI_SQUARED (9.86960440108935861883)
|
||||||
return 1.0 / (sqrt(1.0 + 3.0 * deviation * deviation / PI_SQUARED));
|
return 1.0 / (sqrt(1.0 + 3.0 * deviation * deviation / PI_SQUARED));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+148
-148
@@ -47,195 +47,195 @@ class Glicko2_impl;
|
|||||||
*/
|
*/
|
||||||
class Glicko2
|
class Glicko2
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enumeration of possible results.
|
|
||||||
*/
|
|
||||||
enum RESULT
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A win result, internally 1.0.
|
|
||||||
*/
|
|
||||||
WIN,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A loss result, internally 0.0.
|
* Enumeration of possible results.
|
||||||
*/
|
*/
|
||||||
LOSS,
|
enum RESULT
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A win result, internally 1.0.
|
||||||
|
*/
|
||||||
|
WIN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A loss result, internally 0.0.
|
||||||
|
*/
|
||||||
|
LOSS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A draw result, internally 0.5.
|
||||||
|
*/
|
||||||
|
DRAW
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A draw result, internally 0.5.
|
* Default constructor. Initializes to a rating of 1500, a rating deviation
|
||||||
|
* of 350, and a volatility of 0.06.
|
||||||
*/
|
*/
|
||||||
DRAW
|
Glicko2();
|
||||||
};
|
|
||||||
|
/**
|
||||||
|
* Copy constructor.
|
||||||
|
*
|
||||||
|
* @param rhs Object to copy.
|
||||||
|
*/
|
||||||
|
Glicko2(const Glicko2& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with rating, rating deviation, and volatility specified.
|
||||||
|
*
|
||||||
|
* @param rating Initial rating.
|
||||||
|
* @param deviation Initial rating deviation.
|
||||||
|
* @param volatility Initial volatility.
|
||||||
|
*/
|
||||||
|
Glicko2(double rating, double deviation, double volatility);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor. Initializes to a rating of 1500, a rating deviation
|
* Copy assignment operator.
|
||||||
* of 350, and a volatility of 0.06.
|
*
|
||||||
*/
|
* @param rhs Object to copy.
|
||||||
Glicko2();
|
*
|
||||||
|
* @return Reference to self after copy assignment.
|
||||||
/**
|
*/
|
||||||
* Copy constructor.
|
Glicko2& operator=(const Glicko2& rhs);
|
||||||
*
|
|
||||||
* @param rhs Object to copy.
|
|
||||||
*/
|
|
||||||
Glicko2(const Glicko2& rhs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor with rating, rating deviation, and volatility specified.
|
|
||||||
*
|
|
||||||
* @param rating Initial rating.
|
|
||||||
* @param deviation Initial rating deviation.
|
|
||||||
* @param volatility Initial volatility.
|
|
||||||
*/
|
|
||||||
Glicko2(double rating, double deviation, double volatility);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy assignment operator.
|
* Destructor.
|
||||||
*
|
*/
|
||||||
* @param rhs Object to copy.
|
virtual ~Glicko2();
|
||||||
*
|
|
||||||
* @return Reference to self after copy assignment.
|
|
||||||
*/
|
|
||||||
Glicko2& operator=(const Glicko2& rhs);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destructor.
|
* Less than comparison operator.
|
||||||
*/
|
*
|
||||||
virtual ~Glicko2();
|
* @param rhs Object to compare against.
|
||||||
|
*
|
||||||
|
* @return true if this object's rating is less than rhs's rating; false otherwise.
|
||||||
|
*/
|
||||||
|
bool operator< (const Glicko2& rhs);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Less than comparison operator.
|
* Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
||||||
*
|
* For details, please see http://www.glicko.com/
|
||||||
* @param rhs Object to compare against.
|
*
|
||||||
*
|
* @return
|
||||||
* @return true if this object's rating is less than rhs's rating; false otherwise.
|
*/
|
||||||
*/
|
double GetRating() const;
|
||||||
bool operator< (const Glicko2& rhs);
|
|
||||||
|
/**
|
||||||
|
* Get the current rating deviation. This is a Glicko rating deviation, not a
|
||||||
|
* Glicko-2 RD. For details, please see http://www.glicko.com/
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
double GetDeviation() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current rating volatility.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
double GetVolatility() const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
* Set Glicko rating. Internally this is converted to a Glicko2 rating.
|
||||||
* For details, please see http://www.glicko.com/
|
* For details, please see http://www.glicko.com/
|
||||||
*
|
*
|
||||||
* @return
|
* @param rating Rating.
|
||||||
*/
|
*/
|
||||||
double GetRating() const;
|
void SetRating(double rating);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current rating deviation. This is a Glicko rating deviation, not a
|
* Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
|
||||||
* Glicko-2 RD. For details, please see http://www.glicko.com/
|
* For details, please see http://www.glicko.com/
|
||||||
*
|
*
|
||||||
* @return
|
* @param deviation Rating deviation.
|
||||||
*/
|
*/
|
||||||
double GetDeviation() const;
|
void SetDeviation(double deviation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current rating volatility.
|
* Set rating volatility.
|
||||||
*
|
*
|
||||||
* @return
|
* @param volatility Rating volatility.
|
||||||
*/
|
*/
|
||||||
double GetVolatility() const;
|
void SetVolatility(double volatility);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set Glicko rating. Internally this is converted to a Glicko2 rating.
|
* Clear all results previously added via AddResult(), AddWin(), AddLoss(),
|
||||||
* For details, please see http://www.glicko.com/
|
* and/or AddDraw(). This method is called automatically whenever Update()
|
||||||
*
|
* is called.
|
||||||
* @param rating Rating.
|
*/
|
||||||
*/
|
void ClearResults();
|
||||||
void SetRating(double rating);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
|
|
||||||
* For details, please see http://www.glicko.com/
|
|
||||||
*
|
|
||||||
* @param deviation Rating deviation.
|
|
||||||
*/
|
|
||||||
void SetDeviation(double deviation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set rating volatility.
|
|
||||||
*
|
|
||||||
* @param volatility Rating volatility.
|
|
||||||
*/
|
|
||||||
void SetVolatility(double volatility);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all results previously added via AddResult(), AddWin(), AddLoss(),
|
* Add a result to this rating. Note that no calculation is performed until
|
||||||
* and/or AddDraw(). This method is called automatically whenever Update()
|
* Update() is called.
|
||||||
* is called.
|
*
|
||||||
*/
|
* @param opponent Other player in contest.
|
||||||
void ClearResults();
|
* @param result WIN, LOSS, or DRAW; from the point of view of this player.
|
||||||
|
*/
|
||||||
|
void AddResult(const Glicko2& opponent, RESULT result);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a result to this rating. Note that no calculation is performed until
|
* Add a win result to this rating. Note that no calculation is performed until
|
||||||
* Update() is called.
|
* Update() is called.
|
||||||
*
|
*
|
||||||
* @param opponent Other player in contest.
|
* @param opponent Other (losing) player in contest.
|
||||||
* @param result WIN, LOSS, or DRAW; from the point of view of this player.
|
*/
|
||||||
*/
|
void AddWin(const Glicko2& opponent);
|
||||||
void AddResult(const Glicko2& opponent, RESULT result);
|
|
||||||
|
/**
|
||||||
|
* Add a loss result to this rating. Note that no calculation is performed until
|
||||||
|
* Update() is called.
|
||||||
|
*
|
||||||
|
* @param opponent Other (winning) player in contest.
|
||||||
|
*/
|
||||||
|
void AddLoss(const Glicko2& opponent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a draw result to this rating. Note that no calculation is performed until
|
||||||
|
* Update() is called.
|
||||||
|
*
|
||||||
|
* @param opponent Other (drawing) player in contest.
|
||||||
|
*/
|
||||||
|
void AddDraw(const Glicko2& opponent);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a win result to this rating. Note that no calculation is performed until
|
* Update rating based on current results list, and clear results.
|
||||||
* Update() is called.
|
*/
|
||||||
*
|
void Update();
|
||||||
* @param opponent Other (losing) player in contest.
|
|
||||||
*/
|
|
||||||
void AddWin(const Glicko2& opponent);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a loss result to this rating. Note that no calculation is performed until
|
|
||||||
* Update() is called.
|
|
||||||
*
|
|
||||||
* @param opponent Other (winning) player in contest.
|
|
||||||
*/
|
|
||||||
void AddLoss(const Glicko2& opponent);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a draw result to this rating. Note that no calculation is performed until
|
|
||||||
* Update() is called.
|
|
||||||
*
|
|
||||||
* @param opponent Other (drawing) player in contest.
|
|
||||||
*/
|
|
||||||
void AddDraw(const Glicko2& opponent);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
private:
|
||||||
* Update rating based on current results list, and clear results.
|
|
||||||
*/
|
|
||||||
void Update();
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private Implementation.
|
||||||
private:
|
*/
|
||||||
|
Glicko2_impl* pimpl;
|
||||||
/**
|
|
||||||
* Private Implementation.
|
|
||||||
*/
|
|
||||||
Glicko2_impl* pimpl;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+258
@@ -0,0 +1,258 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 Stephen Waits
|
||||||
|
//
|
||||||
|
// This software is provided 'as-is', without any express or implied warranty. In
|
||||||
|
// no event will the authors be held liable for any damages arising from the use
|
||||||
|
// of this software.
|
||||||
|
//
|
||||||
|
// Permission is granted to anyone to use this software for any purpose,
|
||||||
|
// including commercial applications, and to alter it and redistribute it freely,
|
||||||
|
// subject to the following restrictions:
|
||||||
|
//
|
||||||
|
// 1. The origin of this software must not be misrepresented; you must not claim
|
||||||
|
// that you wrote the original software. If you use this software in a
|
||||||
|
// product, an acknowledgment in the product documentation would be
|
||||||
|
// appreciated but is not required.
|
||||||
|
//
|
||||||
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
// misrepresented as being the original software.
|
||||||
|
//
|
||||||
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Glicko-2 Rating calculator package.
|
||||||
|
//
|
||||||
|
// This package implements the Glicko-2 rating system algorithm written by
|
||||||
|
// Professor Mark E. Glickman. All rating inputs and outputs are Glicko ratings,
|
||||||
|
// but internally everything is converted to and considered Glicko-2 ratings.
|
||||||
|
//
|
||||||
|
// Glicko-2 is an improvement on the original Glicko, which was, in turn, an
|
||||||
|
// improvement on the ELO system.
|
||||||
|
//
|
||||||
|
// The Glicko-2 system is specified on http://www.glicko.com/
|
||||||
|
//
|
||||||
|
package glicko2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// system constant, determines delta volatility over time; should be [0.3,1.2]
|
||||||
|
const kDVOL float64 = 0.3
|
||||||
|
|
||||||
|
// Glicko2 state reprensentation (Glicko2 uses different scales from Glicko)
|
||||||
|
type Glicko2 struct {
|
||||||
|
rating float64
|
||||||
|
deviation float64
|
||||||
|
volatility float64
|
||||||
|
wins [](*Glicko2)
|
||||||
|
losses [](*Glicko2)
|
||||||
|
draws [](*Glicko2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializer with rating, rating deviation, and volatility specified.
|
||||||
|
func NewGlicko2(rating float64, deviation float64, volatility float64) *Glicko2 {
|
||||||
|
return newGlicko2(rating, deviation, volatility)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializer with default rating, rating deviation, and volatility.
|
||||||
|
func NewDefaultGlicko2() *Glicko2 {
|
||||||
|
return newGlicko2(1500.0, 350.0, 0.06)
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper for initialization
|
||||||
|
func newGlicko2(rating float64, deviation float64, volatility float64) *Glicko2 {
|
||||||
|
g := new(Glicko2)
|
||||||
|
g.SetRating(rating)
|
||||||
|
g.SetDeviation(deviation)
|
||||||
|
g.SetVolatility(volatility)
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the probability of beating opponent
|
||||||
|
func (g *Glicko2) ProbWin(opponent *Glicko2) float64 {
|
||||||
|
return 1.0 / (1.0 + math.Pow(10.0, ((opponent.Rating()-g.Rating())/(400.0*math.Sqrt(1.0+0.0000100723986*((g.Deviation()*g.Deviation())+(opponent.Deviation()*opponent.Deviation())))))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
||||||
|
// For details, please see http://www.glicko.com/
|
||||||
|
func (g *Glicko2) Rating() float64 {
|
||||||
|
return ((g.rating * 173.7178) + 1500.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Glicko rating. Internally this is converted to a Glicko2 rating.
|
||||||
|
// For details, please see http://www.glicko.com/
|
||||||
|
func (g *Glicko2) SetRating(rating float64) {
|
||||||
|
g.rating = (rating - 1500.0) / 173.7178
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current rating deviation. This is a Glicko rating deviation, not a
|
||||||
|
// Glicko-2 RD. For details, please see http://www.glicko.com/
|
||||||
|
func (g *Glicko2) Deviation() float64 {
|
||||||
|
return (g.deviation * 173.7178)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
|
||||||
|
// For details, please see http://www.glicko.com/
|
||||||
|
func (g *Glicko2) SetDeviation(deviation float64) {
|
||||||
|
g.deviation = deviation / 173.7178
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current rating volatility.
|
||||||
|
func (g *Glicko2) Volatility() float64 {
|
||||||
|
return g.volatility
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set rating volatility.
|
||||||
|
func (g *Glicko2) SetVolatility(volatility float64) {
|
||||||
|
g.volatility = volatility
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper to make a copy of a Glicko2 struct
|
||||||
|
func (g *Glicko2) duplicate() *Glicko2 {
|
||||||
|
c := *g
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear all results previously added via add_result(), add_win(), add_loss(),
|
||||||
|
// and/or add_draw(). This method is called automatically whenever update()
|
||||||
|
// is called.
|
||||||
|
func (g *Glicko2) ClearResults() {
|
||||||
|
g.wins = nil
|
||||||
|
g.losses = nil
|
||||||
|
g.draws = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a win result to this rating. Note that no calculation is performed until
|
||||||
|
// Update() is called.
|
||||||
|
func (g *Glicko2) AddWin(loser *Glicko2) {
|
||||||
|
g.wins = append(g.wins, loser.duplicate())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a loss result to this rating. Note that no calculation is performed until
|
||||||
|
// Update() is called.
|
||||||
|
func (g *Glicko2) AddLoss(winner *Glicko2) {
|
||||||
|
g.losses = append(g.losses, winner.duplicate())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a draw result to this rating. Note that no calculation is performed until
|
||||||
|
// Update() is called.
|
||||||
|
func (g *Glicko2) AddDraw(drawer *Glicko2) {
|
||||||
|
g.draws = append(g.draws, drawer.duplicate())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a Glicko2 struct
|
||||||
|
func (g *Glicko2) Print() {
|
||||||
|
fmt.Printf(" rating: %v\n", g.Rating())
|
||||||
|
fmt.Printf(" deviation: %v\n", g.Deviation())
|
||||||
|
fmt.Printf("volatility: %v\n", g.Volatility())
|
||||||
|
for _, win := range g.wins {
|
||||||
|
fmt.Printf(" *beat\n")
|
||||||
|
fmt.Printf(" rating: %v\n", win.Rating())
|
||||||
|
fmt.Printf(" deviation: %v\n", win.Deviation())
|
||||||
|
fmt.Printf(" volatility: %v\n", win.Volatility())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// util func 'g'
|
||||||
|
func calcG(deviation float64) float64 {
|
||||||
|
return 1.0 / (math.Sqrt(1.0 + 3.0*(deviation*deviation)/(math.Pi*math.Pi)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// util func 'E'
|
||||||
|
func calcE(rating float64, rating_opponent float64, deviation_opponent float64) float64 {
|
||||||
|
return 1.0 / (1.0 + math.Exp(-calcG(deviation_opponent)*(rating-rating_opponent)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update rating based on current results list, and clear results.
|
||||||
|
func (g *Glicko2) Update() {
|
||||||
|
|
||||||
|
// merge wins, losses, draws slices for convenience
|
||||||
|
results := append(append(g.wins, g.losses...), g.draws...)
|
||||||
|
|
||||||
|
// Note that if a player does not compete during the rating period, then
|
||||||
|
// only Step 6 applies.
|
||||||
|
if len(results) == 0 {
|
||||||
|
g.deviation = math.Sqrt((g.deviation * g.deviation) + (g.volatility * g.volatility))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1. Determine a rating and RD for each player at the onset of the
|
||||||
|
// rating period. The system constant which constrains the change in
|
||||||
|
// volatility over time, needs to be set prior to application of the system.
|
||||||
|
// Reasonable choices are between 0.3 and 1.2, though the system should be
|
||||||
|
// tested to decide which value results in greatest predictive accuracy
|
||||||
|
// ... (ratings already stored in instance)
|
||||||
|
|
||||||
|
// Step 2. For each player, convert the ratings and RD's onto the Glicko-2
|
||||||
|
// scale.
|
||||||
|
// ... (ratings already stored in G2 format)
|
||||||
|
|
||||||
|
// Step 3. Compute the quantity v. This is the estimated variance of the
|
||||||
|
// team's/player's rating based only on game outcomes.
|
||||||
|
variance := 0.0
|
||||||
|
for _, r := range results {
|
||||||
|
g_i := calcG(r.deviation)
|
||||||
|
e_i := calcE(g.rating, r.rating, r.deviation)
|
||||||
|
variance += (g_i * g_i) * e_i * (1.0 - e_i)
|
||||||
|
}
|
||||||
|
variance = 1.0 / variance
|
||||||
|
|
||||||
|
// Step 4. Compute the quantity 'delta', the estimated improvement in rating
|
||||||
|
// by comparing the pre-period rating to the performance rating based only
|
||||||
|
// on game outcomes.
|
||||||
|
delta := 0.0
|
||||||
|
for _, r := range g.wins {
|
||||||
|
delta += calcG(r.deviation) * (1.0 - calcE(g.rating, r.rating, r.deviation))
|
||||||
|
}
|
||||||
|
for _, r := range g.losses {
|
||||||
|
delta += calcG(r.deviation) * (0.0 - calcE(g.rating, r.rating, r.deviation))
|
||||||
|
}
|
||||||
|
for _, r := range g.draws {
|
||||||
|
delta += calcG(r.deviation) * (0.5 - calcE(g.rating, r.rating, r.deviation))
|
||||||
|
}
|
||||||
|
delta *= variance
|
||||||
|
|
||||||
|
// Step 5. Determine the new value of the volatility.
|
||||||
|
new_volatility := 0.0
|
||||||
|
a := math.Log((g.volatility * g.volatility))
|
||||||
|
x := 0.0
|
||||||
|
x_new := a
|
||||||
|
for math.Abs(x-x_new) > 0.0000001 {
|
||||||
|
x = x_new
|
||||||
|
d := (g.deviation * g.deviation) + variance + math.Exp(x)
|
||||||
|
h1 := -(x-a)/(kDVOL*kDVOL) - 0.5*math.Exp(x)/d + 0.5*math.Exp(x)*(delta/d)*(delta/d)
|
||||||
|
h2 := -1.0/(kDVOL*kDVOL) - 0.5*math.Exp(x)*((g.deviation*g.deviation)+variance)/(d*d) + 0.5*(delta*delta)*math.Exp(x)*((g.deviation*g.deviation)+variance-math.Exp(x))/(d*d*d)
|
||||||
|
x_new = x - h1/h2
|
||||||
|
}
|
||||||
|
new_volatility = math.Exp(x_new / 2.0)
|
||||||
|
|
||||||
|
// Step 6. Update the rating deviation to the new pre-rating period value.
|
||||||
|
pre_deviation := math.Sqrt((g.deviation * g.deviation) + (new_volatility * new_volatility))
|
||||||
|
|
||||||
|
// Step 7. Update the rating and RD to the new values.
|
||||||
|
new_deviation := 1.0 / (math.Sqrt(1.0/(pre_deviation*pre_deviation) + 1.0/variance))
|
||||||
|
new_rating := 0.0
|
||||||
|
for _, r := range g.wins {
|
||||||
|
new_rating += calcG(r.deviation) * (1.0 - calcE(g.rating, r.rating, r.deviation))
|
||||||
|
}
|
||||||
|
for _, r := range g.losses {
|
||||||
|
new_rating += calcG(r.deviation) * (0.0 - calcE(g.rating, r.rating, r.deviation))
|
||||||
|
}
|
||||||
|
for _, r := range g.draws {
|
||||||
|
new_rating += calcG(r.deviation) * (0.5 - calcE(g.rating, r.rating, r.deviation))
|
||||||
|
}
|
||||||
|
new_rating = new_rating * (new_deviation * new_deviation)
|
||||||
|
new_rating += g.rating
|
||||||
|
|
||||||
|
// Step 8. Convert ratings and RD's back to original scale.
|
||||||
|
g.deviation = new_deviation
|
||||||
|
g.volatility = new_volatility
|
||||||
|
g.rating = new_rating
|
||||||
|
|
||||||
|
// wipe our result lists
|
||||||
|
g.ClearResults()
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package glicko2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
func inRange(want float64, have float64, delta float64) bool {
|
||||||
|
return math.Abs(want-have) <= delta
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlicko2(t *testing.T) {
|
||||||
|
a := NewGlicko2(1500.0, 200.0, 0.06)
|
||||||
|
b := NewGlicko2(1400.0, 30.0, 0.06)
|
||||||
|
c := NewGlicko2(1550.0, 100.0, 0.06)
|
||||||
|
d := NewGlicko2(1700.0, 300.0, 0.06)
|
||||||
|
|
||||||
|
a.AddWin(b)
|
||||||
|
a.AddLoss(c)
|
||||||
|
a.AddLoss(d)
|
||||||
|
|
||||||
|
if a.Rating() != 1500.0 {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
if a.Deviation() != 200.0 {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Update()
|
||||||
|
|
||||||
|
if !inRange(a.Rating(),1464.05,0.01) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
if !inRange(a.Deviation(),151.516,0.01) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Update()
|
||||||
|
|
||||||
|
if !inRange(a.Rating(),1464.05,0.01) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
if !inRange(a.Deviation(),151.875,0.01) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+6
-6
@@ -30,14 +30,14 @@ import copy
|
|||||||
class Glicko2:
|
class Glicko2:
|
||||||
"""
|
"""
|
||||||
Glicko-2 Rating calculator class.
|
Glicko-2 Rating calculator class.
|
||||||
|
|
||||||
This class implements the Glicko-2 rating system algorithm written by
|
This class implements the Glicko-2 rating system algorithm written by
|
||||||
Professor Mark E. Glickman. All rating inputs and outputs are Glicko ratings,
|
Professor Mark E. Glickman. All rating inputs and outputs are Glicko ratings,
|
||||||
but internally everything is converted to and considered Glicko-2 ratings.
|
but internally everything is converted to and considered Glicko-2 ratings.
|
||||||
|
|
||||||
Glicko-2 is an improvement on the original Glicko, which was, in turn, an
|
Glicko-2 is an improvement on the original Glicko, which was, in turn, an
|
||||||
improvement on the ELO system.
|
improvement on the ELO system.
|
||||||
|
|
||||||
The Glicko-2 system is specified on http://www.glicko.com/
|
The Glicko-2 system is specified on http://www.glicko.com/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -229,12 +229,12 @@ if __name__ == "__main__":
|
|||||||
B = Glicko2(1400.0, 30.0, 0.06)
|
B = Glicko2(1400.0, 30.0, 0.06)
|
||||||
C = Glicko2(1550.0, 100.0, 0.06)
|
C = Glicko2(1550.0, 100.0, 0.06)
|
||||||
D = Glicko2(1700.0, 300.0, 0.06)
|
D = Glicko2(1700.0, 300.0, 0.06)
|
||||||
|
|
||||||
A.AddWin(B)
|
A.AddWin(B)
|
||||||
A.AddLoss(C)
|
A.AddLoss(C)
|
||||||
A.AddResult(D,Glicko2.LOSS)
|
A.AddResult(D,Glicko2.LOSS)
|
||||||
|
|
||||||
A.Update();
|
A.Update();
|
||||||
|
|
||||||
print "rating",A.GetRating(), "deviation",A.GetDeviation()
|
print "rating",A.GetRating(), "deviation",A.GetDeviation()
|
||||||
|
|
||||||
+166
-154
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby -w
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Copyright (c) 2004 Stephen Waits
|
# Copyright (c) 2013 Stephen Waits
|
||||||
#
|
#
|
||||||
# This software is provided 'as-is', without any express or implied warranty. In
|
# This software is provided 'as-is', without any express or implied warranty. In
|
||||||
# no event will the authors be held liable for any damages arising from the use
|
# no event will the authors be held liable for any damages arising from the use
|
||||||
@@ -38,179 +38,191 @@
|
|||||||
#
|
#
|
||||||
class Glicko2
|
class Glicko2
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
# Glicko2 reprensentation (Glicko2 uses different scales from Glicko)
|
# Glicko2 reprensentation (Glicko2 uses different scales from Glicko)
|
||||||
attr_accessor :g2rating, :g2deviation, :g2volatility
|
attr_reader :g2rating, :g2deviation, :g2volatility
|
||||||
|
|
||||||
# a nested Class to hold a single result
|
|
||||||
Result = Struct.new(:opponent, :result)
|
|
||||||
|
|
||||||
public
|
# a nested Class to hold a single result
|
||||||
|
Result = Struct.new(:opponent, :result)
|
||||||
|
|
||||||
WIN = 1.0
|
public
|
||||||
LOSS = 0.0
|
|
||||||
DRAW = 0.5
|
|
||||||
|
|
||||||
# Constructor with rating, rating deviation, and volatility optionally
|
|
||||||
# specified. If nothing specified, nitializes to a rating of 1500, a
|
|
||||||
# rating deviation of 350, and a volatility of 0.06.
|
|
||||||
def initialize(rating = 1500.0, deviation = 350.0, volatility = 0.06)
|
|
||||||
@dvolatility = 0.3
|
|
||||||
self.rating = rating
|
|
||||||
self.deviation = deviation
|
|
||||||
self.volatility = volatility
|
|
||||||
clear_results
|
|
||||||
end
|
|
||||||
|
|
||||||
include Comparable
|
WIN = 1.0
|
||||||
|
LOSS = 0.0
|
||||||
|
DRAW = 0.5
|
||||||
|
|
||||||
# Comparison operator
|
# system constant, determines delta volatility over time; should be [0.3,1.2]
|
||||||
def <=>(other)
|
DVOL = 0.3
|
||||||
self.rating <=> other.rating
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
# Constructor with rating, rating deviation, and volatility optionally
|
||||||
# For details, please see http://www.glicko.com/
|
# specified. If nothing specified, nitializes to a rating of 1500, a
|
||||||
def rating
|
# rating deviation of 350, and a volatility of 0.06.
|
||||||
(@g2rating * 173.7178) + 1500.0
|
def initialize(rating = 1500.0, deviation = 350.0, volatility = 0.06)
|
||||||
end
|
self.rating = rating
|
||||||
|
self.deviation = deviation
|
||||||
|
self.volatility = volatility
|
||||||
|
clear_results
|
||||||
|
end
|
||||||
|
|
||||||
# Set Glicko rating. Internally this is converted to a Glicko2 rating.
|
include Comparable
|
||||||
# For details, please see http://www.glicko.com/
|
|
||||||
def rating=(r)
|
|
||||||
@g2rating = (r - 1500.0) / 173.7178
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the current rating deviation. This is a Glicko rating deviation, not a
|
# Comparison operator
|
||||||
# Glicko-2 RD. For details, please see http://www.glicko.com/
|
def <=>(other)
|
||||||
def deviation
|
p_win(other) <=> 0.5
|
||||||
@g2deviation * 173.7178
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
|
# get the probability of beating opponent
|
||||||
# For details, please see http://www.glicko.com/
|
def p_win(opponent)
|
||||||
def deviation=(d)
|
1.0 / (1.0 + 10.0**(((opponent.rating - self.rating) / (400.0 * Math.sqrt(1.0 + 0.0000100723986 * (self.deviation**2.0 + opponent.deviation**2.0))))))
|
||||||
@g2deviation = d / 173.7178
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Get the current rating volatility.
|
# Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
||||||
def volatility
|
# For details, please see http://www.glicko.com/
|
||||||
@g2volatility
|
def rating
|
||||||
end
|
(@g2rating * 173.7178) + 1500.0
|
||||||
|
end
|
||||||
|
|
||||||
# Set rating volatility.
|
# Set Glicko rating. Internally this is converted to a Glicko2 rating.
|
||||||
def volatility=(v)
|
# For details, please see http://www.glicko.com/
|
||||||
@g2volatility = v
|
def rating=(r)
|
||||||
end
|
@g2rating = (r - 1500.0) / 173.7178
|
||||||
|
end
|
||||||
|
|
||||||
# Clear all results previously added via add_result(), add_win(), add_loss(),
|
# Get the current rating deviation. This is a Glicko rating deviation, not a
|
||||||
# and/or add_draw(). This method is called automatically whenever update()
|
# Glicko-2 RD. For details, please see http://www.glicko.com/
|
||||||
# is called.
|
def deviation
|
||||||
def clear_results
|
@g2deviation * 173.7178
|
||||||
@opponents = []
|
end
|
||||||
@results = []
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add a result to this rating. Note that no calculation is performed until
|
# Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
|
||||||
# update() is called.
|
# For details, please see http://www.glicko.com/
|
||||||
def add_result(opponent,result)
|
def deviation=(d)
|
||||||
@results.push( Result.new(opponent.clone, result) )
|
@g2deviation = d / 173.7178
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a win result to this rating. Note that no calculation is performed until
|
# Get the current rating volatility.
|
||||||
# update() is called.
|
def volatility
|
||||||
def add_win(opponent)
|
@g2volatility
|
||||||
add_result(opponent,Glicko2::WIN)
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Add a loss result to this rating. Note that no calculation is performed until
|
# Set rating volatility.
|
||||||
# update() is called.
|
def volatility=(v)
|
||||||
def add_loss(opponent)
|
@g2volatility = v
|
||||||
add_result(opponent,Glicko2::LOSS)
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Add a draw result to this rating. Note that no calculation is performed until
|
# Clear all results previously added via add_result(), add_win(), add_loss(),
|
||||||
# update() is called.
|
# and/or add_draw(). This method is called automatically whenever update()
|
||||||
def add_draw(opponent)
|
# is called.
|
||||||
add_result(opponent,Glicko2::DRAW)
|
def clear_results
|
||||||
end
|
@opponents = Array.new
|
||||||
|
@results = Array.new
|
||||||
# Update rating based on current results list, and clear results.
|
end
|
||||||
def update
|
|
||||||
|
|
||||||
# util func
|
|
||||||
def Glicko2.g(deviation)
|
|
||||||
1.0 / (Math.sqrt(1.0 + 3.0 * deviation ** 2.0 / (Math::PI ** 2.0)))
|
|
||||||
end
|
|
||||||
|
|
||||||
# util func
|
|
||||||
def Glicko2.E(rating, rating_opponent, deviation_opponent)
|
|
||||||
1.0 / (1.0 + Math.exp(-Glicko2.g(deviation_opponent)*(rating - rating_opponent)));
|
|
||||||
end
|
|
||||||
|
|
||||||
# bail if no opponents set
|
|
||||||
if @results.empty?
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# compute variance
|
# Add a result to this rating. Note that no calculation is performed until
|
||||||
|
# update() is called.
|
||||||
|
def add_result(opponent,result)
|
||||||
|
@results << Result.new(opponent.clone, result)
|
||||||
|
end
|
||||||
|
|
||||||
#variance = 0.0
|
# Add a win result to this rating. Note that no calculation is performed until
|
||||||
#@results.inject(0.0) do |variance,r|
|
# update() is called.
|
||||||
# g_i = Glicko2.g(r.opponent.g2deviation)
|
def add_win(opponent)
|
||||||
# e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation)
|
add_result(opponent,Glicko2::WIN)
|
||||||
# g_i ** 2.0 * e_i * (1.0 - e_i)
|
end
|
||||||
#end
|
|
||||||
|
|
||||||
variance = 0.0
|
# Add a loss result to this rating. Note that no calculation is performed until
|
||||||
@results.each do |r|
|
# update() is called.
|
||||||
g_i = Glicko2.g(r.opponent.g2deviation)
|
def add_loss(opponent)
|
||||||
e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation)
|
add_result(opponent,Glicko2::LOSS)
|
||||||
variance += g_i ** 2.0 * e_i * (1.0 - e_i)
|
end
|
||||||
end
|
|
||||||
variance = 1.0 / variance
|
|
||||||
|
|
||||||
# compute delta
|
|
||||||
delta = 0.0
|
|
||||||
@results.each do |r|
|
|
||||||
delta += Glicko2.g(r.opponent.g2deviation) * (r.result - Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation))
|
|
||||||
end
|
|
||||||
delta *= variance
|
|
||||||
|
|
||||||
# determine new volatility
|
|
||||||
new_volatility = 0.0
|
|
||||||
a = Math.log( @g2volatility**2.0 )
|
|
||||||
x = 0.0
|
|
||||||
x_new = a
|
|
||||||
while ( (x - x_new).abs > 0.0000001 )
|
|
||||||
x = x_new
|
|
||||||
d = @g2deviation**2.0 + variance + Math.exp(x)
|
|
||||||
h1 = -(x - a)/(@dvolatility**2.0) - 0.5*Math.exp(x)/d + 0.5*Math.exp(x)*(delta/d)*(delta/d)
|
|
||||||
h2 = -1.0/(@dvolatility**2.0) - 0.5*Math.exp(x)*(@g2deviation**2.0+variance)/(d**2.0) + 0.5*(delta**2.0)*Math.exp(x)*((@g2deviation**2.0) + variance - Math.exp(x))/(d**3.0)
|
|
||||||
x_new = x - h1/h2
|
|
||||||
end
|
|
||||||
new_volatility = Math.exp(x_new / 2.0)
|
|
||||||
|
|
||||||
# update the rating deviation to the new pre-rating period value
|
|
||||||
pre_deviation = Math.sqrt( @g2deviation**2.0 + new_volatility**2.0 )
|
|
||||||
|
|
||||||
# update the rating and deviation
|
# Add a draw result to this rating. Note that no calculation is performed until
|
||||||
new_deviation = 1.0 / (Math.sqrt( 1.0/(pre_deviation**2.0) + 1.0 / variance))
|
# update() is called.
|
||||||
new_rating = 0.0
|
def add_draw(opponent)
|
||||||
@results.each do |r|
|
add_result(opponent,Glicko2::DRAW)
|
||||||
new_rating += Glicko2.g(r.opponent.g2deviation) * (r.result - Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation))
|
end
|
||||||
end
|
|
||||||
new_rating = new_rating * new_deviation**2.0
|
|
||||||
new_rating += @g2rating
|
|
||||||
|
|
||||||
# wipe our result lists
|
# util func 'g'
|
||||||
clear_results
|
def Glicko2.g(deviation)
|
||||||
|
1.0 / (Math.sqrt(1.0 + 3.0 * deviation ** 2.0 / (Math::PI ** 2.0)))
|
||||||
|
end
|
||||||
|
|
||||||
|
# util func 'E'
|
||||||
|
def Glicko2.E(rating, rating_opponent, deviation_opponent)
|
||||||
|
1.0 / (1.0 + Math.exp(-Glicko2.g(deviation_opponent)*(rating - rating_opponent)));
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update rating based on current results list, and clear results.
|
||||||
|
def update
|
||||||
|
# Note that if a player does not compete during the rating period, then
|
||||||
|
# only Step 6 applies.
|
||||||
|
if @results.empty?
|
||||||
|
# In this case, the player's rating and volatility parameters remain the
|
||||||
|
# same, but the RD increases according to:
|
||||||
|
@g2deviation = Math.sqrt(@g2deviation**2.0 + @g2volatility**2.0)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# Step 1. Determine a rating and RD for each player at the onset of the
|
||||||
|
# rating period. The system constant which constrains the change in
|
||||||
|
# volatility over time, needs to be set prior to application of the system.
|
||||||
|
# Reasonable choices are between 0.3 and 1.2, though the system should be
|
||||||
|
# tested to decide which value results in greatest predictive accuracy
|
||||||
|
# ... (ratings already stored in instance)
|
||||||
|
|
||||||
|
# Step 2. For each player, convert the ratings and RD's onto the Glicko-2
|
||||||
|
# scale.
|
||||||
|
# ... (ratings already stored in G2 format)
|
||||||
|
|
||||||
|
# Step 3. Compute the quantity v. This is the estimated variance of the
|
||||||
|
# team's/player's rating based only on game outcomes.
|
||||||
|
variance = 1.0 / @results.inject(0.0) do |sum,r|
|
||||||
|
g_i = Glicko2.g(r.opponent.g2deviation)
|
||||||
|
e_i = Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation)
|
||||||
|
sum + g_i**2.0 * e_i * (1.0 - e_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Step 4. Compute the quantity 'delta', the estimated improvement in rating
|
||||||
|
# by comparing the pre-period rating to the performance rating based only
|
||||||
|
# on game outcomes.
|
||||||
|
delta = variance * @results.inject(0.0) do |sum,r|
|
||||||
|
g_i = Glicko2.g(r.opponent.g2deviation)
|
||||||
|
e_i = Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation)
|
||||||
|
sum + g_i * (r.result - e_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Step 5. Determine the new value of the volatility.
|
||||||
|
a = Math.log(@g2volatility**2.0)
|
||||||
|
x0, x1 = 0.0, a
|
||||||
|
while ((x0 - x1).abs > 0.0000001)
|
||||||
|
x0 = x1
|
||||||
|
d = @g2deviation**2.0 + variance + Math.exp(x0)
|
||||||
|
h1 = -(x0 - a)/(DVOL**2.0) - 0.5*Math.exp(x0)/d + 0.5*Math.exp(x0)*(delta/d)*(delta/d)
|
||||||
|
h2 = -1.0/(DVOL**2.0) - 0.5*Math.exp(x0)*(@g2deviation**2.0+variance)/(d**2.0) + 0.5*(delta**2.0)*Math.exp(x0)*((@g2deviation**2.0) + variance - Math.exp(x0))/(d**3.0)
|
||||||
|
x1 = x0 - h1/h2
|
||||||
|
end
|
||||||
|
new_volatility = Math.exp(x1 / 2.0)
|
||||||
|
|
||||||
|
# Step 6. Update the rating deviation to the new pre-rating period value.
|
||||||
|
pre_deviation = Math.sqrt(@g2deviation**2.0 + new_volatility**2.0)
|
||||||
|
|
||||||
|
# Step 7. Update the rating and RD to the new values.
|
||||||
|
new_deviation = 1.0 / (Math.sqrt(1.0/(pre_deviation**2.0) + 1.0 / variance))
|
||||||
|
new_rating = @g2rating + new_deviation**2.0 * @results.inject(0.0) do |sum,r|
|
||||||
|
g_i = Glicko2.g(r.opponent.g2deviation)
|
||||||
|
e_i = Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation)
|
||||||
|
sum + g_i * (r.result - e_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Step 8. Convert ratings and RD's back to original scale.
|
||||||
|
@g2deviation = new_deviation
|
||||||
|
@g2volatility = new_volatility
|
||||||
|
@g2rating = new_rating
|
||||||
|
|
||||||
|
# wipe our result lists
|
||||||
|
clear_results
|
||||||
|
end
|
||||||
|
|
||||||
# copy new values
|
|
||||||
@g2deviation = new_deviation
|
|
||||||
@g2volatility = new_volatility
|
|
||||||
@g2rating = new_rating
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Regular → Executable
+25
-25
@@ -1,34 +1,34 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby -w
|
||||||
|
|
||||||
require 'glicko2'
|
require_relative 'glicko2'
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
|
|
||||||
class TestGlicko2 < Test::Unit::TestCase
|
class TestGlicko2 < Test::Unit::TestCase
|
||||||
|
|
||||||
def test_simple
|
def test_simple
|
||||||
a = Glicko2.new(1500.0, 200.0, 0.06)
|
a = Glicko2.new(1500.0, 200.0, 0.06)
|
||||||
b = Glicko2.new(1400.0, 30.0, 0.06)
|
b = Glicko2.new(1400.0, 30.0, 0.06)
|
||||||
c = Glicko2.new(1550.0, 100.0, 0.06)
|
c = Glicko2.new(1550.0, 100.0, 0.06)
|
||||||
d = Glicko2.new(1700.0, 300.0, 0.06)
|
d = Glicko2.new(1700.0, 300.0, 0.06)
|
||||||
|
|
||||||
a.add_win(b)
|
|
||||||
a.add_loss(c)
|
|
||||||
a.add_result(d,0.0)
|
|
||||||
|
|
||||||
assert_equal(1500.0, a.rating)
|
|
||||||
assert_equal(200.0, a.deviation)
|
|
||||||
|
|
||||||
a.update()
|
|
||||||
|
|
||||||
assert_in_delta(1464.05,a.rating,0.01)
|
|
||||||
assert_in_delta(151.516,a.deviation,0.01)
|
|
||||||
|
|
||||||
a.update()
|
a.add_win(b)
|
||||||
|
a.add_loss(c)
|
||||||
assert_in_delta(1464.05,a.rating,0.01)
|
a.add_result(d,0.0)
|
||||||
assert_in_delta(151.516,a.deviation,0.01)
|
|
||||||
|
assert_equal(1500.0, a.rating)
|
||||||
|
assert_equal(200.0, a.deviation)
|
||||||
|
|
||||||
|
a.update()
|
||||||
|
|
||||||
|
assert_in_delta(1464.05,a.rating,0.01)
|
||||||
|
assert_in_delta(151.516,a.deviation,0.01)
|
||||||
|
|
||||||
|
a.update()
|
||||||
|
|
||||||
|
assert_in_delta(1464.05,a.rating,0.01)
|
||||||
|
assert_in_delta(151.875,a.deviation,0.01)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Reference in new issue
Block a user