Commits

Stephen Waits  committed ea4a250

add decay when results are empty and retab file

  • Participants
  • Parent commits a642ea9

Comments (0)

Files changed (2)

File rb/glicko2.rb

-#!/usr/bin/env ruby
+#!/usr/bin/env ruby -w
 
 #
 #
 # 
 class Glicko2
 
-	protected
+  protected
 
-		# Glicko2 reprensentation (Glicko2 uses different scales from Glicko)
-		attr_accessor :g2rating, :g2deviation, :g2volatility
-	
-		# a nested Class to hold a single result
-		Result = Struct.new(:opponent, :result)
+    # Glicko2 reprensentation (Glicko2 uses different scales from Glicko)
+    attr_accessor :g2rating, :g2deviation, :g2volatility
 
-	public
+    # a nested Class to hold a single result
+    Result = Struct.new(:opponent, :result)
 
-		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
-		end
+  public
 
-		include Comparable
+    WIN  = 1.0
+    LOSS = 0.0
+    DRAW = 0.5
 
-		# Comparison operator
-		def <=>(other)
-			self.rating <=> other.rating
-		end
+    # 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
 
-		# 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
+    include Comparable
 
-		# 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
+    # Comparison operator
+    def <=>(other)
+      self.rating <=> other.rating
+    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
+    # 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 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
+    # 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 volatility.
-		def volatility
-			@g2volatility
-		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 rating volatility.
-		def volatility=(v)
-			@g2volatility = v
-		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
 
-		# 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
+    # Get the current rating volatility.
+    def volatility
+      @g2volatility
+    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
+    # Set rating volatility.
+    def volatility=(v)
+      @g2volatility = v
+    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
+    # 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 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 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 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
-	
-		# 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
+    # 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
 
-			# 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
-			#@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
+    # 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
 
-			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 )
+    # util func
+    def Glicko2.g(deviation)
+      1.0 / (Math.sqrt(1.0 + 3.0 * deviation ** 2.0 / (Math::PI ** 2.0)))
+    end
 
-			# 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
+    # util func
+    def Glicko2.E(rating, rating_opponent, deviation_opponent)
+      1.0 / (1.0 + Math.exp(-Glicko2.g(deviation_opponent)*(rating - rating_opponent)));
+    end
 
-			# wipe our result lists
-			clear_results
+    # Update rating based on current results list, and clear results.
+    def update
+      # util func
 
-			# copy new values
-			@g2deviation  = new_deviation
-			@g2volatility = new_volatility
-			@g2rating     = new_rating
-		end
+      # 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
 

File rb/test_glicko2.rb

-#!/usr/bin/env ruby
+#!/usr/bin/env ruby -w
 
-require 'glicko2'
+require_relative 'glicko2'
 require 'test/unit'
 
 class TestGlicko2 < Test::Unit::TestCase
 		a.update()
 		
 		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