icu_ratings 0.2.6 → 0.2.7

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: 6
2
+ :patch: 7
3
3
  :build:
4
4
  :major: 0
5
5
  :minor: 2
@@ -71,7 +71,7 @@ _rating_, _kfactor_ or _games_.
71
71
 
72
72
  p = t.add_player(5)
73
73
  p.type # :unrated
74
-
74
+
75
75
  == Invalid Combinations
76
76
 
77
77
  The above four types of players (_rated_, _provisional_, _unrated_, _foreign_) are the only
@@ -142,7 +142,7 @@ method.
142
142
  def expected_score
143
143
  @results.inject(0.0) { |e, r| e + (r.expected_score || 0.0) }
144
144
  end
145
-
145
+
146
146
  # After the tournament has been rated, this returns the tournament rating performance for
147
147
  # rated, unrated and foreign players. For provisional players it returns a weighted average
148
148
  # of the player's tournament performance and their previous games. For provisional and
@@ -150,7 +150,7 @@ method.
150
150
  def performance
151
151
  @performance
152
152
  end
153
-
153
+
154
154
  # Returns an array of the player's results (ICU::RatedResult) in round order.
155
155
  def results
156
156
  @results
@@ -160,12 +160,12 @@ method.
160
160
  def score
161
161
  @results.inject(0.0) { |e, r| e + r.score }
162
162
  end
163
-
163
+
164
164
  # Returns the type of player as a symbol: one of _rated_, _provisional_, _unrated_ or _foreign_.
165
165
  def type
166
166
  @type
167
167
  end
168
-
168
+
169
169
  def add_result(result) # :nodoc:
170
170
  raise "invalid result (#{result.class})" unless result.is_a? ICU::RatedResult
171
171
  raise "players cannot score results against themselves" if self == result.opponent
@@ -180,7 +180,7 @@ method.
180
180
  @results << result
181
181
  @results.sort!{ |a,b| a.round <=> b.round }
182
182
  end
183
-
183
+
184
184
  def rate! # :nodoc:
185
185
  @results.each { |r| r.rate!(self) }
186
186
  end
@@ -98,24 +98,24 @@ _expected_score_, _rating_change_.
98
98
  def round
99
99
  @round
100
100
  end
101
-
101
+
102
102
  # The player's opponent (an instance of ICU::RatedPlayer).
103
103
  def opponent
104
104
  @opponent
105
105
  end
106
-
106
+
107
107
  # The player's score in this game (1.0, 0.5 or 0.0).
108
108
  def score
109
109
  @score
110
110
  end
111
-
111
+
112
112
  # After the tournament has been rated, this returns the expected score (between 0 and 1)
113
113
  # for the player based on the rating difference with the opponent scaled by 400.
114
114
  # The standard Elo formula is used: 1/(1 + 10^(diff/400)).
115
115
  def expected_score
116
116
  @expected_score
117
117
  end
118
-
118
+
119
119
  # After the tournament has been rated, returns the change in rating due to this particular
120
120
  # result. Only for rated players (returns _nil_ for other types of players). Computed from
121
121
  # the difference between actual and expected scores multiplied by the player's K-factor.
@@ -123,7 +123,7 @@ _expected_score_, _rating_change_.
123
123
  def rating_change
124
124
  @rating_change
125
125
  end
126
-
126
+
127
127
  def rate!(player) # :nodoc:
128
128
  player_rating = player.full_rating? ? player.rating : player.performance
129
129
  opponent_rating = opponent.full_rating? ? opponent.rating : opponent.performance
@@ -10,11 +10,20 @@ ICU::RatedTournament object are created directly.
10
10
 
11
11
  t = ICU::RatedTournament.new
12
12
 
13
- They have one optional parameter called _:desc_ (short for description) the value of which can be
13
+ They have two optional parameters. One is called _desc_ (short for description) the value of which can be
14
14
  any object but will, if utilized, typically be the name of the tournament as a string.
15
15
 
16
- t = ICU::RatedTournament.new(:desc => "Irish Championships 2008")
17
- puts t.desc # "Irish Championships 2008"
16
+ t = ICU::RatedTournament.new(:desc => "Irish Championships 2010")
17
+ puts t.desc # "Irish Championships 2010"
18
+
19
+ The other optional parameter is _start_ for the start date. A Date object or a string that can be
20
+ parsed as a string can be used to set it. The European convention is preferred for dates like
21
+ "03/06/2013" (3rd of June, not 6th of March). Attempting to set an invalid date will raise an
22
+ exception.
23
+
24
+ t = ICU::RatedTournament.new(:start => "01/07/2010")
25
+ puts t.start.class # Date
26
+ puts t.start.to_s # "2010-07-01"
18
27
 
