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 CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- :patch: 7
2
+ :patch: 0
3
3
  :build:
4
4
  :major: 0
5
- :minor: 2
5
+ :minor: 3
@@ -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
 
@@ -7,7 +7,7 @@ module ICU
7
7
 
8
8
  == Parsing dates
9
9
 
10
- Parse strings into date objects, interpreting nn/nn/nnnn as dd/mm/yyyy. Raises exception on error.
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
@@ -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
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: 0.2.7
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Orr