whole_history_rating 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
 
2
2
  # Whole History Rating
3
3
 
4
- A system for ranking game players by skill, based on Rémi Coulom's Whole History Rating algorithm.
4
+ A system for ranking game players by skill, based on Rémi Coulom's [Whole History Rating](http://remi.coulom.free.fr/WHR/WHR.pdf) algorithm, with modifications to support handicaps.
5
5
 
6
- Developed for use on [GoShrine](http://goshrine.com), but the code is not go specific. It can support any two player game, as long as the outcome is a simple win/loss. An addition to the algorithm is support for handicaps.
6
+ Developed for use on [GoShrine](http://goshrine.com), but the code is not go specific. It can support any two player game, as long as the outcome is a simple win/loss.
7
7
 
8
8
  Installation
9
9
  ------------
10
10
 
11
- * gem install whole-history-rating
11
+ * gem install whole_history_rating
12
12
 
13
13
  Enjoy!
14
14
 
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ task :default => [:test_units]
5
+
6
+ desc "Run basic tests"
7
+ Rake::TestTask.new("test_units") { |t|
8
+ t.pattern = 'test/*_test.rb'
9
+ #t.verbose = true
10
+ t.warning = true
11
+ }
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
 
3
3
  module WholeHistoryRating
4
4
 
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.1"
6
6
 
7
7
  STDOUT.sync = true
8
8
 
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'date'
3
2
 
4
3
  module WholeHistoryRating
5
4
 
@@ -14,7 +13,6 @@ module WholeHistoryRating
14
13
  @config[:w2] ||= 300.0 # elo^2
15
14
  @games = []
16
15
  @players = {}
17
- @start_date = nil
18
16
  end
19
17
 
20
18
  def print_ordered_ratings
@@ -40,32 +38,22 @@ module WholeHistoryRating
40
38
  players[name] || players[name] = Player.new(name, @config)
41
39
  end
42
40
 
43
- def create_game(options)
44
- if options['finished_at'].nil?
45
- puts "Skipping (not finished) #{options.inspect}"
46
- return nil
47
- end
48
-
49
- game_date = Date.parse(options['finished_at'])
50
-
51
- @start_date ||= game_date
41
+ def ratings_for_player(name)
42
+ player = player_by_name(name)
43
+ player.days.map {|d| [d.day, d.elo.round, (d.uncertainty*100).round]}
44
+ end
52
45
 
46
+ def create_game(black, white, winner, time_step, handicap, extras = {})
47
+
53
48
  # Avoid self-played games (no info)
54
- if options['w_name'] == options['b_name']
55
- puts "Skipping (black name == white name ?) #{options.inspect}"
49
+ if black == white
50
+ raise "Invalid game (black player == white player)"
56
51
  return nil
57
52
  end
58
53
 
59
- day_num = (game_date - @start_date).to_i
60
-
61
- #puts "day num = #{day_num}"
62
-
63
- white_player = player_by_name(options['w_name'])
64
- white_player.id = options['w_id']
65
- black_player = player_by_name(options['b_name'])
66
- black_player.id = options['b_id']
67
- game = Game.new(day_num, white_player, black_player, options['winner'], options['handicap'], options['extras'])
68
- game.date = game_date
54
+ white_player = player_by_name(white)
55
+ black_player = player_by_name(black)
56
+ game = Game.new(black_player, white_player, winner, time_step, handicap, extras)
69
57
  game
70
58
  end
71
59
 
@@ -78,6 +66,13 @@ module WholeHistoryRating
78
66
  @games << game
79
67
  game
80
68
  end
69
+
70
+ def iterate(count)
71
+ count.times { run_one_iteration }
72
+ players.each do |name,player|
73
+ player.update_uncertainty
74
+ end
75
+ end
81
76
 
82
77
  def run_one_iteration
83
78
  players.each do |name,player|
@@ -1,15 +1,15 @@
1
1
  module WholeHistoryRating
2
2
  class Game
3
- attr_accessor :day, :date, :white_player, :black_player, :handicap, :winner, :wpd, :bpd, :extras
3
+ attr_accessor :day, :white_player, :black_player, :handicap, :winner, :wpd, :bpd, :extras
4
4
 
5
- def initialize(day, white_player, black_player, winner, handicap, extras)
6
- @day = day
7
- @white_player = white_player
8
- @black_player = black_player
5
+ def initialize(black, white, winner, time_step, handicap, extras)
6
+ @day = time_step
7
+ @white_player = white
8
+ @black_player = black
9
9
  @winner = winner
10
10
  @extras = extras
11
11
  @handicap = handicap || 0
12
- @handicap_proc = handicap if handicap.is_a?(Proc)
12
+ @handicap_proc = handicap.is_a?(Proc) ? handicap : nil
13
13
  end
14
14
 
15
15
  def opponents_adjusted_gamma(player)
@@ -64,4 +64,4 @@ module WholeHistoryRating
64
64
  end
65
65
 
66
66
  end
67
- end
67
+ end
@@ -234,7 +234,6 @@ module WholeHistoryRating
234
234
  def add_game(game)
235
235
  if days.last.nil? || days.last.day != game.day
236
236
  new_pday = PlayerDay.new(self, game.day)
237
- new_pday.date = game.date
238
237
  if days.empty?
239
238
  new_pday.is_first_day = true
240
239
  new_pday.gamma = 1
@@ -1,7 +1,7 @@
1
1
 
2
2
  module WholeHistoryRating
3
3
  class PlayerDay
4
- attr_accessor :won_games, :lost_games, :name, :kyudan, :day, :date, :player, :r, :is_first_day, :uncertainty
4
+ attr_accessor :won_games, :lost_games, :name, :day, :player, :r, :is_first_day, :uncertainty
5
5
  def initialize(player, day)
6
6
  @day = day
7
7
  @player = player
data/test/whr_test.rb ADDED
@@ -0,0 +1,69 @@
1
+ require 'test/unit'
2
+ require 'whole_history_rating'
3
+
4
+ class WholeHistoryRatingTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @whr = WholeHistoryRating::Base.new
8
+ end
9
+
10
+ def add_game(black, white, winner, time_step, handicap)
11
+ game = @whr.create_game(black, white, winner, time_step, handicap)
12
+ @whr.add_game(game)
13
+ end
14
+
15
+ def build_test_game_force_elo(white_elo, black_elo, handicap)
16
+ game = add_game("black", "white", "W", 1, handicap)
17
+ game.black_player.days[0].elo = black_elo
18
+ game.white_player.days[0].elo = white_elo
19
+ game
20
+ end
21
+
22
+ def test_even_game_between_equal_strength_players_should_have_white_winrate_of_50_percent
23
+ game = build_test_game_force_elo(500, 500, 0)
24
+ assert_in_delta 0.0001, 0.5, game.white_win_probability
25
+ end
26
+
27
+ def test_handicap_should_confer_advantage
28
+ game = build_test_game_force_elo(500, 500, 1)
29
+ assert game.black_win_probability > 0.5
30
+ end
31
+
32
+ def test_higher_rank_should_confer_advantage
33
+ game = build_test_game_force_elo(600, 500, 0)
34
+ assert game.white_win_probability > 0.5
35
+ end
36
+
37
+ def test_winrates_are_equal_for_same_elo_delta
38
+ game = build_test_game_force_elo(100, 200, 0)
39
+ game2 = build_test_game_force_elo(200, 300, 0)
40
+ assert_in_delta 0.0001, game.white_win_probability, game2.white_win_probability
41
+ end
42
+
43
+ def test_winrates_for_twice_as_strong_player
44
+ game = build_test_game_force_elo(100, 200, 0)
45
+ assert_in_delta 0.0001, 0.359935, game.white_win_probability
46
+ end
47
+
48
+ def test_winrates_should_be_inversely_proportional_with_unequal_ranks
49
+ game = build_test_game_force_elo(600, 500, 0)
50
+ assert_in_delta 0.0001, game.white_win_probability, 1-game.black_win_probability
51
+ end
52
+
53
+ def test_winrates_should_be_inversely_proportional_with_handicap
54
+ game = build_test_game_force_elo(500, 500, 4)
55
+ assert_in_delta 0.0001, game.white_win_probability, 1-game.black_win_probability
56
+ end
57
+
58
+ def test_output
59
+ add_game("shusaku", "shusai", "B", 1, 0)
60
+ add_game("shusaku", "shusai", "W", 2, 0)
61
+ add_game("shusaku", "shusai", "W", 3, 0)
62
+ add_game("shusaku", "shusai", "W", 4, 0)
63
+ add_game("shusaku", "shusai", "W", 4, 0)
64
+ @whr.iterate(50)
65
+ assert_equal [[1, -92, 71], [2, -94, 71], [3, -95, 71], [4, -96, 72]], @whr.ratings_for_player("shusaku")
66
+ assert_equal [[1, 92, 71], [2, 94, 71], [3, 95, 71], [4, 96, 72]], @whr.ratings_for_player("shusai")
67
+ end
68
+ end
69
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whole_history_rating
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-17 00:00:00.000000000Z
12
+ date: 2012-05-20 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: This gem provides a library and executables that take as input as set
15
15
  of games and output a set of skill rankings for the players of those games. The
@@ -23,11 +23,13 @@ extra_rdoc_files: []
23
23
  files:
24
24
  - LICENSE
25
25
  - README.md
26
+ - Rakefile
26
27
  - lib/whole_history_rating.rb
27
28
  - lib/whole_history_rating/base.rb
28
29
  - lib/whole_history_rating/game.rb
29
30
  - lib/whole_history_rating/player.rb
30
31
  - lib/whole_history_rating/player_day.rb
32
+ - test/whr_test.rb
31
33
  - whole_history_rating.gemspec
32
34
  homepage: http://github.com/goshrine/whole_history_rating
33
35
  licenses: []
@@ -49,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
51
  version: '0'
50
52
  requirements: []
51
53
  rubyforge_project:
52
- rubygems_version: 1.8.6
54
+ rubygems_version: 1.8.21
53
55
  signing_key:
54
56
  specification_version: 3
55
57
  summary: A pure ruby implementation of Remi Coulom's Whole-History Rating algorithm.