19
28
  == Rating Tournaments
20
29
 
@@ -55,6 +64,7 @@ to be caught and handled in some suitable place in your code.
55
64
 
56
65
  class RatedTournament
57
66
  attr_accessor :desc
67
+ attr_reader :start
58
68
 
59
69
  # Add a new player to the tournament. Returns the instance of ICU::RatedPlayer created.
60
70
  # See ICU::RatedPlayer for details.
@@ -94,14 +104,19 @@ to be caught and handled in some suitable place in your code.
94
104
  @player[num]
95
105
  end
96
106
 
107
+ # Set the start date. Raises exception on error.
108
+ def start=(date)
109
+ @start = ICU::Util.parsedate!(date)
110
+ end
111
+
97
112
  private
98
113
 
99
114
  # Create a new, empty (no players, no results) tournament.
100
115
  def initialize(opt={})
101
- [:desc].each { |atr| self.send("#{atr}=", opt[atr]) unless opt[atr].nil? }
116
+ [:desc, :start].each { |atr| self.send("#{atr}=", opt[atr]) unless opt[atr].nil? }
102
117
  @player = Hash.new
103
118
  end
104
-
119
+
105
120
  def performance_ratings
106
121
  @player.values.each { |p| p.init_performance }
107
122
  stable, count = false, 0
@@ -0,0 +1,35 @@
1
+ require 'date' # needed for 1.9.1
2
+
3
+ module ICU
4
+ class Util
5
+
6
+ =begin rdoc
7
+
8
+ == Parsing dates
9
+
10
+ Parse strings into date objects, interpreting nn/nn/nnnn as dd/mm/yyyy. Raises exception on error.
11
+
12
+ Util.parsedate!('1955-11-09') # => Date (1955-11-09)
13
+ Util.parsedate!('02/03/2009') # => Date (2009-03-02)
14
+ Util.parsedate!('02/23/2009') # => Date (2009-02-23)
15
+ Util.parsedate!('16th June 1986') # => Date (1986-06-16)
16
+ Util.parsedate!('not a date') # exception raised
17
+
18
+ Note that the parse method of the Date class behaves differently in Ruby 1.8.7 and 1.9.1.
19
+ In 1.8.7 it assumes American dates and will raise ArgumentError on "30/03/2003".
20
+ In 1.9.1 it assumes European dates and will raise ArgumentError on "03/30/2003".
21
+
22
+ =end
23
+
24
+ def self.parsedate!(date)
25
+ string = date.to_s.strip
26
+ raise "invalid date (#{date})" unless string.match(/[1-9]/)
27
+ string = [$3].concat($2.to_i > 12 ? [$1, $2] : [$2, $1]).join('-') if string.match(/^(\d{1,2}).(\d{1,2}).(\d{4})$/)
28
+ begin
29
+ Date.parse(string, true)
30
+ rescue
31
+ raise "invalid date (#{date})"
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/icu_ratings.rb CHANGED
@@ -1,8 +1,3 @@
1
1
  # :enddoc:
2
-
3
- icu_ratings_files = Array.new
4
- icu_ratings_files.concat %w{tournament player result}
5
-
6
2
  dir = File.dirname(__FILE__)
7
-
8
- icu_ratings_files.each { |file| require "#{dir}/icu_ratings/#{file}" }
3
+ %w{tournament player result util}.each { |f| require "#{dir}/icu_ratings/#{f}" }
data/spec/player_spec.rb CHANGED
@@ -10,7 +10,7 @@ module ICU
10
10
  @f = ICU::RatedPlayer.new(3, :rating => 2500)
11
11
  @u = ICU::RatedPlayer.new(4)
12
12
  end
13
-
13
+
14
14
  it "rated players have a rating and k-factor" do
15
15
  @r.num.should == 1
16
16
  @r.rating.should == 2000
@@ -19,7 +19,7 @@ module ICU
19
19
  @r.type.should == :rated
20
20
  @r.full_rating?.should be_true
21
21
  end
22
-
22
+
23
23
  it "provisionally rated players have a rating and number of games" do
24
24
  @p.num.should == 2
