add decay when results are empty and retab file

This commit is contained in:
swaits committed 2013-07-30 01:42:17 -07:00
1 parent 5b64f13b89
commit cbe79afd1f
2 files changed
+158 -156

No files matched your search

+155 -153
View File
@@ -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
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 # Constructor with rating, rating deviation, and volatility optionally
def <=>(other) # specified. If nothing specified, nitializes to a rating of 1500, a
self.rating <=> other.rating # rating deviation of 350, and a volatility of 0.06.
end 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
# Get the current rating. This rating is a Glicko rating, not a Glicko2 rating. include Comparable
# 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. # Comparison operator
# For details, please see http://www.glicko.com/ def <=>(other)
def rating=(r) self.rating <=> other.rating
@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. This rating is a Glicko rating, not a Glicko2 rating.
# Glicko-2 RD. For details, please see http://www.glicko.com/ # For details, please see http://www.glicko.com/
def deviation def rating
@g2deviation * 173.7178 (@g2rating * 173.7178) + 1500.0
end end
# Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD. # 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 deviation=(d) def rating=(r)
@g2deviation = d / 173.7178 @g2rating = (r - 1500.0) / 173.7178
end end
# Get the current rating volatility. # Get the current rating deviation. This is a Glicko rating deviation, not a
def volatility # Glicko-2 RD. For details, please see http://www.glicko.com/
@g2volatility def deviation
end @g2deviation * 173.7178
end
# Set rating volatility. # Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD.
def volatility=(v) # For details, please see http://www.glicko.com/
@g2volatility = v def deviation=(d)
end @g2deviation = d / 173.7178
end
# Clear all results previously added via add_result(), add_win(), add_loss(), # Get the current rating volatility.
# and/or add_draw(). This method is called automatically whenever update() def volatility
# is called. @g2volatility
def clear_results end
@opponents = []
@results = []
end
# Add a result to this rating. Note that no calculation is performed until # Set rating volatility.
# update() is called. def volatility=(v)
def add_result(opponent,result) @g2volatility = v
@results.push( Result.new(opponent.clone, result) ) end
end
# Add a win 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_win(opponent) # is called.
add_result(opponent,Glicko2::WIN) def clear_results
end @opponents = []
@results = []
end
# Add a loss 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_loss(opponent) def add_result(opponent,result)
add_result(opponent,Glicko2::LOSS) @results.push( Result.new(opponent.clone, result) )
end end
# Add a draw 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_draw(opponent) def add_win(opponent)
add_result(opponent,Glicko2::DRAW) add_result(opponent,Glicko2::WIN)
end end
# Update rating based on current results list, and clear results.
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 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
#variance = 0.0 # Add a draw 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_draw(opponent)
# e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation) add_result(opponent,Glicko2::DRAW)
# g_i ** 2.0 * e_i * (1.0 - e_i) end
#end
variance = 0.0 # util func
@results.each do |r| def Glicko2.g(deviation)
g_i = Glicko2.g(r.opponent.g2deviation) 1.0 / (Math.sqrt(1.0 + 3.0 * deviation ** 2.0 / (Math::PI ** 2.0)))
e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation) end
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 # util func
new_deviation = 1.0 / (Math.sqrt( 1.0/(pre_deviation**2.0) + 1.0 / variance)) def Glicko2.E(rating, rating_opponent, deviation_opponent)
new_rating = 0.0 1.0 / (1.0 + Math.exp(-Glicko2.g(deviation_opponent)*(rating - rating_opponent)));
@results.each do |r| end
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 # Update rating based on current results list, and clear results.
clear_results def update
# util func
# copy new values # bail if no opponents set
@g2deviation = new_deviation if @results.empty?
@g2volatility = new_volatility @g2deviation = Math.sqrt(@g2deviation**2.0 + @g2volatility**2.0)
@g2rating = new_rating return
end 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 end
Regular → Executable
+3 -3
View File
@@ -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