icu_ratings 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -273,7 +273,9 @@ module ICU
273
273
  stable
274
274
  end
275
275
 
276
- def calculate_bonus # :nodoc:
276
+ def calculate_bonus(allow_new_bonus) # :nodoc:
277
+ # New in December 2012 - see http://ratings.icu.ie/articles/18 (Part 2).
278
+ return if !allow_new_bonus && @bonus == 0
277
279
  # Rounding is performed in places to emulate the older MSAccess implementation.
278
280
  return if @type != :rated || @kfactor <= 24 || @results.size <= 4 || @rating >= 2100
279
281
  change = rating_change
@@ -57,15 +57,20 @@ module ICU
57
57
  #
58
58
  # See ICU::RatedPlayer and ICU::RatedResult for more details.
59
59
  #
60
- # The <em>rate!</em> method takes some optional arguments to control the algoritm, for example:
60
+ # The <em>rate!</em> method takes some optional arguments to control the algorithm, for example:
61
61
  #
62
- # t.rate!(max_iterations2: 30)
62
+ # t.rate!(max_iterations2: 30, phase_2_bonuses: false) # these are the recommended options
63
63
  #
64
- # The complete set of current options is:
64
+ # The complete set of current options, with their defaults, is:
65
65
  #
66
66
  # * <em>max_iterations1</em>: the maximum number of re-estimation iterations before the bonus calculation (default <b>30</b>)
67
67
  # * <em>max_iterations2</em>: the maximum number of re-estimation iterations after the bonus calculation (default <b>1</b>)
68
68
  # * <em>threshold</em>: the maximum difference allowed between re-estimated ratings in a stabe solution (default <b>0.5</b>)
69
+ # * <em>phase_2_bonuses</em>: allow new bonuses in the second phase of calculation (default <b>true</b>)
70
+ #
71
+ # The defaults correspond to the original algorithm. However, it is recommended not to use the defaults for
72
+ # <em>max_iterations2</em> and <em>phase_2_bonuses</em> but to use <em>30</em> and <em>false</em> instead.
73
+ # See <a href="http://ratings.icu.ie/articles/18">this article</a> for more details.
69
74
  #
70
75
  # == Error Handling
71
76
  #
@@ -109,7 +114,8 @@ module ICU
109
114
  # Rate the tournament. Called after all players and results have been added.
110
115
  def rate!(opt={})
111
116
  max_iterations1 = opt[:max_iterations1] || 30
112
- max_iterations2 = opt[:max_iterations2] || 1
117
+ max_iterations2 = opt[:max_iterations2] || 1 # legacy default - see http://ratings.icu.ie/articles/18 (Part 1)
118
+ phase_2_bonuses = opt.has_key?(:phase_2_bonuses) ? opt[:phase_2_bonuses] : true # legacy default - see http://ratings.icu.ie/articles/18 (Part 2)
113
119
  threshold = opt[:threshold] || 0.5
114
120
  players.each { |p| p.init }
115
121
  @iterations1 = performance_ratings(max_iterations1, threshold)
@@ -117,7 +123,7 @@ module ICU
117
123
  if !no_bonuses && calculate_bonuses > 0
118
124
  players.each { |p| p.rate! }
119
125
  @iterations2 = performance_ratings(max_iterations2, threshold)
120
- calculate_bonuses
126
+ calculate_bonuses(phase_2_bonuses)
121
127
  else
122
128
  @iterations2 = 0
123
129
  end
@@ -164,8 +170,8 @@ module ICU
164
170
  end
165
171
 
166
172
  # Calculate bonuses for all players and return the number who got one.
167
- def calculate_bonuses
168
- @player.values.inject(0) { |t,p| t + (p.calculate_bonus ? 1 : 0) }
173
+ def calculate_bonuses(allow_new_bonus=true)
174
+ @player.values.inject(0) { |t,p| t + (p.calculate_bonus(allow_new_bonus) ? 1 : 0) }
169
175
  end
170
176
  end
171
177
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ICU
4
4
  class Ratings
5
- VERSION = "1.3.1"
5
+ VERSION = "1.4.0"
6
6
  end
7
7
  end
@@ -2001,11 +2001,9 @@ module ICU
2001
2001
  it "should produce inconsistent results with default settings" do
2002
2002
  @t.rate!
2003
2003
 
2004
- @p.new_rating.should be_within(0.5).of(713) # the original inconsistent calculation
2005
2004
  @p.new_rating.should == @p.performance
2006
2005
 
2007
2006
  @o1.bonus.should == 16
2008
- @o1.new_rating.should be_within(0.5).of(1013)
2009
2007
  @o2.bonus.should == 0
2010
2008
  @o3.bonus.should == 0
2011
2009
  @o4.bonus.should == 0
@@ -2021,13 +2019,31 @@ module ICU
2021
2019
  @t.iterations2.should == 1
2022
2020
  end
2023
2021
 
2024
- it "should produce consistent results somehow" do
2025
- pending "a fix to the algorithm (the number of iterations does not help)"
2026
-
2022
+ it "should produce inconsistent results even with more 2nd phase iterations" do
2027
2023
  @t.rate!(max_iterations2: 30)
2028
2024
 
2025
+ @p.new_rating.should == @p.performance
2026
+
2029
2027
  @o1.bonus.should == 16
2030
- @o1.new_rating.should be_within(0.5).of(1013)
2028
+ @o2.bonus.should == 0
2029
+ @o3.bonus.should == 0
2030
+ @o4.bonus.should == 0
2031
+ @o5.bonus.should == 0
2032
+ @o6.bonus.should == 0
2033
+
2034
+ ratings = [@o1, @o2, @o3, @o4, @o5, @o6].map { |o| o.bonus == 0 ? o.rating : o.new_rating }
2035
+
2036
+ performance = ratings.inject(0.0){ |m,r| m = m + r } / 6.0 - 400.0 / 3.0
2037
+ performance.should_not be_within(0.5).of(@p.new_rating)
2038
+
2039
+ @t.iterations1.should be > 1
2040
+ @t.iterations2.should be > 1
2041
+ end
2042
+
2043
+ it "should produce consistent results only when new bonuses in phase 2 are disallowed" do
2044
+ @t.rate!(max_iterations2: 30, phase_2_bonuses: false)
2045
+
2046
+ @o1.bonus.should == 0 # no bonus this time because it comes from 2nd phase
2031
2047
  @o2.bonus.should == 0
2032
2048
  @o3.bonus.should == 0
2033
2049
  @o4.bonus.should == 0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: icu_ratings
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
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-12-02 00:00:00.000000000 Z
12
+ date: 2012-12-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -111,7 +111,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
111
  version: '0'
112
112
  segments:
113
113
  - 0
114
- hash: 2472592709794521848
114
+ hash: 4520836644444600714
115
115
  required_rubygems_version: !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
@@ -120,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
120
  version: '0'
121
121
  segments:
122
122
  - 0
123
- hash: 2472592709794521848
123
+ hash: 4520836644444600714
124
124
  requirements: []
125
125
  rubyforge_project: icu_ratings
126
126
  rubygems_version: 1.8.23