25
25
  @p.rating.should == 1500
@@ -28,7 +28,7 @@ module ICU
28
28
  @p.type.should == :provisional
29
29
  @p.full_rating?.should be_false
30
30
  end
31
-
31
+
32
32
  it "foreign players just have a rating" do
33
33
  @f.num.should == 3
34
34
  @f.rating.should == 2500
@@ -37,7 +37,7 @@ module ICU
37
37
  @f.type.should == :foreign
38
38
  @f.full_rating?.should be_true
39
39
  end
40
-
40
+
41
41
  it "unrated players just have nothing other than their number" do
42
42
  @u.num.should == 4
43
43
  @u.rating.should be_nil
@@ -46,7 +46,7 @@ module ICU
46
46
  @u.type.should == :unrated
47
47
  @u.full_rating?.should be_false
48
48
  end
49
-
49
+
50
50
  it "other combinations are invalid" do
51
51
  [
52
52
  { :games => 10 },
@@ -56,7 +56,7 @@ module ICU
56
56
  ].each { |opts| lambda { ICU::RatedPlayer.new(1, opts) }.should raise_error(/invalid.*combination/i) }
57
57
  end
58
58
  end
59
-
59
+
60
60
  context "#new - miscellaneous" do
61
61
  it "attribute values can be given by strings, even when space padded" do
62
62
  p = ICU::RatedPlayer.new(' 1 ', :kfactor => ' 10.0 ', :rating => ' 1000 ')
@@ -81,19 +81,19 @@ module ICU
81
81
  lambda { ICU::RatedPlayer.new(1, :rating => 0) }.should_not raise_error
82
82
  lambda { ICU::RatedPlayer.new(1, :rating => -1) }.should_not raise_error
83
83
  end
84
-
84
+
85
85
  it "ratings are stored as floats but can be specified with an integer" do
86
86
  ICU::RatedPlayer.new(1, :rating => 1234.5).rating.should == 1234.5
87
87
  ICU::RatedPlayer.new(1, :rating => 1234.0).rating.should == 1234
88
88
  ICU::RatedPlayer.new(1, :rating => 1234).rating.should == 1234
89
89
  end
90
-
90
+
91
91
  it "the number of games shoud not exceed 20" do
92
92
  lambda { ICU::RatedPlayer.new(1, :rating => 1000, :games => 19) }.should_not raise_error
93
93
  lambda { ICU::RatedPlayer.new(1, :rating => 1000, :games => 20) }.should raise_error
94
94
  lambda { ICU::RatedPlayer.new(1, :rating => 1000, :games => 21) }.should raise_error
95
95
  end
96
-
96
+
97
97
  it "a description, such as a name, but can be any object, is optional" do
98
98
  ICU::RatedPlayer.new(1, :desc => 'Fischer, Robert').desc.should == 'Fischer, Robert'
99
99
  ICU::RatedPlayer.new(1, :desc => 1).desc.should be_an_instance_of(Fixnum)
@@ -124,7 +124,7 @@ module ICU
124
124
  @p.results[1].should == @r2
125
125
  @p.results[2].should == @r3
126
126
  end
127
-
127
+
128
128
  it "the total score should stay consistent with results as they are added" do
129
129
  @p.score.should == 0.0
130
130
  @p.add_result(@r1)
@@ -135,18 +135,18 @@ module ICU
135
135
  @p.score.should == 1.5
136
136
  end
137
137
  end
138
-
138
+
139
139
  context "Rdoc examples" do
140
140
  before(:each) do
141
141
  @t = ICU::RatedTournament.new
142
142
  @t.add_player(1)
143
143
  end
144
-
144
+
145
145
  it "the same player number can't be added twice" do
146
146
  lambda { @t.add_player(2) }.should_not raise_error
147
147
  lambda { @t.add_player(2) }.should raise_error
148
148
  end
149
-
149
+
150
150
  it "parameters can be specified using strings, even with whitespace padding" do
151
151
  p = @t.add_player(" 0 ", :rating => " 2000.5 ", :kfactor => " 20.5 ")
152
152
  p.num.should == 0
@@ -159,11 +159,11 @@ module ICU
159
159
  p.games.should == 15
160
160
  p.games.should be_an_instance_of(Fixnum)
161
161
  end
162
-
162
+
163
163
  it "the games parameter should not exceed 20" do
164
164
  lambda { @t.add_player(2, :rating => 1500, :games => 20 ) }.should raise_error
