merge
This commit is contained in:
commit
f4b06ae191
5 files changed
+366
-356
No files matched your search
+21
-21
@@ -33,33 +33,33 @@
|
||||
|
||||
class Glicko2_impl
|
||||
{
|
||||
public:
|
||||
public:
|
||||
|
||||
// constructors
|
||||
Glicko2_impl();
|
||||
Glicko2_impl(const Glicko2_impl& rhs);
|
||||
// constructors
|
||||
Glicko2_impl();
|
||||
Glicko2_impl(const Glicko2_impl& rhs);
|
||||
|
||||
// copy assignment
|
||||
Glicko2_impl& operator=(const Glicko2_impl& rhs);
|
||||
// copy assignment
|
||||
Glicko2_impl& operator=(const Glicko2_impl& rhs);
|
||||
|
||||
// destructor
|
||||
virtual ~Glicko2_impl();
|
||||
// destructor
|
||||
virtual ~Glicko2_impl();
|
||||
|
||||
// system constants
|
||||
static const double dvolatility;
|
||||
// system constants
|
||||
static const double dvolatility;
|
||||
|
||||
// rating data
|
||||
double rating;
|
||||
double deviation;
|
||||
double volatility;
|
||||
// rating data
|
||||
double rating;
|
||||
double deviation;
|
||||
double volatility;
|
||||
|
||||
// result data (copy of each opponent and results, 0.0, 0.5, or 1.0)
|
||||
std::vector<Glicko2> opponents;
|
||||
std::vector<double> results;
|
||||
// result data (copy of each opponent and results, 0.0, 0.5, or 1.0)
|
||||
std::vector<Glicko2> opponents;
|
||||
std::vector<double> results;
|
||||
|
||||
// utility functions
|
||||
double g(const double& deviation);
|
||||
double E(const double& rating, const double& rating_opponent, const double& deviation_opponent);
|
||||
// utility functions
|
||||
double g(const double& deviation);
|
||||
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)
|
||||
{
|
||||
#define PI_SQUARED (9.86960440108935861883)
|
||||
#define PI_SQUARED (9.86960440108935861883)
|
||||
return 1.0 / (sqrt(1.0 + 3.0 * deviation * deviation / PI_SQUARED));
|
||||
}
|
||||
|
||||
|
||||
+148
-148
@@ -47,195 +47,195 @@ class Glicko2_impl;
|
||||
*/
|
||||
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
|
||||
* of 350, and a volatility of 0.06.
|
||||
*/
|
||||
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);
|
||||
/**
|
||||
* Copy assignment operator.
|
||||
*
|
||||
* @param rhs Object to copy.
|
||||
*
|
||||
* @return Reference to self after copy assignment.
|
||||
*/
|
||||
Glicko2& operator=(const Glicko2& rhs);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Copy assignment operator.
|
||||
*
|
||||
* @param rhs Object to copy.
|
||||
*
|
||||
* @return Reference to self after copy assignment.
|
||||
*/
|
||||
Glicko2& operator=(const Glicko2& rhs);
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Glicko2();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Glicko2();
|
||||
/**
|
||||
* Less than comparison operator.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
/**
|
||||
* Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
||||
* For details, please see http://www.glicko.com/
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
double GetRating() const;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* For details, please see http://www.glicko.com/
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
double GetRating() const;
|
||||
/**
|
||||
* Set Glicko rating. Internally this is converted to a Glicko2 rating.
|
||||
* For details, please see http://www.glicko.com/
|
||||
*
|
||||
* @param rating Rating.
|
||||
*/
|
||||
void SetRating(double rating);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Get the current rating volatility.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
double GetVolatility() const;
|
||||
/**
|
||||
* Set rating volatility.
|
||||
*
|
||||
* @param volatility Rating volatility.
|
||||
*/
|
||||
void SetVolatility(double volatility);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set Glicko rating. Internally this is converted to a Glicko2 rating.
|
||||
* For details, please see http://www.glicko.com/
|
||||
*
|
||||
* @param rating Rating.
|
||||
*/
|
||||
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(),
|
||||
* and/or AddDraw(). This method is called automatically whenever Update()
|
||||
* is called.
|
||||
*/
|
||||
void ClearResults();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clear all results previously added via AddResult(), AddWin(), AddLoss(),
|
||||
* and/or AddDraw(). This method is called automatically whenever Update()
|
||||
* is called.
|
||||
*/
|
||||
void ClearResults();
|
||||
/**
|
||||
* Add a result to this rating. Note that no calculation is performed until
|
||||
* Update() is called.
|
||||
*
|
||||
* @param opponent Other player in contest.
|
||||
* @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
|
||||
* Update() is called.
|
||||
*
|
||||
* @param opponent Other player in contest.
|
||||
* @param result WIN, LOSS, or DRAW; from the point of view of this player.
|
||||
*/
|
||||
void AddResult(const Glicko2& opponent, RESULT result);
|
||||
/**
|
||||
* Add a win result to this rating. Note that no calculation is performed until
|
||||
* Update() is called.
|
||||
*
|
||||
* @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);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add a win result to this rating. Note that no calculation is performed until
|
||||
* Update() is called.
|
||||
*
|
||||
* @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);
|
||||
/**
|
||||
* Update rating based on current results list, and clear results.
|
||||
*/
|
||||
void Update();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Update rating based on current results list, and clear results.
|
||||
*/
|
||||
void Update();
|
||||
private:
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Private Implementation.
|
||||
*/
|
||||
Glicko2_impl* pimpl;
|
||||
/**
|
||||
* Private Implementation.
|
||||
*/
|
||||
Glicko2_impl* pimpl;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+6
-6
@@ -30,14 +30,14 @@ import copy
|
||||
class Glicko2:
|
||||
"""
|
||||
Glicko-2 Rating calculator class.
|
||||
|
||||
|
||||
This class 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/
|
||||
"""
|
||||
|
||||
@@ -229,12 +229,12 @@ if __name__ == "__main__":
|
||||
B = Glicko2(1400.0, 30.0, 0.06)
|
||||
C = Glicko2(1550.0, 100.0, 0.06)
|
||||
D = Glicko2(1700.0, 300.0, 0.06)
|
||||
|
||||
|
||||
A.AddWin(B)
|
||||
A.AddLoss(C)
|
||||
A.AddResult(D,Glicko2.LOSS)
|
||||
|
||||
|
||||
A.Update();
|
||||
|
||||
|
||||
print "rating",A.GetRating(), "deviation",A.GetDeviation()
|
||||
|
||||
+168
-158
@@ -40,179 +40,189 @@ class Glicko2
|
||||
|
||||
protected
|
||||
|
||||
# Glicko2 reprensentation (Glicko2 uses different scales from Glicko)
|
||||
attr_accessor :g2rating, :g2deviation, :g2volatility
|
||||
# Glicko2 reprensentation (Glicko2 uses different scales from Glicko)
|
||||
attr_reader :g2rating, :g2deviation, :g2volatility
|
||||
|
||||
# a nested Class to hold a single result
|
||||
Result = Struct.new(:opponent, :result)
|
||||
# a nested Class to hold a single result
|
||||
Result = Struct.new(:opponent, :result)
|
||||
|
||||
public
|
||||
|
||||
WIN = 1.0
|
||||
LOSS = 0.0
|
||||
DRAW = 0.5
|
||||
WIN = 1.0
|
||||
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
|
||||
# system constant, determines delta volatility over time; should be [0.3,1.2]
|
||||
DVOL = 0.3
|
||||
|
||||
# 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)
|
||||
self.rating = rating
|
||||
self.deviation = deviation
|
||||
self.volatility = volatility
|
||||
clear_results
|
||||
end
|
||||
|
||||
include Comparable
|
||||
|
||||
# Comparison operator
|
||||
def <=>(other)
|
||||
p_win(other) <=> 0.5
|
||||
end
|
||||
|
||||
# get the probability of beating opponent
|
||||
def p_win(opponent)
|
||||
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))))))
|
||||
end
|
||||
|
||||
# Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
||||
# For details, please see http://www.glicko.com/
|
||||
def rating
|
||||
(@g2rating * 173.7178) + 1500.0
|
||||
end
|
||||
|
||||
# Set Glicko rating. Internally this is converted to a Glicko2 rating.
|
||||
# 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
|
||||
# Glicko-2 RD. For details, please see http://www.glicko.com/
|
||||
def deviation
|
||||
@g2deviation * 173.7178
|
||||
end
|
||||
|
||||
# Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
|
||||
# For details, please see http://www.glicko.com/
|
||||
def deviation=(d)
|
||||
@g2deviation = d / 173.7178
|
||||
end
|
||||
|
||||
# Get the current rating volatility.
|
||||
def volatility
|
||||
@g2volatility
|
||||
end
|
||||
|
||||
# Set rating volatility.
|
||||
def volatility=(v)
|
||||
@g2volatility = v
|
||||
end
|
||||
|
||||
# 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.
|
||||
def clear_results
|
||||
@opponents = Array.new
|
||||
@results = Array.new
|
||||
end
|
||||
|
||||
# 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
|
||||
|
||||
# Add a win result to this rating. Note that no calculation is performed until
|
||||
# update() is called.
|
||||
def add_win(opponent)
|
||||
add_result(opponent,Glicko2::WIN)
|
||||
end
|
||||
|
||||
# Add a loss result to this rating. Note that no calculation is performed until
|
||||
# update() is called.
|
||||
def add_loss(opponent)
|
||||
add_result(opponent,Glicko2::LOSS)
|
||||
end
|
||||
|
||||
# Add a draw result to this rating. Note that no calculation is performed until
|
||||
# update() is called.
|
||||
def add_draw(opponent)
|
||||
add_result(opponent,Glicko2::DRAW)
|
||||
end
|
||||
|
||||
# util func 'g'
|
||||
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
|
||||
|
||||
include Comparable
|
||||
# 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)
|
||||
|
||||
# Comparison operator
|
||||
def <=>(other)
|
||||
self.rating <=> other.rating
|
||||
# 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
|
||||
|
||||
# Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
||||
# For details, please see http://www.glicko.com/
|
||||
def rating
|
||||
(@g2rating * 173.7178) + 1500.0
|
||||
# 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
|
||||
|
||||
# Set Glicko rating. Internally this is converted to a Glicko2 rating.
|
||||
# For details, please see http://www.glicko.com/
|
||||
def rating=(r)
|
||||
@g2rating = (r - 1500.0) / 173.7178
|
||||
# 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
|
||||
|
||||
# Get the current rating deviation. This is a Glicko rating deviation, not a
|
||||
# Glicko-2 RD. For details, please see http://www.glicko.com/
|
||||
def deviation
|
||||
@g2deviation * 173.7178
|
||||
end
|
||||
# Step 8. Convert ratings and RD's back to original scale.
|
||||
@g2deviation = new_deviation
|
||||
@g2volatility = new_volatility
|
||||
@g2rating = new_rating
|
||||
|
||||
# Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
|
||||
# For details, please see http://www.glicko.com/
|
||||
def deviation=(d)
|
||||
@g2deviation = d / 173.7178
|
||||
end
|
||||
# wipe our result lists
|
||||
clear_results
|
||||
end
|
||||
|
||||
# Get the current rating volatility.
|
||||
def volatility
|
||||
@g2volatility
|
||||
end
|
||||
|
||||
# Set rating volatility.
|
||||
def volatility=(v)
|
||||
@g2volatility = v
|
||||
end
|
||||
|
||||
# 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.
|
||||
def clear_results
|
||||
@opponents = []
|
||||
@results = []
|
||||
end
|
||||
|
||||
# Add a result to this rating. Note that no calculation is performed until
|
||||
# update() is called.
|
||||
def add_result(opponent,result)
|
||||
@results.push( Result.new(opponent.clone, result) )
|
||||
end
|
||||
|
||||
# Add a win result to this rating. Note that no calculation is performed until
|
||||
# update() is called.
|
||||
def add_win(opponent)
|
||||
add_result(opponent,Glicko2::WIN)
|
||||
end
|
||||
|
||||
# Add a loss result to this rating. Note that no calculation is performed until
|
||||
# update() is called.
|
||||
def add_loss(opponent)
|
||||
add_result(opponent,Glicko2::LOSS)
|
||||
end
|
||||
|
||||
# Add a draw result to this rating. Note that no calculation is performed until
|
||||
# update() is called.
|
||||
def add_draw(opponent)
|
||||
add_result(opponent,Glicko2::DRAW)
|
||||
end
|
||||
|
||||
# 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
|
||||
|
||||
# Update rating based on current results list, and clear results.
|
||||
def update
|
||||
# util func
|
||||
|
||||
# bail if no opponents set
|
||||
if @results.empty?
|
||||
@g2deviation = Math.sqrt(@g2deviation**2.0 + @g2volatility**2.0)
|
||||
return
|
||||
end
|
||||
|
||||
# compute variance
|
||||
|
||||
#variance = 0.0
|
||||
#@results.inject(0.0) do |variance,r|
|
||||
# g_i = Glicko2.g(r.opponent.g2deviation)
|
||||
# e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation)
|
||||
# g_i ** 2.0 * e_i * (1.0 - e_i)
|
||||
#end
|
||||
|
||||
variance = 0.0
|
||||
@results.each do |r|
|
||||
g_i = Glicko2.g(r.opponent.g2deviation)
|
||||
e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation)
|
||||
variance += g_i ** 2.0 * e_i * (1.0 - e_i)
|
||||
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
|
||||
new_deviation = 1.0 / (Math.sqrt( 1.0/(pre_deviation**2.0) + 1.0 / variance))
|
||||
new_rating = 0.0
|
||||
@results.each do |r|
|
||||
new_rating += Glicko2.g(r.opponent.g2deviation) * (r.result - Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation))
|
||||
end
|
||||
new_rating = new_rating * new_deviation**2.0
|
||||
new_rating += @g2rating
|
||||
|
||||
# wipe our result lists
|
||||
clear_results
|
||||
|
||||
# copy new values
|
||||
@g2deviation = new_deviation
|
||||
@g2volatility = new_volatility
|
||||
@g2rating = new_rating
|
||||
end
|
||||
end
|
||||
|
||||
+23
-23
@@ -5,30 +5,30 @@ require 'test/unit'
|
||||
|
||||
class TestGlicko2 < Test::Unit::TestCase
|
||||
|
||||
def test_simple
|
||||
a = Glicko2.new(1500.0, 200.0, 0.06)
|
||||
b = Glicko2.new(1400.0, 30.0, 0.06)
|
||||
c = Glicko2.new(1550.0, 100.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)
|
||||
def test_simple
|
||||
a = Glicko2.new(1500.0, 200.0, 0.06)
|
||||
b = Glicko2.new(1400.0, 30.0, 0.06)
|
||||
c = Glicko2.new(1550.0, 100.0, 0.06)
|
||||
d = Glicko2.new(1700.0, 300.0, 0.06)
|
||||
|
||||
a.update()
|
||||
|
||||
assert_in_delta(1464.05,a.rating,0.01)
|
||||
assert_in_delta(151.875,a.deviation,0.01)
|
||||
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()
|
||||
|
||||
assert_in_delta(1464.05,a.rating,0.01)
|
||||
assert_in_delta(151.875,a.deviation,0.01)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user