icu_ratings 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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