165
165
  end
166
-
166
+
167
167
  it "adding different player types" do
168
168
  p = @t.add_player(3, :rating => 2000, :kfactor => 16)
169
169
  p.type.should == :rated
data/spec/result_spec.rb CHANGED
@@ -7,7 +7,7 @@ module ICU
7
7
  before(:all) do
8
8
  @o = ICU::RatedPlayer.new(2)
9
9
  end
10
-
10
+
11
11
  it "needs a round, opponent and score (win, loss or draw)" do
12
12
  r = ICU::RatedResult.new(1, @o, 'W')
13
13
  r.round.should == 1
@@ -20,7 +20,7 @@ module ICU
20
20
  before(:each) do
21
21
  @p = ICU::RatedPlayer.new(2)
22
22
  end
23
-
23
+
24
24
  it "round numbers must be positive" do
25
25
  lambda { ICU::RatedResult.new(0, 1, 'W') }.should raise_error(/invalid.*round number/i)
26
26
  lambda { ICU::RatedResult.new(-1, 1, 'W') }.should raise_error(/invalid.*round number/i)
@@ -43,7 +43,7 @@ module ICU
43
43
  before(:each) do
44
44
  @p = ICU::RatedPlayer.new(2)
45
45
  end
46
-
46
+
47
47
  it "should give the score from the opponent's perspective" do
48
48
  ICU::RatedResult.new(1, @p, 'W').opponents_score.should == 0.0
49
49
  ICU::RatedResult.new(1, @p, 'L').opponents_score.should == 1.0
@@ -69,7 +69,7 @@ module ICU
69
69
  (@r1 == @r5).should be_false
70
70
  end
71
71
  end
72
-
72
+
73
73
  context "Rdoc examples" do
74
74
  before(:each) do
75
75
  @t = ICU::RatedTournament.new
@@ -89,16 +89,16 @@ module ICU
89
89
  @t.player(10).results.size.should == 1
90
90
  @t.player(20).results.size.should == 1
91
91
  end
92
-
92
+
93
93
  it "adding results against other players in the same round will cause an exception" do
94
94
  lambda { @t.add_result(1, 10, 30, 'W') }.should raise_error(/inconsistent/i)
95
95
  lambda { @t.add_result(1, 10, 20, 'L') }.should raise_error(/inconsistent/i)
96
96
  end
97
-
97
+
98
98
  it "a player cannot have a result against himself/herself" do
99
99
  lambda { @t.add_result(2, 10, 10, 'D') }.should raise_error(/players.*cannot.*sel[fv]/i)
100
100
  end
101
-
101
+
102
102
  it "results are returned in score order irrespecive of the order they're added in" do
103
103
  @t.player(0).results.map{ |r| r.round }.join(',').should == "1,2,3,4"
104
104
  end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  require 'rubygems'
2
2
  require 'spec'
3
- require File.dirname(__FILE__) + '/../lib/icu_ratings'
3
+ require File.dirname(__FILE__) + '/../lib/icu_ratings'
@@ -9,6 +9,22 @@ module ICU
9
9
  ICU::RatedTournament.new(:desc => 1.0).desc.should be_an_instance_of(Float)
10
10
  ICU::RatedTournament.new.desc.should be_nil
11
11
  end
12
+
13
+ it "a tournament can have an optional start date" do
14
+ ICU::RatedTournament.new(:start => '2010-01-01').start.should be_a(Date)
15
+ ICU::RatedTournament.new(:start => '03/06/2013').start.to_s.should == '2013-06-03'
16
+ ICU::RatedTournament.new(:start => Date.parse('1955-11-09')).start.to_s.should == '1955-11-09'
17
+ ICU::RatedTournament.new.start.should be_nil
18
+ lambda { ICU::RatedTournament.new(:start => 'error') }.should raise_error
19
+ end
20
+
21
+ it "should have setters for the optional arguments" do
22
+ t = ICU::RatedTournament.new
23
+ t.desc=("Championship")
24
+ t.start=("2010-07-01")
25
+ t.desc.should == "Championship"
26
+ t.start.should == Date.parse("2010-07-01")
27
+ end
12
28
  end
13
29
 
14
30
  context "#players and #player" do
@@ -79,7 +95,7 @@ module ICU
79
95
  lambda { @t.add_result(1, @p1, @p3, 'W') }.should raise_error(/inconsistent/)
