icu_ratings 0.2.7 → 0.3.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.
- data/VERSION.yml +2 -2
- data/lib/icu_ratings/player.rb +37 -0
- data/lib/icu_ratings/tournament.rb +1 -0
- data/lib/icu_ratings/util.rb +18 -2
- data/spec/player_spec.rb +24 -0
- data/spec/tournament_spec.rb +26 -0
- data/spec/util_spec.rb +15 -0
- metadata +1 -1
data/VERSION.yml
CHANGED
data/lib/icu_ratings/player.rb
CHANGED
@@ -91,6 +91,29 @@ all these parameters can be specified using strings, even when padded with white
|
|
91
91
|
p.rating # 2000.5 (Float)
|
92
92
|
p.kfactor # 20.5 (Float)
|
93
93
|
|
94
|
+
== Calculation of K-factors
|
95
|
+
|
96
|
+
Rather than pre-calculating the value to set for a rated player's K-factor, the RatedPlayer class can itself
|
97
|
+
calculate K-factors if the releavant information is supplied. ICU K-factors depend not only on a player's
|
98
|
+
rating, but also on their age and experience. Therefore, supply a hash, instead of a numerical value, for the
|
99
|
+
_kfactor_ attribute with values set for date-of-birth (_dob_) and date joined (_joined_):
|
100
|
+
|
101
|
+
t = Tournament.new(:start => "2010-07-10")
|
102
|
+
p = t.add_player(1, :rating => 2225, :kfactor => { :dob => "1993-12-20", :joined => "2004-11-28" })
|
103
|
+
p.kfactor # 16.0
|
104
|
+
|
105
|
+
For this to work the tournament's optional start date must be set to enable the player's age and
|
106
|
+
experience at the start of the tournament be to calculated. The ICU K-factor rules are:
|
107
|
+
|
108
|
+
* 16 for players rated 2100 and over, otherwise
|
109
|
+
* 40 for players aged under 21, otherwise
|
110
|
+
* 32 for players who have been members for less than 8 years, otherwise
|
111
|
+
* 24
|
112
|
+
|
113
|
+
If you want to calculate K-factors accrding to some other, non-ICU scheme, then override the
|
114
|
+
static method _kfactor_ of the RatedPlayer class and pass in a hash of whatever key-value pairs
|
115
|
+
it requires as the value associated with _kfactor_ key in the _add_player_ method.
|
116
|
+
|
94
117
|
== Description Parameter
|
95
118
|
|
96
119
|
There is one other optional parameter, _desc_ (short for "description"). It has no effect on player
|
@@ -166,6 +189,20 @@ method.
|
|
166
189
|
@type
|
167
190
|
end
|
168
191
|
|
192
|
+
# Calculate a K-factor according to ICU rules.
|
193
|
+
def self.kfactor(args)
|
194
|
+
case
|
195
|
+
when args[:rating] >= 2100 then
|
196
|
+
16
|
197
|
+
when ICU::Util.age(args[:dob], args[:start]) < 21 then
|
198
|
+
40
|
199
|
+
when ICU::Util.age(args[:joined], args[:start]) < 8 then
|
200
|
+
32
|
201
|
+
else
|
202
|
+
24
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
169
206
|
def add_result(result) # :nodoc:
|
170
207
|
raise "invalid result (#{result.class})" unless result.is_a? ICU::RatedResult
|
171
208
|
raise "players cannot score results against themselves" if self == result.opponent
|
@@ -70,6 +70,7 @@ to be caught and handled in some suitable place in your code.
|
|
70
70
|
# See ICU::RatedPlayer for details.
|
71
71
|
def add_player(num, args={})
|
72
72
|
raise "player with number #{num} already exists" if @player[num]
|
73
|
+
args[:kfactor] = ICU::RatedPlayer.kfactor(args[:kfactor].merge({ :start => start, :rating => args[:rating] })) if args[:kfactor].is_a?(Hash)
|
73
74
|
@player[num] = ICU::RatedPlayer.new(num, args)
|
74
75
|
end
|
75
76
|
|
data/lib/icu_ratings/util.rb
CHANGED
@@ -7,7 +7,7 @@ module ICU
|
|
7
7
|
|
8
8
|
== Parsing dates
|
9
9
|
|
10
|
-
|
10
|
+
The method _parsedate!_ parses strings into date objects, interpreting nn/nn/nnnn as dd/mm/yyyy. It raises an exception on error.
|
11
11
|
|
12
12
|
Util.parsedate!('1955-11-09') # => Date (1955-11-09)
|
13
13
|
Util.parsedate!('02/03/2009') # => Date (2009-03-02)
|
@@ -19,9 +19,19 @@ Note that the parse method of the Date class behaves differently in Ruby 1.8.7 a
|
|
19
19
|
In 1.8.7 it assumes American dates and will raise ArgumentError on "30/03/2003".
|
20
20
|
In 1.9.1 it assumes European dates and will raise ArgumentError on "03/30/2003".
|
21
21
|
|
22
|
+
== Diffing dates
|
23
|
+
|
24
|
+
The method _age_ returns the difference of two dates:
|
25
|
+
|
26
|
+
Util.age(born, date) # age in years at the given date (Float)
|
27
|
+
Util.age(born) # age in years now (today)
|
28
|
+
|
29
|
+
Internally it uses _parsedate!_ so can throw a exception if an invalid date is supplied.
|
30
|
+
|
22
31
|
=end
|
23
32
|
|
24
33
|
def self.parsedate!(date)
|
34
|
+
return date.clone if date.is_a?(Date)
|
25
35
|
string = date.to_s.strip
|
26
36
|
raise "invalid date (#{date})" unless string.match(/[1-9]/)
|
27
37
|
string = [$3].concat($2.to_i > 12 ? [$1, $2] : [$2, $1]).join('-') if string.match(/^(\d{1,2}).(\d{1,2}).(\d{4})$/)
|
@@ -31,5 +41,11 @@ In 1.9.1 it assumes European dates and will raise ArgumentError on "03/30/2003".
|
|
31
41
|
raise "invalid date (#{date})"
|
32
42
|
end
|
33
43
|
end
|
44
|
+
|
45
|
+
def self.age(born, date=Date.today)
|
46
|
+
born = parsedate!(born)
|
47
|
+
date = parsedate!(date)
|
48
|
+
date.year - born.year + (date.yday - born.yday) / 366.0
|
49
|
+
end
|
34
50
|
end
|
35
|
-
end
|
51
|
+
end
|
data/spec/player_spec.rb
CHANGED
@@ -136,6 +136,30 @@ module ICU
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
|
+
context "calculation of K-factor" do
|
140
|
+
it "should return 16 for players 2100 and above" do
|
141
|
+
ICU::RatedPlayer.kfactor(:rating => 2101, :start => '2010-07-10', :dob => '1955-11-09', :joined => '1974-01-01').should == 16
|
142
|
+
ICU::RatedPlayer.kfactor(:rating => 2100, :start => '2010-07-10', :dob => '1955-11-09', :joined => '1974-01-01').should == 16
|
143
|
+
ICU::RatedPlayer.kfactor(:rating => 2099, :start => '2010-07-10', :dob => '1955-11-09', :joined => '1974-01-01').should_not == 16
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should otherwise return 40 for players aged under 21 at the start of the tournament" do
|
147
|
+
ICU::RatedPlayer.kfactor(:rating => 2000, :start => '2010-07-10', :dob => '1989-07-11', :joined => '1999-01-01').should == 40
|
148
|
+
ICU::RatedPlayer.kfactor(:rating => 2000, :start => '2010-07-10', :dob => '1989-07-10', :joined => '1999-01-01').should_not == 40
|
149
|
+
ICU::RatedPlayer.kfactor(:rating => 2000, :start => '2010-07-10', :dob => '1989-07-09', :joined => '1999-01-01').should_not == 40
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should otherwise return 32 for players with under 8 years experience at the start of the tournament" do
|
153
|
+
ICU::RatedPlayer.kfactor(:rating => 2000, :start => '2010-07-10', :dob => '1989-01-01', :joined => '2002-07-11').should == 32
|
154
|
+
ICU::RatedPlayer.kfactor(:rating => 2000, :start => '2010-07-10', :dob => '1989-01-01', :joined => '2002-07-10').should_not == 32
|
155
|
+
ICU::RatedPlayer.kfactor(:rating => 2000, :start => '2010-07-10', :dob => '1989-01-01', :joined => '2002-07-09').should_not == 32
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should otherwise return 24" do
|
159
|
+
ICU::RatedPlayer.kfactor(:rating => 2000, :start => '2010-07-10', :dob => '1989-01-01', :joined => '2002-01-01').should == 24
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
139
163
|
context "Rdoc examples" do
|
140
164
|
before(:each) do
|
141
165
|
@t = ICU::RatedTournament.new
|
data/spec/tournament_spec.rb
CHANGED
@@ -27,6 +27,32 @@ module ICU
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
context "#add_player and calculation of K-factor" do
|
31
|
+
before(:each) do
|
32
|
+
@t = ICU::RatedTournament.new(:start => "2010-07-10")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should set a K-factor of 16 for players with rating >= 2100" do
|
36
|
+
@p = @t.add_player(1, :rating => 2200, :kfactor => { :dob => "1955-11-09", :joined => "1976-09-01" })
|
37
|
+
@p.kfactor.should == 16
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should set a K-factor of 40 for players with rating < 2100 and age < 21" do
|
41
|
+
@p = @t.add_player(1, :rating => 2000, :kfactor => { :dob => "1995-01-10", :joined => "2009-09-01" })
|
42
|
+
@p.kfactor.should == 40
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should set a K-factor of 32 for players with rating < 2100, age >= 21 and experience < 8" do
|
46
|
+
@p = @t.add_player(1, :rating => 2000, :kfactor => { :dob => "1975-01-10", :joined => "2005-09-01" })
|
47
|
+
@p.kfactor.should == 32
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should set a K-factor of 24 for players with rating < 2100, age >= 21 and experience >= 8" do
|
51
|
+
@p = @t.add_player(1, :rating => 2000, :kfactor => { :dob => "1975-01-10", :joined => "1995-09-01" })
|
52
|
+
@p.kfactor.should == 24
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
30
56
|
context "#players and #player" do
|
31
57
|
before(:each) do
|
32
58
|
@t = ICU::RatedTournament.new
|
data/spec/util_spec.rb
CHANGED
@@ -45,5 +45,20 @@ module ICU
|
|
45
45
|
Util.parsedate!(Date.parse('2013-07-01')).should == Date.parse('2013-07-01')
|
46
46
|
end
|
47
47
|
end
|
48
|
+
|
49
|
+
context "#age" do
|
50
|
+
it "should return age in years" do
|
51
|
+
Util.age('2001-01-01', '2001-01-01').should == 0.0
|
52
|
+
Util.age('2001-01-01', '2002-01-01').should == 1.0
|
53
|
+
Util.age('2001-01-01', '2001-01-02').should be_close(1/365.0, 0.01)
|
54
|
+
Util.age('2001-01-01', '2001-02-01').should be_close(1/12.0, 0.01)
|
55
|
+
Util.age('1955-11-09', '2010-01-17').should be_close(54.2, 0.01)
|
56
|
+
Util.age('2001-01-01', '2000-01-01').should == -1.0
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should default second date to today" do
|
60
|
+
Util.age(Date.today).should == 0.0
|
61
|
+
end
|
62
|
+
end
|
48
63
|
end
|
49
64
|
end
|