icu_ratings 0.2.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/.autotest +8 -0
- data/.gitignore +3 -0
- data/LICENCE +22 -0
- data/README.rdoc +64 -0
- data/Rakefile +44 -0
- data/VERSION.yml +5 -0
- data/lib/icu_ratings/player.rb +264 -0
- data/lib/icu_ratings/result.rb +172 -0
- data/lib/icu_ratings/tournament.rb +114 -0
- data/lib/icu_ratings.rb +6 -0
- data/spec/player_spec.rb +180 -0
- data/spec/result_spec.rb +106 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/tournament_spec.rb +618 -0
- metadata +72 -0
@@ -0,0 +1,618 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
module ICU
|
4
|
+
describe RatedTournament do
|
5
|
+
context "restrictions, or lack thereof, on attributes" do
|
6
|
+
it "a tournament can have an optional description, such as a name, but any object is allowed" do
|
7
|
+
ICU::RatedTournament.new(:desc => 'Irish Championship 2010').desc.should == 'Irish Championship 2010'
|
8
|
+
ICU::RatedTournament.new(:desc => 1.0).desc.should be_an_instance_of(Float)
|
9
|
+
ICU::RatedTournament.new.desc.should be_nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "#players and #player" do
|
14
|
+
before(:each) do
|
15
|
+
@t = ICU::RatedTournament.new
|
16
|
+
@t.add_player(2)
|
17
|
+
@t.add_player(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return the players in number-order" do
|
21
|
+
@t.players.size.should == 2
|
22
|
+
@t.players[0].num.should == 1
|
23
|
+
@t.players[1].num.should == 2
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return the player object with the matching number" do
|
27
|
+
@t.player(1).num.should == 1
|
28
|
+
@t.player(2).num.should == 2
|
29
|
+
@t.player(3).should be_nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "#add_result" do
|
34
|
+
before(:each) do
|
35
|
+
@t = ICU::RatedTournament.new
|
36
|
+
@p1 = @t.add_player(1)
|
37
|
+
@p2 = @t.add_player(2)
|
38
|
+
@p3 = @t.add_player(3)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be added to both players" do
|
42
|
+
@t.add_result(1, @p1, @p2, 'L')
|
43
|
+
@p1.results.size.should == 1
|
44
|
+
@p1.results[0].should == ICU::RatedResult.new(1, @p2, 'L')
|
45
|
+
@p2.results.size.should == 1
|
46
|
+
@p2.results[0].should == ICU::RatedResult.new(1, @p1, 'W')
|
47
|
+
@p3.results.size.should == 0
|
48
|
+
@t.add_result(2, @p3, @p2, 'W')
|
49
|
+
@p1.results.size.should == 1
|
50
|
+
@p2.results.size.should == 2
|
51
|
+
@p2.results[0].should == ICU::RatedResult.new(1, @p1, 'W')
|
52
|
+
@p2.results[1].should == ICU::RatedResult.new(2, @p3, 'L')
|
53
|
+
@p3.results.size.should == 1
|
54
|
+
@p3.results[0].should == ICU::RatedResult.new(2, @p2, 'W')
|
55
|
+
end
|
56
|
+
|
57
|
+
it "player objects or numbers can be used" do
|
58
|
+
lambda { @t.add_result(1, @p1, @p2, 'L') }.should_not raise_error
|
59
|
+
lambda { @t.add_result(2, 1, 2, 'W') }.should_not raise_error
|
60
|
+
end
|
61
|
+
|
62
|
+
it "the player numbers should exist already in the tournament" do
|
63
|
+
lambda { @t.add_result(2, 1, 4, 'W') }.should raise_error(/player number.*4/)
|
64
|
+
lambda { @t.add_result(2, 5, 2, 'W') }.should raise_error(/player number.*5/)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "adding precisely the same result more than once is okay and changes nothing" do
|
68
|
+
@t.add_result(1, @p1, @p2, 'L')
|
69
|
+
@t.add_result(1, @p1, @p2, 'L')
|
70
|
+
@t.add_result(1, @p2, @p1, 'W')
|
71
|
+
@p1.results.size.should == 1
|
72
|
+
@p2.results.size.should == 1
|
73
|
+
end
|
74
|
+
|
75
|
+
it "a player cannot have two different results in the same round" do
|
76
|
+
@t.add_result(1, @p1, @p2, 'L')
|
77
|
+
lambda { @t.add_result(1, @p1, @p2, 'W') }.should raise_error(/inconsistent/)
|
78
|
+
lambda { @t.add_result(1, @p1, @p3, 'W') }.should raise_error(/inconsistent/)
|
79
|
+
lambda { @t.add_result(1, @p3, @p2, 'W') }.should raise_error(/inconsistent/)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "#rate - corner case - tournament is empy" do
|
84
|
+
it "should not throw an exception" do
|
85
|
+
@t = ICU::RatedTournament.new
|
86
|
+
lambda { @t.rate! }.should_not raise_error
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "#rate - all players rated, artificial example" do
|
91
|
+
before(:each) do
|
92
|
+
@t = ICU::RatedTournament.new
|
93
|
+
(1..4).each do |num|
|
94
|
+
@t.add_player(num, :kfactor => 10 * num, :rating => 2200 - 100 * (num - 1))
|
95
|
+
end
|
96
|
+
@t.add_result(1, 1, 2, 'W')
|
97
|
+
@t.add_result(1, 3, 4, 'W')
|
98
|
+
@t.add_result(2, 1, 3, 'W')
|
99
|
+
@t.add_result(2, 2, 4, 'W')
|
100
|
+
@t.add_result(3, 1, 4, 'W')
|
101
|
+
@t.add_result(3, 2, 3, 'W')
|
102
|
+
end
|
103
|
+
|
104
|
+
it "before the tournament is rated" do
|
105
|
+
(1..4).each do |num|
|
106
|
+
p = @t.player(num)
|
107
|
+
p.expected_score.should == 0.0
|
108
|
+
p.rating_change.should == 0.0
|
109
|
+
p.new_rating.should == p.rating
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it "after the tournament is rated" do
|
114
|
+
@t.rate!
|
115
|
+
|
116
|
+
@t.player(1).expected_score.should be_close(2.249, 0.001)
|
117
|
+
@t.player(2).expected_score.should be_close(1.760, 0.001)
|
118
|
+
@t.player(3).expected_score.should be_close(1.240, 0.001)
|
119
|
+
@t.player(4).expected_score.should be_close(0.751, 0.001)
|
120
|
+
|
121
|
+
@t.player(1).rating_change.should be_close(7.51, 0.01)
|
122
|
+
@t.player(2).rating_change.should be_close(4.81, 0.01)
|
123
|
+
@t.player(3).rating_change.should be_close(-7.21, 0.01)
|
124
|
+
@t.player(4).rating_change.should be_close(-30.05, 0.01)
|
125
|
+
|
126
|
+
@t.player(1).new_rating.should be_close(2207.5, 0.1)
|
127
|
+
@t.player(2).new_rating.should be_close(2104.8, 0.1)
|
128
|
+
@t.player(3).new_rating.should be_close(1992.8, 0.1)
|
129
|
+
@t.player(4).new_rating.should be_close(1870.0, 0.1)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "#rate - all players rated, real swiss example" do
|
134
|
+
# 1 Killane, Jack 740 4 2:D 3:W 4:W 5:D 6:W
|
135
|
+
# 2 Cassidy, Paul 194 4 1:D 3:D 4:W 5:W 6:W
|
136
|
+
# 3 King, Melvyn 10214 3.5 1:L 2:D 4:W 5:W 6:W
|
137
|
+
# 4 Dempsey, Denis 322 1.5 1:L 2:L 3:L 5:D 6:W
|
138
|
+
# 5 Thomson, Andrew 1613 1 1:D 2:L 3:L 4:D 6:L
|
139
|
+
# 6 Coveney, Maurice 255 1 1:L 2:L 3:L 4:L 5:W
|
140
|
+
# see also http://en.wikipedia.org/wiki/Round-robin_tournament#Scheduling_algorithm
|
141
|
+
before(:each) do
|
142
|
+
@t = ICU::RatedTournament.new(:desc => 'Irish Veterans 2008')
|
143
|
+
|
144
|
+
@t.add_player(1, :kfactor => 24, :rating => 1852, :desc => 'Jack Killane (740)')
|
145
|
+
@t.add_player(2, :kfactor => 24, :rating => 1845, :desc => 'Paul Cassidy (194)')
|
146
|
+
@t.add_player(3, :kfactor => 32, :rating => 1632, :desc => 'Melvin King (10214)')
|
147
|
+
@t.add_player(4, :kfactor => 24, :rating => 1337, :desc => 'Dennis Dempsey (322)')
|
148
|
+
@t.add_player(5, :kfactor => 24, :rating => 1493, :desc => 'Andrew Thomson (1613)')
|
149
|
+
@t.add_player(6, :kfactor => 24, :rating => 1297, :desc => 'Maurice J. Coveney (255)')
|
150
|
+
|
151
|
+
@t.add_result(1, 1, 2, 'D')
|
152
|
+
@t.add_result(2, 1, 3, 'W')
|
153
|
+
@t.add_result(3, 1, 4, 'W')
|
154
|
+
@t.add_result(4, 1, 5, 'D')
|
155
|
+
@t.add_result(5, 1, 6, 'W')
|
156
|
+
|
157
|
+
@t.add_result(4, 2, 3, 'D')
|
158
|
+
@t.add_result(2, 2, 4, 'W')
|
159
|
+
@t.add_result(5, 2, 5, 'W')
|
160
|
+
@t.add_result(3, 2, 6, 'W')
|
161
|
+
|
162
|
+
@t.add_result(5, 3, 4, 'W')
|
163
|
+
@t.add_result(3, 3, 5, 'W')
|
164
|
+
@t.add_result(1, 3, 6, 'W')
|
165
|
+
|
166
|
+
@t.add_result(1, 4, 5, 'D')
|
167
|
+
@t.add_result(4, 4, 6, 'W')
|
168
|
+
|
169
|
+
@t.add_result(2, 5, 6, 'L')
|
170
|
+
|
171
|
+
@t.rate!
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should get same results as ICU rating database" do
|
175
|
+
[
|
176
|
+
[1, 4.089, 1850],
|
177
|
+
[2, 4.055, 1844],
|
178
|
+
[3, 2.855, 1653],
|
179
|
+
[4, 1.101, 1347],
|
180
|
+
[5, 2.005, 1469],
|
181
|
+
[6, 0.894, 1300],
|
182
|
+
].each do |item|
|
183
|
+
num, expected_score, new_rating = item
|
184
|
+
p = @t.player(num)
|
185
|
+
p.expected_score.should be_close(expected_score, 0.001)
|
186
|
+
p.new_rating.should be_close(new_rating, 0.5)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it "tournament performances are not the same as ICU year-to-date performances" do
|
191
|
+
[
|
192
|
+
[1, 1867, 1761],
|
193
|
+
[2, 1803, 1762],
|
194
|
+
[3, 1650, 1725],
|
195
|
+
[4, 1338, 1464],
|
196
|
+
[5, 1358, 1353],
|
197
|
+
[6, 1289, 1392],
|
198
|
+
].each do |item|
|
199
|
+
num, ytd_performance, tournament_performance = item
|
200
|
+
p = @t.player(num)
|
201
|
+
p.performance.should_not be_close(ytd_performance, 0.5)
|
202
|
+
p.performance.should be_close(tournament_performance, 0.5)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context "#rate - all players rated, real match example" do
|
208
|
+
before(:each) do
|
209
|
+
@t = ICU::RatedTournament.new(:desc => "Dempsey-O'Raghallaigh Match 2009")
|
210
|
+
@t.add_player(1, :kfactor => 24, :rating => 1347, :desc => 'Dennis Dempsey (322)')
|
211
|
+
@t.add_player(2, :kfactor => 32, :rating => 1303, :desc => "Paul O'Raghallaigh (10219)")
|
212
|
+
@t.add_result(1, 1, 2, 'W')
|
213
|
+
@t.add_result(2, 1, 2, 'W')
|
214
|
+
@t.add_result(3, 1, 2, 'W')
|
215
|
+
@t.rate!
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should get same results as ICU rating database" do
|
219
|
+
@t.player(1).expected_score.should be_close(1.689, 0.001)
|
220
|
+
@t.player(2).expected_score.should be_close(1.311, 0.001)
|
221
|
+
@t.player(1).new_rating.should be_close(1378, 0.5)
|
222
|
+
@t.player(2).new_rating.should be_close(1261, 0.5)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context "#rate - typical foreign tournament" do
|
227
|
+
# Player,456,Fox,Anthony
|
228
|
+
# 1,0,B,Berg,Antonin,1877,,CZE
|
229
|
+
# 2,0,W,Zanikov,Konstantin,1972,,CZE
|
230
|
+
# 3,=,B,Pozzi,Claudio,1931,,ITA
|
231
|
+
# 4,=,W,Horak,Karel,2105,,CZE
|
232
|
+
# 5,=,B,Chromovsky,Hynek,1808,,CZE
|
233
|
+
# 6,1,W,Hulin,Petr,1975,,CZE
|
234
|
+
# 7,=,B,Jancarik,Joel,1990,,CZE
|
235
|
+
# 8,1,W,Andersen,Carsten,1982,,DEN
|
236
|
+
# 9,=,B,Gooding,Ian,2107,,ENG
|
237
|
+
#
|
238
|
+
# Player,159,Cafolla,Peter
|
239
|
+
# 1,0,W,Dolezal,Radoslav,2423,IM,CZE
|
240
|
+
# 2,1,B,Pozzi,Claudio,1931,,ITA
|
241
|
+
# 3,0,W,Srirhanzl,Radek,1621,,CZE
|
242
|
+
# 4,1,B,Milfort,Vaclav,1779,,CZE
|
243
|
+
# 5,0,W,Srba,Milan,2209,,CZE
|
244
|
+
# 6,1,B,Hajek,Jaroslav,1967,,CZE
|
245
|
+
# 7,=,B,Soucek,Milan,2151,,CZE
|
246
|
+
# 8,=,W,Babar,Michael,2145,FM,GER
|
247
|
+
# 9,0,B,Bartos,Jan,2227,FM,CZE
|
248
|
+
before(:all) do
|
249
|
+
@t = ICU::RatedTournament.new(:desc => 'Prague Open 2008')
|
250
|
+
|
251
|
+
@t.add_player(1, :rating => 2105, :kfactor => 16, :desc => 'Fox, Anthony (456)')
|
252
|
+
@t.add_player(2, :rating => 1976, :kfactor => 24, :desc => 'Cafolla, Peter (159)')
|
253
|
+
@t.add_player(3, :rating => 1877, :desc => 'Berg, Antonin')
|
254
|
+
@t.add_player(4, :rating => 1972, :desc => 'Zanikov, Konstantin')
|
255
|
+
@t.add_player(5, :rating => 1931, :desc => 'Pozzi, Claudio')
|
256
|
+
@t.add_player(6, :rating => 2105, :desc => 'Horak, Karel')
|
257
|
+
@t.add_player(7, :rating => 1808, :desc => 'Chromovsky, Hynek')
|
258
|
+
@t.add_player(8, :rating => 1975, :desc => 'Hulin, Petr')
|
259
|
+
@t.add_player(9, :rating => 1990, :desc => 'Jancarik, Joel')
|
260
|
+
@t.add_player(10, :rating => 1982, :desc => 'Andersen, Carsten')
|
261
|
+
@t.add_player(11, :rating => 2107, :desc => 'Gooding, Ian')
|
262
|
+
@t.add_player(12, :rating => 2423, :desc => 'Dolezal, Radoslav')
|
263
|
+
@t.add_player(14, :rating => 1621, :desc => 'Srirhanzl, Radek')
|
264
|
+
@t.add_player(15, :rating => 1779, :desc => 'Milfort, Vaclav')
|
265
|
+
@t.add_player(16, :rating => 2209, :desc => 'Srba, Milan')
|
266
|
+
@t.add_player(17, :rating => 1967, :desc => 'Hajek, Jaroslav')
|
267
|
+
@t.add_player(18, :rating => 2151, :desc => 'Soucek, Milan')
|
268
|
+
@t.add_player(19, :rating => 2187, :desc => 'Babar, Michael')
|
269
|
+
@t.add_player(20, :rating => 2227, :desc => 'Bartos, Jan')
|
270
|
+
|
271
|
+
@t.add_result(1, 1, 3, 'L')
|
272
|
+
@t.add_result(2, 1, 4, 'L')
|
273
|
+
@t.add_result(3, 1, 5, 'D')
|
274
|
+
@t.add_result(4, 1, 6, 'D')
|
275
|
+
@t.add_result(5, 1, 7, 'D')
|
276
|
+
@t.add_result(6, 1, 8, 'W')
|
277
|
+
@t.add_result(7, 1, 9, 'D')
|
278
|
+
@t.add_result(8, 1, 10, 'W')
|
279
|
+
@t.add_result(9, 1, 11, 'D')
|
280
|
+
|
281
|
+
@t.add_result(1, 2, 12, 'L')
|
282
|
+
@t.add_result(2, 2, 5, 'W')
|
283
|
+
@t.add_result(3, 2, 14, 'L')
|
284
|
+
@t.add_result(4, 2, 15, 'W')
|
285
|
+
@t.add_result(5, 2, 16, 'L')
|
286
|
+
@t.add_result(6, 2, 17, 'W')
|
287
|
+
@t.add_result(7, 2, 18, 'D')
|
288
|
+
@t.add_result(8, 2, 19, 'D')
|
289
|
+
@t.add_result(9, 2, 20, 'L')
|
290
|
+
|
291
|
+
@t.rate!
|
292
|
+
end
|
293
|
+
|
294
|
+
it "foreign players should have no rating change but non-zero expected scores" do
|
295
|
+
(3..20).each do |num|
|
296
|
+
unless num == 13
|
297
|
+
p = @t.player(num)
|
298
|
+
p.expected_score.should_not == 0.0
|
299
|
+
p.rating_change.should == 0.0
|
300
|
+
p.new_rating.should == p.rating
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
it "foreign players should have tournament performnce ratings" do
|
306
|
+
[
|
307
|
+
[3, 2505.0],
|
308
|
+
[4, 2505.0],
|
309
|
+
[5, 1840.5],
|
310
|
+
[6, 2105.0],
|
311
|
+
[7, 2105.0],
|
312
|
+
[8, 1705.0],
|
313
|
+
[9, 2105.0],
|
314
|
+
[10, 1705.0],
|
315
|
+
[11, 2105.0],
|
316
|
+
[12, 2376.0],
|
317
|
+
[14, 2376.0],
|
318
|
+
[15, 1576.0],
|
319
|
+
[16, 2376.0],
|
320
|
+
[17, 1576.0],
|
321
|
+
[18, 1976.0],
|
322
|
+
[19, 1976.0],
|
323
|
+
[20, 2376.0],
|
324
|
+
].each do |item|
|
325
|
+
num, performance = item
|
326
|
+
p = @t.player(num)
|
327
|
+
p.performance.should == performance
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should get the same results as the ICU database for the Irish players" do
|
332
|
+
af = @t.player(1)
|
333
|
+
pc = @t.player(2)
|
334
|
+
|
335
|
+
af.score.should == 4.5
|
336
|
+
af.expected_score.should be_close(6.054, 0.001)
|
337
|
+
af.new_rating.should be_close(2080, 0.5)
|
338
|
+
|
339
|
+
pc.score.should == 4.0
|
340
|
+
pc.expected_score.should be_close(3.685, 0.001)
|
341
|
+
pc.new_rating.should be_close(1984, 0.5)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
context "#rate - a tournament with some provisionally rated players and some players without results" do
|
346
|
+
before(:all) do
|
347
|
+
@t = ICU::RatedTournament.new(:desc => "Malahide CC's 2007")
|
348
|
+
@t.add_player(1, :rating => 1751, :kfactor => 32, :desc => 'DALY, JUSTIN')
|
349
|
+
@t.add_player(2, :rating => 1716, :kfactor => 32, :desc => 'REILLY, PAUL')
|
350
|
+
@t.add_player(3, :rating => 1583, :kfactor => 24, :desc => "O'SULLIVAN, TOM")
|
351
|
+
@t.add_player(4, :rating => 1659, :kfactor => 24, :desc => 'McGRANE, KEVIN')
|
352
|
+
@t.add_player(5, :rating => 1647, :kfactor => 24, :desc => 'BISSETT, VINCENT')
|
353
|
+
@t.add_player(6, :rating => 1442, :kfactor => 24, :desc => 'ROCHE, CORMAC')
|
354
|
+
@t.add_player(7, :rating => 1665, :kfactor => 24, :desc => 'BUCKLEY, GERARD')
|
355
|
+
@t.add_player(8, :rating => 1601, :kfactor => 24, :desc => 'WHALLEY, ANTHONY')
|
356
|
+
@t.add_player(9, :rating => 1283, :kfactor => 24, :desc => 'SCOTT, SHAY')
|
357
|
+
@t.add_player(10, :rating => 1469, :kfactor => 24, :desc => "O'CONGHAILE, DIARMUID")
|
358
|
+
@t.add_player(11, :rating => 1376, :kfactor => 24, :desc => 'SHEARAN, JOHN')
|
359
|
+
@t.add_player(12, :rating => 1096, :games => 11, :desc => 'MOLDT, TOBIAS')
|
360
|
+
@t.add_player(13, :rating => 1317, :kfactor => 24, :desc => 'MARTIN, SEAMUS')
|
361
|
+
@t.add_player(14, :rating => 1613, :kfactor => 24, :desc => 'FREDJ, NIZAR')
|
362
|
+
@t.add_player(15, :rating => 1554, :kfactor => 24, :desc => 'REID, BRIAN')
|
363
|
+
@t.add_player(16, :rating => 1016, :games => 7, :desc => 'DONNELLY, VINCENT')
|
364
|
+
|
365
|
+
# 1 DALY, JUSTIN 4.5 10:W 14:W 5:D 7:D 2:W 4:D
|
366
|
+
@t.add_result(1, 1, 10, 'W')
|
367
|
+
@t.add_result(2, 1, 14, 'W')
|
368
|
+
@t.add_result(3, 1, 5, 'D')
|
369
|
+
@t.add_result(4, 1, 7, 'D')
|
370
|
+
@t.add_result(5, 1, 2, 'W')
|
371
|
+
@t.add_result(6, 1, 4, 'D')
|
372
|
+
|
373
|
+
# 2 REILLY, PAUL 4.5 13:W 7:D 3:W 5:W 1:L 8:W
|
374
|
+
@t.add_result(1, 2, 13, 'W')
|
375
|
+
@t.add_result(2, 2, 7, 'D')
|
376
|
+
@t.add_result(3, 2, 3, 'W')
|
377
|
+
@t.add_result(4, 2, 5, 'W')
|
378
|
+
@t.add_result(5, 2, 1, 'L')
|
379
|
+
@t.add_result(6, 2, 8, 'W')
|
380
|
+
|
381
|
+
# 3 O'SULLIVAN, TOM 4 16:+ 4:- 2:L 9:W 12:W 5:W
|
382
|
+
@t.add_result(3, 3, 2, 'L')
|
383
|
+
@t.add_result(4, 3, 9, 'W')
|
384
|
+
@t.add_result(5, 3, 12, 'W')
|
385
|
+
@t.add_result(6, 3, 5, 'W')
|
386
|
+
|
387
|
+
# 4 McGRANE, KEVIN 4 11:W 3:- 7:D 12:W 8:W 1:D
|
388
|
+
@t.add_result(1, 4, 11, 'W')
|
389
|
+
@t.add_result(3, 4, 7, 'D')
|
390
|
+
@t.add_result(4, 4, 12, 'W')
|
391
|
+
@t.add_result(5, 4, 8, 'W')
|
392
|
+
@t.add_result(6, 4, 1, 'D')
|
393
|
+
|
394
|
+
# 5 BISSETT, VINCENT 3.5 17:+ 8:W 1:D 2:L 7:W 3:L
|
395
|
+
@t.add_result(2, 5, 8, 'W')
|
396
|
+
@t.add_result(3, 5, 1, 'D')
|
397
|
+
@t.add_result(4, 5, 2, 'L')
|
398
|
+
@t.add_result(5, 5, 7, 'W')
|
399
|
+
@t.add_result(6, 5, 3, 'L')
|
400
|
+
|
401
|
+
# 6 ROCHE, CORMAC 3.5 7:L 13:W 8:L 10:W 9:D 12:W
|
402
|
+
@t.add_result(1, 6, 7, 'L')
|
403
|
+
@t.add_result(2, 6, 13, 'W')
|
404
|
+
@t.add_result(3, 6, 8, 'L')
|
405
|
+
@t.add_result(4, 6, 10, 'W')
|
406
|
+
@t.add_result(5, 6, 9, 'D')
|
407
|
+
@t.add_result(6, 6, 12, 'W')
|
408
|
+
|
409
|
+
# 7 BUCKLEY, GERARD 3 6:W 2:D 4:D 1:D 5:L 11:D
|
410
|
+
@t.add_result(1, 7, 6, 'W')
|
411
|
+
@t.add_result(2, 7, 2, 'D')
|
412
|
+
@t.add_result(3, 7, 4, 'D')
|
413
|
+
@t.add_result(4, 7, 1, 'D')
|
414
|
+
@t.add_result(5, 7, 5, 'L')
|
415
|
+
@t.add_result(6, 7, 11, 'D')
|
416
|
+
|
417
|
+
# 8 WHALLEY, ANTHONY 3 12:W 5:L 6:W 14:+ 4:L 2:L
|
418
|
+
@t.add_result(1, 8, 12, 'W')
|
419
|
+
@t.add_result(2, 8, 5, 'L')
|
420
|
+
@t.add_result(3, 8, 6, 'W')
|
421
|
+
@t.add_result(5, 8, 4, 'L')
|
422
|
+
@t.add_result(6, 8, 2, 'L')
|
423
|
+
|
424
|
+
# 9 SCOTT, SHAY 3 14:L 11:D 13:W 3:L 6:D 0:W
|
425
|
+
@t.add_result(1, 9, 14, 'L')
|
426
|
+
@t.add_result(2, 9, 11, 'D')
|
427
|
+
@t.add_result(3, 9, 13, 'W')
|
428
|
+
@t.add_result(4, 9, 3, 'L')
|
429
|
+
@t.add_result(5, 9, 6, 'D')
|
430
|
+
|
431
|
+
# 10 O'CONGHAILE, DIARMUID 2.5 1:L 16:W 14:- 6:L 11:D 13:W
|
432
|
+
@t.add_result(1, 10, 1, 'L')
|
433
|
+
@t.add_result(2, 10, 16, 'W')
|
434
|
+
@t.add_result(4, 10, 6, 'L')
|
435
|
+
@t.add_result(5, 10, 11, 'D')
|
436
|
+
@t.add_result(6, 10, 13, 'W')
|
437
|
+
|
438
|
+
# 11 SHEARAN, JOHN 2.5 4:L 9:D 12:L 13:W 10:D 7:D
|
439
|
+
@t.add_result(1, 11, 4, 'L')
|
440
|
+
@t.add_result(2, 11, 9, 'D')
|
441
|
+
@t.add_result(3, 11, 12, 'L')
|
442
|
+
@t.add_result(4, 11, 13, 'W')
|
443
|
+
@t.add_result(5, 11, 10, 'D')
|
444
|
+
@t.add_result(6, 11, 7, 'D')
|
445
|
+
|
446
|
+
# 12 MOLDT, TOBIAS 2 8:L 17:+ 11:W 4:L 3:L 6:L
|
447
|
+
@t.add_result(1, 12, 8, 'L')
|
448
|
+
@t.add_result(3, 12, 11, 'W')
|
449
|
+
@t.add_result(4, 12, 4, 'L')
|
450
|
+
@t.add_result(5, 12, 3, 'L')
|
451
|
+
@t.add_result(6, 12, 6, 'L')
|
452
|
+
|
453
|
+
# 13 MARTIN, SEAMUS 1 2:L 6:L 9:L 11:L 0:W 10:L
|
454
|
+
@t.add_result(1, 13, 2, 'L')
|
455
|
+
@t.add_result(2, 13, 6, 'L')
|
456
|
+
@t.add_result(3, 13, 9, 'L')
|
457
|
+
@t.add_result(4, 13, 11, 'L')
|
458
|
+
@t.add_result(6, 13, 10, 'L')
|
459
|
+
|
460
|
+
# 14 FREDJ, NIZAR 1 9:W 1:L 10:- 8:- 0: 0:
|
461
|
+
@t.add_result(1, 14, 9, 'W')
|
462
|
+
@t.add_result(2, 14, 1, 'L')
|
463
|
+
|
464
|
+
# 15 REID, BRIAN .5 0:D 0: 0: 0: 0: 0:
|
465
|
+
|
466
|
+
# 16 DONNELLY, VINCENT 0 3:- 10:L 0: 0: 0: 0:
|
467
|
+
@t.add_result(2, 16, 10, 'L')
|
468
|
+
|
469
|
+
@t.rate!
|
470
|
+
end
|
471
|
+
|
472
|
+
it "should agree with ICU database for rated players with results" do
|
473
|
+
[
|
474
|
+
[1, 3.97, 1768],
|
475
|
+
[2, 3.87, 1736],
|
476
|
+
[3, 2.50, 1595],
|
477
|
+
[4, 3.23, 1678],
|
478
|
+
[5, 2.39, 1650],
|
479
|
+
[6, 3.19, 1449],
|
480
|
+
[7, 3.46, 1654],
|
481
|
+
[8, 2.83, 1581],
|
482
|
+
[9, 1.39, 1298],
|
483
|
+
[10, 2.97, 1458],
|
484
|
+
[11, 2.69, 1372],
|
485
|
+
[13, 1.68, 1277],
|
486
|
+
[14, 1.18, 1609],
|
487
|
+
].each do |item|
|
488
|
+
num, expected_score, new_rating = item
|
489
|
+
p = @t.player(num)
|
490
|
+
p.expected_score.should be_close(expected_score, 0.01)
|
491
|
+
p.new_rating.should be_close(new_rating, 0.5)
|
492
|
+
p.results.inject(p.rating){ |t,r| t + r.rating_change }.should be_close(new_rating, 0.5)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
it "should agree with ICU database for provisionally rated players with results" do
|
497
|
+
[
|
498
|
+
[12, 0.59, 1157],
|
499
|
+
[16, 0.07, 1023],
|
500
|
+
].each do |item|
|
501
|
+
num, expected_score, new_rating = item
|
502
|
+
p = @t.player(num)
|
503
|
+
p.expected_score.should be_close(expected_score, 0.01)
|
504
|
+
p.new_rating.should be_close(new_rating, 0.5)
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
it "players who didn't play any rated games should not change their rating" do
|
509
|
+
p = @t.player(15)
|
510
|
+
p.expected_score.should == 0
|
511
|
+
p.new_rating.should == p.rating
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
context "#rate - a made-up tournament with players of all kinds" do
|
516
|
+
# 1 Orr, Mark 1350 3 8:W 3:W 2:W
|
517
|
+
# 2 Coughlan, Anne 251 2 7:W 4:W 1:L
|
518
|
+
# 3 Kennedy, Cian 6607 2 6:W 1:L 4:W
|
519
|
+
# 4 Ledwidge O'Brien, Aoife 10744 1 5:W 2:L 3:L
|
520
|
+
# 5 Martin, Ryan 6988 2 4:L 7:W 6:W
|
521
|
+
# 6 Hanley, Gerard 5701 1 3:L 8:W 5:L
|
522
|
+
# 7 Fagan, Joseph 5502 1 2:L 5:L 8:W
|
523
|
+
# 8 Hassett, James 10185 0 1:L 6:L 7:L
|
524
|
+
# Used in an experiment to prove the rating database is insensitive to K-factors for performance ratings.
|
525
|
+
# See the files in the SVN repository ICU/data/etc/experiments/test.txt.
|
526
|
+
before(:all) do
|
527
|
+
@t = ICU::RatedTournament.new(:desc => "K Factor Test")
|
528
|
+
@t.add_player(1, :desc => 'Orr, Mark', :rating => 2174, :kfactor => 16) # rated
|
529
|
+
@t.add_player(2, :desc => 'Coughlan, Anne', :rating => 1167, :kfactor => 24) # rated
|
530
|
+
@t.add_player(3, :desc => 'Kennedy, Cian', :rating => 751, :games => 19) # provisional, almost rated
|
531
|
+
@t.add_player(4, :desc => "Ledwidge O'Brien, Aoife", :rating => 1273, :games => 19) # provisional, almost rated
|
532
|
+
@t.add_player(5, :desc => 'Martin, Ryan', :rating => 627, :games => 10) # provisional
|
533
|
+
@t.add_player(6, :desc => 'Hanley, Gerard', :rating => 684, :games => 10) # provisional
|
534
|
+
@t.add_player(7, :desc => 'Fagan, Joseph') # unrated
|
535
|
+
@t.add_player(8, :desc => 'Hassett, James') # unrated
|
536
|
+
@t.add_result(1, 1, 8, 'W')
|
537
|
+
@t.add_result(1, 2, 7, 'W')
|
538
|
+
@t.add_result(1, 3, 6, 'W')
|
539
|
+
@t.add_result(1, 4, 5, 'W')
|
540
|
+
@t.add_result(2, 1, 3, 'W')
|
541
|
+
@t.add_result(2, 2, 4, 'W')
|
542
|
+
@t.add_result(2, 5, 7, 'W')
|
543
|
+
@t.add_result(2, 6, 8, 'W')
|
544
|
+
@t.add_result(3, 1, 2, 'W')
|
545
|
+
@t.add_result(3, 3, 4, 'W')
|
546
|
+
@t.add_result(3, 5, 6, 'W')
|
547
|
+
@t.add_result(3, 7, 8, 'W')
|
548
|
+
@t.rate!
|
549
|
+
end
|
550
|
+
|
551
|
+
it "should agree with ICU rating database" do
|
552
|
+
[
|
553
|
+
[1, 3.00, 2174],
|
554
|
+
[2, 1.36, 1182],
|
555
|
+
[3, 0.85, 851],
|
556
|
+
[4, 2.38, 1206],
|
557
|
+
[5, 1.05, 717],
|
558
|
+
[6, 1.04, 678],
|
559
|
+
[7, 1.09, 763],
|
560
|
+
[8, 1.24, 805],
|
561
|
+
].each do |item|
|
562
|
+
num, expected_score, new_rating = item
|
563
|
+
p = @t.player(num)
|
564
|
+
p.expected_score.should be_close(expected_score, 0.01)
|
565
|
+
p.new_rating.should be_close(new_rating, 0.5)
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
context "#rate - a made-up tournament that includes a group of unrateable players" do
|
571
|
+
# 1 Orr, Mark 1350 2 2:W 3:W 0:-
|
572
|
+
# 2 Coughlan, Anne 251 1 1:L 0:- 3:W
|
573
|
+
# 3 Martin, Ryan 6988 0 0:- 1:L 2:L
|
574
|
+
# 4 Hanley, Gerard 5701 2 5:W 6:W 0:-
|
575
|
+
# 7 Meeny, Kevin 5507 1 4:L 0:- 6:W
|
576
|
+
# 8 Prior, Alo 12314 0 0:- 4:L 5:L
|
577
|
+
# Used in an experiment to prove the rating database skips unrateable games.
|
578
|
+
# See the README and test3.txt in the SVN repository ICU/data/etc/experiments.
|
579
|
+
before(:all) do
|
580
|
+
@t = ICU::RatedTournament.new(:desc => "Unrateable Test 3")
|
581
|
+
@t.add_player(1, :desc => 'Orr, Mark', :rating => 2174, :kfactor => 16) # rated
|
582
|
+
@t.add_player(2, :desc => 'Coughlan, Anne', :rating => 1182, :kfactor => 24) # rated
|
583
|
+
@t.add_player(3, :desc => 'Martin, Ryan', :rating => 717, :games => 13) # provisional
|
584
|
+
@t.add_player(4, :desc => 'Hanley, Gerard', :rating => 678, :games => 13) # provisional
|
585
|
+
@t.add_player(5, :desc => 'Meeny, Kevin') # unrated
|
586
|
+
@t.add_player(6, :desc => 'Prior, Alo') # unrated
|
587
|
+
@t.add_result(1, 1, 2, 'W')
|
588
|
+
@t.add_result(1, 4, 5, 'W')
|
589
|
+
@t.add_result(2, 1, 3, 'W')
|
590
|
+
@t.add_result(2, 4, 6, 'W')
|
591
|
+
@t.add_result(3, 2, 3, 'W')
|
592
|
+
@t.add_result(3, 5, 6, 'W')
|
593
|
+
@t.rate!
|
594
|
+
end
|
595
|
+
|
596
|
+
it "should agree with ICU rating database for rateable players" do
|
597
|
+
[
|
598
|
+
[1, 2.00, 2174],
|
599
|
+
[2, 0.91, 1184],
|
600
|
+
[3, 0.10, 792],
|
601
|
+
].each do |item|
|
602
|
+
num, expected_score, new_rating, performance = item
|
603
|
+
p = @t.player(num)
|
604
|
+
p.expected_score.should be_close(expected_score, 0.01)
|
605
|
+
p.new_rating.should be_close(new_rating, 0.5)
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
it "should not rate players that have no rateable games" do
|
610
|
+
[4, 5, 6].each do |num|
|
611
|
+
p = @t.player(num)
|
612
|
+
p.expected_score.should == 0.0
|
613
|
+
p.new_rating.should be_nil
|
614
|
+
end
|
615
|
+
end
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: icu_ratings
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark Orr
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-03 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Build an object that represents a chess tournament then get it to calculate ratings of all the players.
|
17
|
+
email: mark.j.l.orr@googlemail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- .autotest
|
26
|
+
- .gitignore
|
27
|
+
- LICENCE
|
28
|
+
- README.rdoc
|
29
|
+
- Rakefile
|
30
|
+
- VERSION.yml
|
31
|
+
- lib/icu_ratings.rb
|
32
|
+
- lib/icu_ratings/player.rb
|
33
|
+
- lib/icu_ratings/result.rb
|
34
|
+
- lib/icu_ratings/tournament.rb
|
35
|
+
- spec/player_spec.rb
|
36
|
+
- spec/result_spec.rb
|
37
|
+
- spec/spec_helper.rb
|
38
|
+
- spec/tournament_spec.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/sanichi/icu_ratings
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --charset
|
46
|
+
- UTF-8
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.3.5
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: For rating chess tournaments.
|
68
|
+
test_files:
|
69
|
+
- spec/player_spec.rb
|
70
|
+
- spec/result_spec.rb
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
- spec/tournament_spec.rb
|