80
96
  lambda { @t.add_result(1, @p3, @p2, 'W') }.should raise_error(/inconsistent/)
81
97
  end
82
-
98
+
83
99
  it "players cannot have results against themselves" do
84
100
  lambda { @t.add_result(1, @p1, @p1, 'W') }.should raise_error(/against.*themsel(f|ves)/)
85
101
  end
@@ -191,7 +207,7 @@ module ICU
191
207
  p.new_rating.should be_close(new_rating, 0.5)
192
208
  end
193
209
  end
194
-
210
+
195
211
  it "tournament performances are not the same as ICU year-to-date performances" do
196
212
  [
197
213
  [1, 1867, 1761],
@@ -306,7 +322,7 @@ module ICU
306
322
  end
307
323
  end
308
324
  end
309
-
325
+
310
326
  it "foreign players should have tournament performnce ratings" do
311
327
  [
312
328
  [3, 2505.0],
@@ -571,7 +587,7 @@ module ICU
571
587
  end
572
588
  end
573
589
  end
574
-
590
+
575
591
  context "#rate - a made-up tournament that includes a group of unrateable players" do
576
592
  # 1 Orr, Mark 1350 2 2:W 3:W 0:-
577
593
  # 2 Coughlan, Anne 251 1 1:L 0:- 3:W
@@ -610,7 +626,7 @@ module ICU
610
626
  p.new_rating.should be_close(new_rating, 0.5)
611
627
  end
612
628
  end
613
-
629
+
614
630
  it "should not rate players that have no rateable games" do
615
631
  [4, 5, 6].each do |num|
616
632
  p = @t.player(num)
data/spec/util_spec.rb ADDED
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module ICU
4
+ describe Util do
5
+ context "#parsedate!" do
6
+ it "should return instances of class Date" do
7
+ Util.parsedate!('2001-01-01').should be_a(Date)
8
+ end
9
+
10
+ it "should parse standard dates" do
11
+ Util.parsedate!('2001-01-01').to_s.should == '2001-01-01'
12
+ Util.parsedate!('1955-11-09').to_s.should == '1955-11-09'
13
+ end
14
+
15
+ it "should handle US format" do
16
+ Util.parsedate!('03/30/2009').to_s.should == '2009-03-30'
17
+ end
18
+
19
+ it "should handle European format" do
20
+ Util.parsedate!('30/03/2009').to_s.should == '2009-03-30'
21
+ end
22
+
23
+ it "should prefer European format" do
24
+ Util.parsedate!('02/03/2009').to_s.should == '2009-03-02'
25
+ end
26
+
27
+ it "should handle US style when there's no alternative" do
28
+ Util.parsedate!('02/23/2009').to_s.should == '2009-02-23'
29
+ end
30
+
31
+ it "should handle single digits" do
32
+ Util.parsedate!('9/8/2006').to_s.should == '2006-08-09'
33
+ end
34
+
35
+ it "should handle names of months" do
36
+ Util.parsedate!('9th Nov 1955').to_s.should == '1955-11-09'
37
+ Util.parsedate!('16th June 1986').to_s.should == '1986-06-16'
38
+ end
39
+
40
+ it "should raise exception on error" do
41
+ lambda { Util.parsedate!('2010-13-32') }.should raise_error(/invalid date/)
42
+ end
43
+
44
+ it "should accept Date objects as well as strings" do
45
+ Util.parsedate!(Date.parse('2013-07-01')).should == Date.parse('2013-07-01')
46
+ end
47
+ end
48
+ end
49
+ 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.6
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Orr
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-16 00:00:00 +00:00
12
+ date: 2010-01-17 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -30,10 +30,12 @@ files:
30
30
  - lib/icu_ratings/player.rb
31
31
  - lib/icu_ratings/result.rb
32
32
  - lib/icu_ratings/tournament.rb
33
+ - lib/icu_ratings/util.rb
33
34
  - spec/player_spec.rb
34
35
  - spec/result_spec.rb
35
36
  - spec/spec_helper.rb
36
37
  - spec/tournament_spec.rb
38
+ - spec/util_spec.rb
37
39
  has_rdoc: true
38
40
  homepage: http://github.com/sanichi/icu_ratings
39
41
  licenses: []
@@ -67,3 +69,4 @@ test_files:
67
69
  - spec/result_spec.rb
68
70
  - spec/spec_helper.rb
69
71
  - spec/tournament_spec.rb
72
+ - spec/util_spec.rb