icu_ratings 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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