Permalink
Browse files

initial check-in

  • Loading branch information...
ps2 committed May 17, 2012
0 parents commit acf06057a7c36cecc5976050bf0a205171da640f
20 LICENSE
@@ -0,0 +1,20 @@
Copyright (c) 2012 Pete Schwamb
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,14 @@
= Whole History Rating
A system for ranking Go players, based on Rémi Coulom's Whole History Rating algorithm.
* Developed for use on GoShrine (http://goshrine.com)
== INSTALL:
* gem install whole-history-rating
== USE:
@@ -0,0 +1,19 @@
require 'rubygems'
module WholeHistoryRating
VERSION = "0.1.0"
STDOUT.sync = true
ROOT = File.expand_path(File.dirname(__FILE__))
%w[ base
player
game
player_day
].each do |lib|
require File.join(ROOT, 'whole_history_rating', lib)
end
end
@@ -0,0 +1,88 @@
#!/usr/bin/env ruby
require 'date'
module WholeHistoryRating
class Base
class UnstableRatingException < RuntimeError; end
attr_accessor :players, :games
def initialize(config = {})
@config = config
@config[:w2] ||= 300.0 # elo^2
@games = []
@players = {}
@start_date = nil
end
def print_ordered_ratings
players = @players.values.select {|p| p.days.count > 0}
players.sort_by { |p| p.days.last.gamma }.each_with_index do |p,idx|
if p.days.count > 0
puts "#{p.name} => #{p.days.map(&:elo)}"
end
end
end
def log_likelihood
score = 0.0
@players.values.each do |p|
unless p.days.empty?
score += p.log_likelihood
end
end
score
end
def player_by_name(name)
players[name] || players[name] = Player.new(name, @config)
end
def create_game(options)
if options['finished_at'].nil?
puts "Skipping (not finished) #{options.inspect}"
return nil
end
game_date = Date.parse(options['finished_at'])
@start_date ||= game_date
# Avoid self-played games (no info)
if options['w_name'] == options['b_name']
puts "Skipping (black name == white name ?) #{options.inspect}"
return nil
end
day_num = (game_date - @start_date).to_i
#puts "day num = #{day_num}"
white_player = player_by_name(options['w_name'])
white_player.id = options['w_id']
black_player = player_by_name(options['b_name'])
black_player.id = options['b_id']
game = Game.new(day_num, white_player, black_player, options['winner'], options['handicap'], options['extras'])
game.date = game_date
game
end
def add_game(game)
game.white_player.add_game(game)
game.black_player.add_game(game)
if game.bpd.nil?
puts "Bad game: #{options.inspect} -> #{game.inspect}"
end
@games << game
game
end
def run_one_iteration
players.each do |name,player|
player.run_one_newton_iteration
end
end
end
end
@@ -0,0 +1,67 @@
module WholeHistoryRating
class Game
attr_accessor :day, :date, :white_player, :black_player, :handicap, :winner, :wpd, :bpd, :extras
def initialize(day, white_player, black_player, winner, handicap, extras)
@day = day
@white_player = white_player
@black_player = black_player
@winner = winner
@extras = extras
@handicap = handicap || 0
@handicap_proc = handicap if handicap.is_a?(Proc)
end
def opponents_adjusted_gamma(player)
black_advantage = @handicap_proc ? @handicap_proc.call(self) : @handicap
#puts "black_advantage = #{black_advantage}"
if player == white_player
opponent_elo = bpd.elo + black_advantage
elsif player == black_player
opponent_elo = wpd.elo - black_advantage
else
raise "No opponent for #{player.inspect}, since they're not in this game: #{self.inspect}."
end
rval = 10**(opponent_elo/400.0)
if rval == 0 || rval.infinite? || rval.nan?
raise WHR::UnstableRatingException, "bad adjusted gamma: #{inspect}"
end
rval
end
def opponent(player)
if player == white_player
black_player
elsif player == black_player
white_player
end
end
def prediction_score
if white_win_probability == 0.5
0.5
else
((winner == "W" && white_win_probability > 0.5) || (winner == "B" && white_win_probability < 0.5)) ? 1.0 : 0.0
end
end
def inspect
"#{self}: W:#{white_player.name}(r=#{wpd ? wpd.r : '?'}) B:#{black_player.name}(r=#{bpd ? bpd.r : '?'}) winner = #{winner}, komi = #{@komi}, handicap = #{@handicap}"
end
#def likelihood
# winner == "W" ? white_win_probability : 1-white_win_probability
#end
# This is the Bradley-Terry Model
def white_win_probability
wpd.gamma/(wpd.gamma + opponents_adjusted_gamma(white_player))
end
def black_win_probability
bpd.gamma/(bpd.gamma + opponents_adjusted_gamma(black_player))
end
end
end
Oops, something went wrong.

0 comments on commit acf0605

Please sign in to comment.