add decay when results are empty and retab file
This commit is contained in:
1 parent
5b64f13b89
commit
cbe79afd1f
2 files changed
+149
-147
No files matched your search
+146
-144
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby -w
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -38,179 +38,181 @@
|
|||||||
#
|
#
|
||||||
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_accessor :g2rating, :g2deviation, :g2volatility
|
||||||
|
|
||||||
# a nested Class to hold a single result
|
# a nested Class to hold a single result
|
||||||
Result = Struct.new(:opponent, :result)
|
Result = Struct.new(:opponent, :result)
|
||||||
|
|
||||||
public
|
public
|
||||||
|
|
||||||
WIN = 1.0
|
WIN = 1.0
|
||||||
LOSS = 0.0
|
LOSS = 0.0
|
||||||
DRAW = 0.5
|
DRAW = 0.5
|
||||||
|
|
||||||
# Constructor with rating, rating deviation, and volatility optionally
|
# Constructor with rating, rating deviation, and volatility optionally
|
||||||
# specified. If nothing specified, nitializes to a rating of 1500, a
|
# specified. If nothing specified, nitializes to a rating of 1500, a
|
||||||
# rating deviation of 350, and a volatility of 0.06.
|
# rating deviation of 350, and a volatility of 0.06.
|
||||||
def initialize(rating = 1500.0, deviation = 350.0, volatility = 0.06)
|
def initialize(rating = 1500.0, deviation = 350.0, volatility = 0.06)
|
||||||
@dvolatility = 0.3
|
@dvolatility = 0.3
|
||||||
self.rating = rating
|
self.rating = rating
|
||||||
self.deviation = deviation
|
self.deviation = deviation
|
||||||
self.volatility = volatility
|
self.volatility = volatility
|
||||||
clear_results
|
clear_results
|
||||||
end
|
end
|
||||||
|
|
||||||
include Comparable
|
include Comparable
|
||||||
|
|
||||||
# Comparison operator
|
# Comparison operator
|
||||||
def <=>(other)
|
def <=>(other)
|
||||||
self.rating <=> other.rating
|
self.rating <=> other.rating
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
# Get the current rating. This rating is a Glicko rating, not a Glicko2 rating.
|
||||||
# For details, please see http://www.glicko.com/
|
# For details, please see http://www.glicko.com/
|
||||||
def rating
|
def rating
|
||||||
(@g2rating * 173.7178) + 1500.0
|
(@g2rating * 173.7178) + 1500.0
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set Glicko rating. Internally this is converted to 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/
|
||||||
def rating=(r)
|
def rating=(r)
|
||||||
@g2rating = (r - 1500.0) / 173.7178
|
@g2rating = (r - 1500.0) / 173.7178
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the current rating deviation. This is a Glicko rating deviation, not a
|
# Get the current rating deviation. This is a Glicko rating deviation, not a
|
||||||
# Glicko-2 RD. For details, please see http://www.glicko.com/
|
# Glicko-2 RD. For details, please see http://www.glicko.com/
|
||||||
def deviation
|
def deviation
|
||||||
@g2deviation * 173.7178
|
@g2deviation * 173.7178
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
|
# Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
|
||||||
# For details, please see http://www.glicko.com/
|
# For details, please see http://www.glicko.com/
|
||||||
def deviation=(d)
|
def deviation=(d)
|
||||||
@g2deviation = d / 173.7178
|
@g2deviation = d / 173.7178
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the current rating volatility.
|
# Get the current rating volatility.
|
||||||
def volatility
|
def volatility
|
||||||
@g2volatility
|
@g2volatility
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set rating volatility.
|
# Set rating volatility.
|
||||||
def volatility=(v)
|
def volatility=(v)
|
||||||
@g2volatility = v
|
@g2volatility = v
|
||||||
end
|
end
|
||||||
|
|
||||||
# Clear all results previously added via add_result(), add_win(), add_loss(),
|
# Clear all results previously added via add_result(), add_win(), add_loss(),
|
||||||
# and/or add_draw(). This method is called automatically whenever update()
|
# and/or add_draw(). This method is called automatically whenever update()
|
||||||
# is called.
|
# is called.
|
||||||
def clear_results
|
def clear_results
|
||||||
@opponents = []
|
@opponents = []
|
||||||
@results = []
|
@results = []
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a result to this rating. Note that no calculation is performed until
|
# Add a result to this rating. Note that no calculation is performed until
|
||||||
# update() is called.
|
# update() is called.
|
||||||
def add_result(opponent,result)
|
def add_result(opponent,result)
|
||||||
@results.push( Result.new(opponent.clone, result) )
|
@results.push( Result.new(opponent.clone, result) )
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a win 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.
|
||||||
def add_win(opponent)
|
def add_win(opponent)
|
||||||
add_result(opponent,Glicko2::WIN)
|
add_result(opponent,Glicko2::WIN)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a loss result to this rating. Note that no calculation is performed until
|
# Add a loss result to this rating. Note that no calculation is performed until
|
||||||
# update() is called.
|
# update() is called.
|
||||||
def add_loss(opponent)
|
def add_loss(opponent)
|
||||||
add_result(opponent,Glicko2::LOSS)
|
add_result(opponent,Glicko2::LOSS)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a draw result to this rating. Note that no calculation is performed until
|
# Add a draw result to this rating. Note that no calculation is performed until
|
||||||
# update() is called.
|
# update() is called.
|
||||||
def add_draw(opponent)
|
def add_draw(opponent)
|
||||||
add_result(opponent,Glicko2::DRAW)
|
add_result(opponent,Glicko2::DRAW)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Update rating based on current results list, and clear results.
|
# util func
|
||||||
def update
|
def Glicko2.g(deviation)
|
||||||
|
1.0 / (Math.sqrt(1.0 + 3.0 * deviation ** 2.0 / (Math::PI ** 2.0)))
|
||||||
|
end
|
||||||
|
|
||||||
# util func
|
# util func
|
||||||
def Glicko2.g(deviation)
|
def Glicko2.E(rating, rating_opponent, deviation_opponent)
|
||||||
1.0 / (Math.sqrt(1.0 + 3.0 * deviation ** 2.0 / (Math::PI ** 2.0)))
|
1.0 / (1.0 + Math.exp(-Glicko2.g(deviation_opponent)*(rating - rating_opponent)));
|
||||||
end
|
end
|
||||||
|
|
||||||
# util func
|
# Update rating based on current results list, and clear results.
|
||||||
def Glicko2.E(rating, rating_opponent, deviation_opponent)
|
def update
|
||||||
1.0 / (1.0 + Math.exp(-Glicko2.g(deviation_opponent)*(rating - rating_opponent)));
|
# util func
|
||||||
end
|
|
||||||
|
|
||||||
# bail if no opponents set
|
# bail if no opponents set
|
||||||
if @results.empty?
|
if @results.empty?
|
||||||
return nil
|
@g2deviation = Math.sqrt(@g2deviation**2.0 + @g2volatility**2.0)
|
||||||
end
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# compute variance
|
# compute variance
|
||||||
|
|
||||||
#variance = 0.0
|
#variance = 0.0
|
||||||
#@results.inject(0.0) do |variance,r|
|
#@results.inject(0.0) do |variance,r|
|
||||||
# g_i = Glicko2.g(r.opponent.g2deviation)
|
# g_i = Glicko2.g(r.opponent.g2deviation)
|
||||||
# e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation)
|
# e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation)
|
||||||
# g_i ** 2.0 * e_i * (1.0 - e_i)
|
# g_i ** 2.0 * e_i * (1.0 - e_i)
|
||||||
#end
|
#end
|
||||||
|
|
||||||
variance = 0.0
|
variance = 0.0
|
||||||
@results.each do |r|
|
@results.each do |r|
|
||||||
g_i = Glicko2.g(r.opponent.g2deviation)
|
g_i = Glicko2.g(r.opponent.g2deviation)
|
||||||
e_i = Glicko2.E(@g2rating,r.opponent.g2rating,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)
|
variance += g_i ** 2.0 * e_i * (1.0 - e_i)
|
||||||
end
|
end
|
||||||
variance = 1.0 / variance
|
variance = 1.0 / variance
|
||||||
|
|
||||||
# compute delta
|
# compute delta
|
||||||
delta = 0.0
|
delta = 0.0
|
||||||
@results.each do |r|
|
@results.each do |r|
|
||||||
delta += Glicko2.g(r.opponent.g2deviation) * (r.result - Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation))
|
delta += Glicko2.g(r.opponent.g2deviation) * (r.result - Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation))
|
||||||
end
|
end
|
||||||
delta *= variance
|
delta *= variance
|
||||||
|
|
||||||
# determine new volatility
|
# determine new volatility
|
||||||
new_volatility = 0.0
|
new_volatility = 0.0
|
||||||
a = Math.log( @g2volatility**2.0 )
|
a = Math.log( @g2volatility**2.0 )
|
||||||
x = 0.0
|
x = 0.0
|
||||||
x_new = a
|
x_new = a
|
||||||
while ( (x - x_new).abs > 0.0000001 )
|
while ( (x - x_new).abs > 0.0000001 )
|
||||||
x = x_new
|
x = x_new
|
||||||
d = @g2deviation**2.0 + variance + Math.exp(x)
|
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)
|
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)
|
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
|
x_new = x - h1/h2
|
||||||
end
|
end
|
||||||
new_volatility = Math.exp(x_new / 2.0)
|
new_volatility = Math.exp(x_new / 2.0)
|
||||||
|
|
||||||
# update the rating deviation to the new pre-rating period value
|
# update the rating deviation to the new pre-rating period value
|
||||||
pre_deviation = Math.sqrt( @g2deviation**2.0 + new_volatility**2.0 )
|
pre_deviation = Math.sqrt( @g2deviation**2.0 + new_volatility**2.0 )
|
||||||
|
|
||||||
# update the rating and deviation
|
# update the rating and deviation
|
||||||
new_deviation = 1.0 / (Math.sqrt( 1.0/(pre_deviation**2.0) + 1.0 / variance))
|
new_deviation = 1.0 / (Math.sqrt( 1.0/(pre_deviation**2.0) + 1.0 / variance))
|
||||||
new_rating = 0.0
|
new_rating = 0.0
|
||||||
@results.each do |r|
|
@results.each do |r|
|
||||||
new_rating += Glicko2.g(r.opponent.g2deviation) * (r.result - Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation))
|
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 = new_rating * new_deviation**2.0
|
||||||
new_rating += @g2rating
|
new_rating += @g2rating
|
||||||
|
|
||||||
# wipe our result lists
|
# wipe our result lists
|
||||||
clear_results
|
clear_results
|
||||||
|
|
||||||
# copy new values
|
# copy new values
|
||||||
@g2deviation = new_deviation
|
@g2deviation = new_deviation
|
||||||
@g2volatility = new_volatility
|
@g2volatility = new_volatility
|
||||||
@g2rating = new_rating
|
@g2rating = new_rating
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Regular → Executable
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
#!/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
|
||||||
@@ -26,7 +26,7 @@ class TestGlicko2 < Test::Unit::TestCase
|
|||||||
a.update()
|
a.update()
|
||||||
|
|
||||||
assert_in_delta(1464.05,a.rating,0.01)
|
assert_in_delta(1464.05,a.rating,0.01)
|
||||||
assert_in_delta(151.516,a.deviation,0.01)
|
assert_in_delta(151.875,a.deviation,0.01)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user