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.
- data/lib/icu_ratings/player.rb +3 -1
- data/lib/icu_ratings/tournament.rb +13 -7
- data/lib/icu_ratings/version.rb +1 -1
- data/spec/tournament_spec.rb +22 -6
- metadata +4 -4
data/lib/icu_ratings/player.rb
CHANGED
@@ -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
|
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
|
data/lib/icu_ratings/version.rb
CHANGED
data/spec/tournament_spec.rb
CHANGED
@@ -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
|
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
|
-
@
|
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.
|
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-
|
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:
|
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:
|
123
|
+
hash: 4520836644444600714
|
124
124
|
requirements: []
|
125
125
|
rubyforge_project: icu_ratings
|
126
126
|
rubygems_version: 1.8.23
|