icu_tournament 0.8.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/LICENCE +22 -0
- data/README.rdoc +75 -0
- data/Rakefile +57 -0
- data/VERSION.yml +5 -0
- data/lib/icu_tournament.rb +8 -0
- data/lib/icu_tournament/federation.rb +303 -0
- data/lib/icu_tournament/name.rb +274 -0
- data/lib/icu_tournament/player.rb +204 -0
- data/lib/icu_tournament/result.rb +191 -0
- data/lib/icu_tournament/team.rb +90 -0
- data/lib/icu_tournament/tournament.rb +508 -0
- data/lib/icu_tournament/tournament_fcsv.rb +310 -0
- data/lib/icu_tournament/tournament_krause.rb +329 -0
- data/lib/icu_tournament/util.rb +156 -0
- data/spec/federation_spec.rb +176 -0
- data/spec/name_spec.rb +208 -0
- data/spec/player_spec.rb +313 -0
- data/spec/result_spec.rb +203 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/team_spec.rb +60 -0
- data/spec/tournament_fcsv_spec.rb +548 -0
- data/spec/tournament_krause_spec.rb +379 -0
- data/spec/tournament_spec.rb +733 -0
- data/spec/util_spec.rb +357 -0
- metadata +97 -0
@@ -0,0 +1,379 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
module ICU
|
4
|
+
class Tournament
|
5
|
+
describe Krause do
|
6
|
+
def check_player(num, first, last, other={})
|
7
|
+
p = @t.player(num)
|
8
|
+
p.first_name.should == first
|
9
|
+
p.last_name.should == last
|
10
|
+
p.gender.should == other[:gender]
|
11
|
+
p.title.should == other[:title]
|
12
|
+
p.rating.should == other[:rating]
|
13
|
+
p.fed.should == other[:fed]
|
14
|
+
p.id.should == other[:id]
|
15
|
+
p.dob.should == other[:dob]
|
16
|
+
p.rank.should == other[:rank]
|
17
|
+
end
|
18
|
+
|
19
|
+
def check_results(num, results, points)
|
20
|
+
p = @t.player(num)
|
21
|
+
p.results.size.should == results
|
22
|
+
p.points.should == points
|
23
|
+
end
|
24
|
+
|
25
|
+
context "a typical tournament" do
|
26
|
+
before(:all) do
|
27
|
+
krause = <<KRAUSE
|
28
|
+
012 Las Vegas National Open
|
29
|
+
022 Las Vegas
|
30
|
+
032 USA
|
31
|
+
042 2008.06.07
|
32
|
+
052 2008.06.09
|
33
|
+
062 3
|
34
|
+
072 3
|
35
|
+
082 1
|
36
|
+
092 All-Play-All
|
37
|
+
102 Hans Scmidt
|
38
|
+
112 Gerry Graham, Herbert Scarry
|
39
|
+
122 60 in 2hr, 30 in 1hr, rest in 1hr
|
40
|
+
013 Coaching Team 1 2 3
|
41
|
+
0 1 2 3 4 5 6 7 8 9 0 1 2
|
42
|
+
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
|
43
|
+
132 08.06.07 08.06.08 08.06.09
|
44
|
+
001 1 w Ui Laighleis,Gearoidin 1985 IRL 2501171 1964.06.10 1.0 2 2 b 0 3 w 1
|
45
|
+
001 2 m m Orr,Mark 2258 IRL 2500035 1955.11.09 2.0 1 1 w 1 3 b 1
|
46
|
+
001 3 m g Bologan,Viktor 2663 MDA 13900048 1971.01.01 0.0 3 1 b 0 2 w 0
|
47
|
+
KRAUSE
|
48
|
+
@p = ICU::Tournament::Krause.new
|
49
|
+
@t = @p.parse!(krause)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should have a name, city and federation" do
|
53
|
+
@t.name.should == 'Las Vegas National Open'
|
54
|
+
@t.city.should == 'Las Vegas'
|
55
|
+
@t.fed.should == 'USA'
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should have start and end dates" do
|
59
|
+
@t.start.should == '2008-06-07'
|
60
|
+
@t.finish.should == '2008-06-09'
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should have a number of rounds, a type and a time control" do
|
64
|
+
@t.rounds.should == 3
|
65
|
+
@t.last_round.should == 3
|
66
|
+
@t.type.should == 'All-Play-All'
|
67
|
+
@t.time_control.should == '60 in 2hr, 30 in 1hr, rest in 1hr'
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should have an arbiter and deputies" do
|
71
|
+
@t.arbiter.should == 'Hans Scmidt'
|
72
|
+
@t.deputy.should == 'Gerry Graham, Herbert Scarry'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should have players and their details" do
|
76
|
+
@t.should have(3).players
|
77
|
+
check_player(1, 'Gearoidin', 'Ui Laighleis', :gender => 'F', :rating => 1985, :fed => 'IRL', :id => 2501171, :dob => '1964-06-10', :rank => 2)
|
78
|
+
check_player(2, 'Mark', 'Orr', :gender => 'M', :rating => 2258, :fed => 'IRL', :id => 2500035, :dob => '1955-11-09', :rank => 1, :title => 'IM')
|
79
|
+
check_player(3, 'Viktor', 'Bologan', :gender => 'M', :rating => 2663, :fed => 'MDA', :id => 13900048, :dob => '1971-01-01', :rank => 3, :title => 'GM')
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should have correct results for each player" do
|
83
|
+
check_results(1, 2, 1.0)
|
84
|
+
check_results(2, 2, 2.0)
|
85
|
+
check_results(3, 2, 0.0)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should have correct round dates" do
|
89
|
+
@t.round_dates.join('|').should == '2008-06-07|2008-06-08|2008-06-09'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "the parser should retain comment lines" do
|
93
|
+
comments = <<COMMENTS
|
94
|
+
062 3
|
95
|
+
072 3
|
96
|
+
082 1
|
97
|
+
0 1 2 3 4 5 6 7 8 9 0 1 2
|
98
|
+
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
|
99
|
+
COMMENTS
|
100
|
+
@p.comments.should == comments
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "the documentation example" do
|
105
|
+
before(:all) do
|
106
|
+
krause = <<KRAUSE
|
107
|
+
012 Fantasy Tournament
|
108
|
+
032 IRL
|
109
|
+
042 2009.09.09
|
110
|
+
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
|
111
|
+
132 09.09.09 09.09.10 09.09.11
|
112
|
+
001 1 w Mouse,Minerva 1900 USA 1234567 1928.05.15 1.0 2 2 b 0 3 w 1
|
113
|
+
001 2 m m Duck,Daffy 2200 IRL 7654321 1937.04.17 2.0 1 1 w 1 3 b 1
|
114
|
+
001 3 m g Mouse,Mickey 2600 USA 1726354 1928.05.15 0.0 3 1 b 0 2 w 0
|
115
|
+
KRAUSE
|
116
|
+
@p = ICU::Tournament::Krause.new
|
117
|
+
@t = @p.parse!(krause)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should have a name and federation" do
|
121
|
+
@t.name.should == 'Fantasy Tournament'
|
122
|
+
@t.fed.should == 'IRL'
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should have a various dates" do
|
126
|
+
@t.start.should == '2009-09-09'
|
127
|
+
@t.finish.should == '2009-09-11'
|
128
|
+
@t.round_dates.join('|').should == '2009-09-09|2009-09-10|2009-09-11'
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should have a number of rounds" do
|
132
|
+
@t.rounds.should == 3
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should have players and their details" do
|
136
|
+
@t.should have(3).players
|
137
|
+
check_player(1, 'Minerva', 'Mouse', :gender => 'F', :rating => 1900, :fed => 'USA', :id => 1234567, :dob => '1928-05-15', :rank => 2)
|
138
|
+
check_player(2, 'Daffy', 'Duck', :gender => 'M', :rating => 2200, :fed => 'IRL', :id => 7654321, :dob => '1937-04-17', :rank => 1, :title => 'IM')
|
139
|
+
check_player(3, 'Mickey', 'Mouse', :gender => 'M', :rating => 2600, :fed => 'USA', :id => 1726354, :dob => '1928-05-15', :rank => 3, :title => 'GM')
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should have correct results for each player" do
|
143
|
+
check_results(1, 2, 1.0)
|
144
|
+
check_results(2, 2, 2.0)
|
145
|
+
check_results(3, 2, 0.0)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "the parser should retain comment lines" do
|
149
|
+
@p.comments.should == "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "the README serialisation example" do
|
154
|
+
before(:all) do
|
155
|
+
@t = ICU::Tournament.new('World Championship', '1972-07-11')
|
156
|
+
@t.add_player(ICU::Player.new('Robert J.', 'Fischer', 1))
|
157
|
+
@t.add_player(ICU::Player.new('Boris V.', 'Spassky', 2))
|
158
|
+
@t.add_result(ICU::Result.new(1, 1, 'L', :opponent => 2, :colour => 'B'))
|
159
|
+
@t.add_result(ICU::Result.new(2, 1, 'L', :opponent => 2, :colour => 'W', :rateable => false))
|
160
|
+
@t.add_result(ICU::Result.new(3, 1, 'W', :opponent => 2, :colour => 'B'))
|
161
|
+
@t.add_result(ICU::Result.new(4, 1, 'W', :opponent => 2, :colour => 'B'))
|
162
|
+
serializer = ICU::Tournament::Krause.new
|
163
|
+
@k = serializer.serialize(@t)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should produce a valid tournament" do
|
167
|
+
@t.invalid.should be_false
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should produce output that looks reasonable" do
|
171
|
+
@k.should match(/Fischer,Robert J\./)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "serialisation" do
|
176
|
+
before(:all) do
|
177
|
+
@krause = <<KRAUSE
|
178
|
+
012 Las Vegas National Open
|
179
|
+
022 Las Vegas
|
180
|
+
032 USA
|
181
|
+
042 2008-06-07
|
182
|
+
052 2008-06-10
|
183
|
+
092 All-Play-All
|
184
|
+
102 Hans Scmidt
|
185
|
+
112 Gerry Graham, Herbert Scarry
|
186
|
+
122 60 in 2hr, 30 in 1hr, rest in 1hr
|
187
|
+
013 Boys 2 3
|
188
|
+
013 Girls 1 4
|
189
|
+
132 08-06-07 08-06-08 08-06-09
|
190
|
+
001 1 w Ui Laighleis,Gearoidin 1985 IRL 2501171 1964-06-10 2.0 2 2 b 0 3 w + 4 b 1
|
191
|
+
001 2 m m Orr,Mark 2258 IRL 2500035 1955-11-09 2.5 1 1 w 1 0000 - = 3 b 1
|
192
|
+
001 3 m g Bologan,Viktor 2663 MDA 13900048 1971-01-01 0.0 4 1 b - 2 w 0
|
193
|
+
001 4 wg Cramling,Pia 2500 SWE 1700030 1963-04-23 0.5 3 0000 - = 1 w 0
|
194
|
+
KRAUSE
|
195
|
+
@p = ICU::Tournament::Krause.new
|
196
|
+
@t = @p.parse!(@krause)
|
197
|
+
@q = ICU::Tournament::Krause.new
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should serialize back to the original if the input is fully canonicalised" do
|
201
|
+
@q.serialize(@t).should == @krause
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should serialize using the convenience method of the tournament object" do
|
205
|
+
@t.serialize('Krause').should == @krause
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should return nil on invalid input" do
|
209
|
+
@q.serialize('Rubbish').should be_nil
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "auto-ranking" do
|
214
|
+
before(:all) do
|
215
|
+
@krause = <<KRAUSE
|
216
|
+
012 Las Vegas National Open
|
217
|
+
042 2008-06-07
|
218
|
+
001 1 w Ui Laighleis,Gearoidin 1985 IRL 2501171 1964-06-10 1.0 2 b 0 3 w 1
|
219
|
+
001 2 m m Orr,Mark 2258 IRL 2500035 1955-11-09 2.0 1 w 1 3 b 1
|
220
|
+
001 3 m g Bologan,Viktor 2663 MDA 13900048 1971-01-01 0.0 1 b 0 2 w 0
|
221
|
+
KRAUSE
|
222
|
+
@p = ICU::Tournament::Krause.new
|
223
|
+
@t = @p.parse!(@krause)
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should have rankings automatically set" do
|
227
|
+
@t.player(1).rank.should == 2
|
228
|
+
@t.player(2).rank.should == 1
|
229
|
+
@t.player(3).rank.should == 3
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context "renumbering" do
|
234
|
+
before(:all) do
|
235
|
+
@krause = <<KRAUSE
|
236
|
+
012 Las Vegas National Open
|
237
|
+
042 2008-06-07
|
238
|
+
001 10 w Ui Laighleis,Gearoidin 1985 IRL 1.0 20 b 0 30 w 1
|
239
|
+
001 20 m m Orr,Mark 2258 IRL 2.0 10 w 1 30 b 1
|
240
|
+
001 30 m g Bologan,Viktor 2663 MDA 0.0 10 b 0 20 w 0
|
241
|
+
KRAUSE
|
242
|
+
@p = ICU::Tournament::Krause.new
|
243
|
+
@t = @p.parse!(@krause)
|
244
|
+
@reordered = <<REORDERED
|
245
|
+
012 Las Vegas National Open
|
246
|
+
042 2008-06-07
|
247
|
+
001 1 m m Orr,Mark 2258 IRL 2.0 1 2 w 1 3 b 1
|
248
|
+
001 2 w Ui Laighleis,Gearoidin 1985 IRL 1.0 2 1 b 0 3 w 1
|
249
|
+
001 3 m g Bologan,Viktor 2663 MDA 0.0 3 2 b 0 1 w 0
|
250
|
+
REORDERED
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should serialise correctly after renumbering by rank" do
|
254
|
+
@t.renumber
|
255
|
+
@p.serialize(@t).should == @reordered
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
context "serialisation of a manually build tournament" do
|
260
|
+
before(:all) do
|
261
|
+
@krause = <<KRAUSE
|
262
|
+
012 Las Vegas National Open
|
263
|
+
042 2008-06-07
|
264
|
+
001 1 w Ui Laighleis,Gearoidin 1985 IRL 2501171 1964-06-10 1.0 2 b 0 3 w 1
|
265
|
+
001 2 m Orr,Mark 2258 IRL 2500035 1955-11-09 2.0 1 w 1 3 b 1
|
266
|
+
001 3 g Bologan,Viktor 2663 MDA 13900048 1971-01-01 0.0 1 b 0 2 w 0
|
267
|
+
KRAUSE
|
268
|
+
@p = ICU::Tournament::Krause.new
|
269
|
+
@t = ICU::Tournament.new('Las Vegas National Open', '2008-06-07')
|
270
|
+
@t.add_player(ICU::Player.new('Gearoidin', 'Ui Laighleis', 1, :rating => 1985, :id => 2501171, :dob => '1964-06-10', :fed => 'IRL', :gender => 'f'))
|
271
|
+
@t.add_player(ICU::Player.new('Mark', 'Orr', 2, :rating => 2258, :id => 2500035, :dob => '1955-11-09', :fed => 'IRL', :title => 'm'))
|
272
|
+
@t.add_player(ICU::Player.new('Viktor', 'Bologan', 3, :rating => 2663, :id => 13900048, :dob => '1971-01-01', :fed => 'MDA', :title => 'g'))
|
273
|
+
@t.add_result(ICU::Result.new(1, 1, 'L', :opponent => 2, :colour => 'B'))
|
274
|
+
@t.add_result(ICU::Result.new(2, 1, 'W', :opponent => 3, :colour => 'W'))
|
275
|
+
@t.add_result(ICU::Result.new(3, 2, 'W', :opponent => 3, :colour => 'B'))
|
276
|
+
@output = @p.serialize(@t)
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should serialise manually build tournaments" do
|
280
|
+
@output.should == @krause
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context "errors" do
|
285
|
+
before(:each) do
|
286
|
+
@k = <<KRAUSE
|
287
|
+
012 Gonzaga Classic
|
288
|
+
022 Dublin
|
289
|
+
032 IRL
|
290
|
+
042 2008-02-01
|
291
|
+
052 2008-02-03
|
292
|
+
062 12
|
293
|
+
092 Swiss
|
294
|
+
102 Michael Germaine, mlgermaine@eircom.net
|
295
|
+
122 120 minutes per player per game
|
296
|
+
001 1 Griffiths,Ryan Rhys IRL 2502054 4.0 1 0000 - = 3 b 1 8 w 1 5 b = 7 w 1
|
297
|
+
001 2 Hotak,Marian SVK 14909677 3.5 2 3 w 0 6 b = 11 w 1 8 b 1 5 w 1
|
298
|
+
001 3 Duffy,Seamus IRL 3.0 3 2 b 1 1 w 0 4 w 1 6 b = 8 w =
|
299
|
+
001 4 Cafolla,Peter IRL 2500884 3.0 4 7 b 1 5 w = 3 b 0 11 b + 6 w =
|
300
|
+
001 5 Ferry,Edward SCO 2461587 3.0 5 10 b 1 4 b = 9 w 1 1 w = 2 b 0
|
301
|
+
001 6 Boyle,Bernard IRL 2501830 3.0 6 12 b = 2 w = 10 b 1 3 w = 4 b =
|
302
|
+
001 7 McCarthy,Tim IRL 2500710 2.5 7 4 w 0 10 w = 12 b + 9 b 1 1 b 0
|
303
|
+
001 8 Benson,Oisin P. IRL 2501821 2.0 8 0000 - = 11 w 1 1 b 0 2 w 0 3 b =
|
304
|
+
001 9 Murray,David B. IRL 2501511 2.0 9 11 b = 12 w + 5 b 0 7 w 0 10 w =
|
305
|
+
001 10 Moser,Philippe SUI 1308041 1.5 10 5 w 0 7 b = 6 w 0 0000 - = 9 b =
|
306
|
+
001 11 Barbosa,Paulo POR 1904612 1.5 11 9 w = 8 b 0 2 b 0 4 w - 0000 - +
|
307
|
+
001 12 McCabe,Darren IRL 2500760 0.5 12 6 w = 9 b - 7 w -
|
308
|
+
KRAUSE
|
309
|
+
@p = ICU::Tournament::Krause.new
|
310
|
+
end
|
311
|
+
|
312
|
+
it "the unaltered example is valid Krause" do
|
313
|
+
t = @p.parse(@k).should be_instance_of(ICU::Tournament)
|
314
|
+
end
|
315
|
+
|
316
|
+
it "removing the line on which the tournament name is specified should cause an error" do
|
317
|
+
@k.sub!('012 Gonzaga Classic', '')
|
318
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/name missing/)
|
319
|
+
end
|
320
|
+
|
321
|
+
it "blanking the tournament name should cause an error" do
|
322
|
+
@k.sub!('Gonzaga Classic', '')
|
323
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/name missing/)
|
324
|
+
end
|
325
|
+
|
326
|
+
it "blanking the start date should cause an error" do
|
327
|
+
@k.sub!('2008-02-01', '2008-02-04')
|
328
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/start.*after.*end/)
|
329
|
+
end
|
330
|
+
|
331
|
+
it "the start cannot be later than the end date" do
|
332
|
+
@k.sub!('2008-02-01', '')
|
333
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/start date missing/)
|
334
|
+
end
|
335
|
+
|
336
|
+
it "creating a duplicate player number should cause an error" do
|
337
|
+
@k.sub!(' 2 ', ' 1 ')
|
338
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/player number/)
|
339
|
+
end
|
340
|
+
|
341
|
+
it "creating a duplicate rank number should not cause an error becuse the tournament will be reranked" do
|
342
|
+
@k.sub!('4.0 1', '4.0 2')
|
343
|
+
t = @p.parse!(@k)
|
344
|
+
t.player(1).rank.should == 1
|
345
|
+
end
|
346
|
+
|
347
|
+
it "referring to a non-existant player number should cause an error" do
|
348
|
+
@k.sub!(' 3 b 1', '33 b 1')
|
349
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/opponent number/)
|
350
|
+
end
|
351
|
+
|
352
|
+
it "inconsistent colours should cause an error" do
|
353
|
+
@k.sub!('3 b 1', '3 w 1')
|
354
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/result/)
|
355
|
+
end
|
356
|
+
|
357
|
+
it "inconsistent scores should cause an error" do
|
358
|
+
@k.sub!('3 b 1', '3 b =')
|
359
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/result/)
|
360
|
+
end
|
361
|
+
|
362
|
+
it "inconsistent totals should cause an error" do
|
363
|
+
@k.sub!('4.0', '4.5')
|
364
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/total/)
|
365
|
+
end
|
366
|
+
|
367
|
+
it "invalid federations should cause an error" do
|
368
|
+
@k.sub!('SCO', 'XYZ')
|
369
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/federation/)
|
370
|
+
end
|
371
|
+
|
372
|
+
it "removing any player that somebody else has played should cause an error" do
|
373
|
+
@k.sub!(/^001 12.*$/, '')
|
374
|
+
lambda { t = @p.parse!(@k) }.should raise_error(/opponent/)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
@@ -0,0 +1,733 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
module ICU
|
4
|
+
describe Tournament do
|
5
|
+
context "a basic tournament" do
|
6
|
+
it "has a name, start date, some players and some results" do
|
7
|
+
lambda do
|
8
|
+
t = Tournament.new('Bangor Bash', '2009-11-09')
|
9
|
+
t.add_player(Player.new('Bobby', 'Fischer', 1))
|
10
|
+
t.add_player(Player.new('Garry', 'Kasparov', 2))
|
11
|
+
t.add_player(Player.new('Mark', 'Orr', 3))
|
12
|
+
t.add_result(Result.new(1, 1, '=', :opponent => 2, :colour => 'W'))
|
13
|
+
t.add_result(Result.new(2, 2, 'L', :opponent => 3, :colour => 'W'))
|
14
|
+
t.add_result(Result.new(3, 3, 'W', :opponent => 1, :colour => 'W'))
|
15
|
+
t.validate!
|
16
|
+
end.should_not raise_error
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "documentation example" do
|
21
|
+
before(:each) do
|
22
|
+
@t = t = ICU::Tournament.new('Bangor Masters', '2009-11-09')
|
23
|
+
t.add_player(ICU::Player.new('Bobby', 'Fischer', 10))
|
24
|
+
t.add_player(ICU::Player.new('Garry', 'Kasparov', 20))
|
25
|
+
t.add_player(ICU::Player.new('Mark', 'Orr', 30))
|
26
|
+
t.add_result(ICU::Result.new(1, 10, 'D', :opponent => 30, :colour => 'W'))
|
27
|
+
t.add_result(ICU::Result.new(2, 20, 'W', :opponent => 30, :colour => 'B'))
|
28
|
+
t.add_result(ICU::Result.new(3, 20, 'L', :opponent => 10, :colour => 'W'))
|
29
|
+
t.validate!(:rerank => true)
|
30
|
+
@s = <<EOS
|
31
|
+
012 Bangor Masters
|
32
|
+
042 2009-11-09
|
33
|
+
001 10 Fischer,Bobby 1.5 1 30 w = 20 b 1
|
34
|
+
001 20 Kasparov,Garry 1.0 2 30 b 1 10 w 0
|
35
|
+
001 30 Orr,Mark 0.5 3 10 b = 20 w 0
|
36
|
+
EOS
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should serialize to Krause" do
|
40
|
+
parser = ICU::Tournament::Krause.new
|
41
|
+
parser.serialize(@t).should == @s
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "name" do
|
46
|
+
before(:each) do
|
47
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "must be specified in constructor" do
|
51
|
+
@t.name.should == 'Edinburgh Masters'
|
52
|
+
end
|
53
|
+
|
54
|
+
it "can be replaced by accessor" do
|
55
|
+
@t.name = 'Bangor Bashers'
|
56
|
+
@t.name.should == 'Bangor Bashers'
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not be blank or without letters" do
|
60
|
+
lambda { Tournament.new(' ', '2009-11-09') }.should raise_error(/invalid.*name/)
|
61
|
+
lambda { @t.name = '333' }.should raise_error(/invalid.*name/)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "city" do
|
66
|
+
before(:each) do
|
67
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09', :city => 'Edinburgh')
|
68
|
+
end
|
69
|
+
|
70
|
+
it "may be specified in constructor" do
|
71
|
+
@t.city.should == 'Edinburgh'
|
72
|
+
end
|
73
|
+
|
74
|
+
it "can be replaced by accessor" do
|
75
|
+
@t.city = 'Glasgow'
|
76
|
+
@t.city.should == 'Glasgow'
|
77
|
+
end
|
78
|
+
|
79
|
+
it "can be set to nil" do
|
80
|
+
@t.city = ''
|
81
|
+
@t.city.should be_nil
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should not be without letters if set" do
|
85
|
+
lambda { @t.city = '123' }.should raise_error(/invalid.*city/)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "federation" do
|
90
|
+
before(:each) do
|
91
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09', :fed => 'SCO')
|
92
|
+
end
|
93
|
+
|
94
|
+
it "may be specified in constructor" do
|
95
|
+
@t.fed.should == 'SCO'
|
96
|
+
end
|
97
|
+
|
98
|
+
it "can be replaced by accessor" do
|
99
|
+
@t.fed = 'IRL'
|
100
|
+
@t.fed.should == 'IRL'
|
101
|
+
end
|
102
|
+
|
103
|
+
it "can be set to nil" do
|
104
|
+
@t.fed = ''
|
105
|
+
@t.fed.should be_nil
|
106
|
+
end
|
107
|
+
|
108
|
+
it "three letters will automatically be upcased" do
|
109
|
+
@t.fed = 'rus'
|
110
|
+
@t.fed.should == 'RUS'
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should not be without letters if set" do
|
114
|
+
lambda { @t.fed = '123' }.should raise_error(/invalid.*federation/)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "start date" do
|
119
|
+
before(:each) do
|
120
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09')
|
121
|
+
end
|
122
|
+
|
123
|
+
it "must be specified in constructor" do
|
124
|
+
@t.start.should == '2009-11-09'
|
125
|
+
end
|
126
|
+
|
127
|
+
it "can be replaced by accessor" do
|
128
|
+
@t.start = '16th June 2010'
|
129
|
+
@t.start.should == '2010-06-16'
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should be a valid date" do
|
133
|
+
lambda { Tournament.new('Edinburgh Masters', ' ') }.should raise_error(/invalid.*date/)
|
134
|
+
lambda { @t.start = 'X' }.should raise_error(/invalid.*date/)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "finish date" do
|
139
|
+
before(:each) do
|
140
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09', :finish => '12th November 2009')
|
141
|
+
end
|
142
|
+
|
143
|
+
it "may be specified in constructor" do
|
144
|
+
@t.finish.should == '2009-11-12'
|
145
|
+
end
|
146
|
+
|
147
|
+
it "can be replaced by accessor" do
|
148
|
+
@t.finish = '16th December 2009'
|
149
|
+
@t.finish.should == '2009-12-16'
|
150
|
+
end
|
151
|
+
|
152
|
+
it "can be set to nil" do
|
153
|
+
@t.finish = ''
|
154
|
+
@t.finish.should be_nil
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should be a valid date" do
|
158
|
+
lambda { Tournament.new('Edinburgh Masters', '2009-11-09', :finish => 'next week') }.should raise_error(/invalid.*date/)
|
159
|
+
lambda { @t.finish = 'X' }.should raise_error(/invalid.*date/)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "rounds" do
|
164
|
+
it "defaults to nil" do
|
165
|
+
Tournament.new('Edinburgh Masters', '2009-11-09').rounds.should be_nil
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should be a positive integer or nil" do
|
169
|
+
Tournament.new('Edinburgh Masters', '2009-11-09', :rounds => 3).rounds.should == 3
|
170
|
+
Tournament.new('Edinburgh Masters', '2009-11-09', :rounds => ' 10 ').rounds.should == 10
|
171
|
+
Tournament.new('Edinburgh Masters', '2009-11-09', :rounds => nil).rounds.should be_nil
|
172
|
+
lambda { Tournament.new('Edinburgh Masters', '2009-11-09', :rounds => ' 0 ') }.should raise_error(/invalid.*rounds/)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "last_round" do
|
177
|
+
before(:each) do
|
178
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09')
|
179
|
+
@t.add_player(@mark = Player.new('Mark', 'Orr', 1))
|
180
|
+
@t.add_player(@gary = Player.new('Gary', 'Kasparov', 2))
|
181
|
+
@t.add_player(@boby = Player.new('Bobby', 'Fischer', 3))
|
182
|
+
end
|
183
|
+
|
184
|
+
it "depends on the players results" do
|
185
|
+
@t.last_round.should == 0
|
186
|
+
@t.add_result(Result.new(1, 1, 'W', :opponent => 2))
|
187
|
+
@t.last_round.should == 1
|
188
|
+
@t.add_result(Result.new(2, 2, 'D', :opponent => 3))
|
189
|
+
@t.last_round.should == 2
|
190
|
+
@t.add_result(Result.new(5, 3, 'L', :opponent => 1))
|
191
|
+
@t.last_round.should == 5
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context "round date" do
|
196
|
+
before(:each) do
|
197
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09')
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should default to none" do
|
201
|
+
@t.round_dates.size.should == 0
|
202
|
+
end
|
203
|
+
|
204
|
+
it "can be added one by one in any order" do
|
205
|
+
@t.add_round_date('2009-11-11')
|
206
|
+
@t.add_round_date('09/11/2009')
|
207
|
+
@t.add_round_date('10th November 2009')
|
208
|
+
@t.round_dates.join('|').should == '2009-11-09|2009-11-10|2009-11-11'
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context "site" do
|
213
|
+
it "defaults to nil" do
|
214
|
+
Tournament.new('Edinburgh Masters', '2009-11-09').site.should be_nil
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should be a reasonably valid looking URL" do
|
218
|
+
Tournament.new('Edinburgh Masters', '2009-11-09', :site => 'https://www.bbc.co.uk').site.should == 'https://www.bbc.co.uk'
|
219
|
+
Tournament.new('Edinburgh Masters', '2009-11-09', :site => 'www.icu.ie/event.php?id=1').site.should == 'http://www.icu.ie/event.php?id=1'
|
220
|
+
lambda { Tournament.new('Edinburgh Masters', '2009-11-09', :site => 'X') }.should raise_error(/invalid.*site/)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "type, arbiter, deputy and time control" do
|
225
|
+
before(:each) do
|
226
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09', :type => 'Swiss', :arbiter => 'Gerry Graham', :deputy => 'Herbert Scarry', :time_control => '120 minutes')
|
227
|
+
end
|
228
|
+
|
229
|
+
it "may be specified in constructor" do
|
230
|
+
@t.type.should == 'Swiss'
|
231
|
+
@t.arbiter.should == 'Gerry Graham'
|
232
|
+
@t.deputy.should == 'Herbert Scarry'
|
233
|
+
@t.time_control.should == '120 minutes'
|
234
|
+
end
|
235
|
+
|
236
|
+
it "can be replaced by accessor" do
|
237
|
+
@t.type = 'all-play-all'
|
238
|
+
@t.type.should == 'all-play-all'
|
239
|
+
@t.arbiter = 'Michael Crowe'
|
240
|
+
@t.arbiter.should == 'Michael Crowe'
|
241
|
+
@t.deputy = 'Mark Orr'
|
242
|
+
@t.deputy.should == 'Mark Orr'
|
243
|
+
@t.time_control = '90 minutes'
|
244
|
+
@t.time_control.should == '90 minutes'
|
245
|
+
end
|
246
|
+
|
247
|
+
it "can be set to nil" do
|
248
|
+
@t.type = ''
|
249
|
+
@t.type.should be_nil
|
250
|
+
@t.arbiter = ''
|
251
|
+
@t.arbiter.should be_nil
|
252
|
+
@t.deputy = ''
|
253
|
+
@t.deputy.should be_nil
|
254
|
+
@t.time_control = ''
|
255
|
+
@t.time_control.should be_nil
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should be valid" do
|
259
|
+
lambda { @t.type = '123' }.should raise_error(/invalid.*type/)
|
260
|
+
lambda { @t.arbiter = '123' }.should raise_error(/invalid.*arbiter/)
|
261
|
+
lambda { @t.deputy = '123' }.should raise_error(/invalid.*deputy/)
|
262
|
+
lambda { @t.time_control = 'abc' }.should raise_error(/invalid.*time.*control/)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
context "players" do
|
267
|
+
before(:each) do
|
268
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09')
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should have unique numbers" do
|
272
|
+
@t.add_player(Player.new('Mark', 'Orr', 1))
|
273
|
+
lambda { @t.add_player(Player.new('Bobby', 'Fischer', 1)) }.should raise_error(/player.*unique/)
|
274
|
+
end
|
275
|
+
|
276
|
+
it "can be added one at a time" do
|
277
|
+
@t.add_player(Player.new('Mark', 'Orr', -1))
|
278
|
+
@t.add_player(Player.new('Gary', 'Kasparov', -2))
|
279
|
+
@t.add_player(Player.new('Bobby', 'Fischer', -3))
|
280
|
+
@t.players.size.should == 3
|
281
|
+
@t.player(-1).first_name.should == 'Mark'
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context "results" do
|
286
|
+
before(:each) do
|
287
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09', :rounds => 3)
|
288
|
+
@t.add_player(@mark = Player.new('Mark', 'Orr', 1))
|
289
|
+
@t.add_player(@gary = Player.new('Gary', 'Kasparov', 2))
|
290
|
+
@t.add_player(@boby = Player.new('Bobby', 'Fischer', 3))
|
291
|
+
end
|
292
|
+
|
293
|
+
it "can be added one at a time" do
|
294
|
+
@t.add_result(Result.new(1, 1, 'W', :opponent => 2))
|
295
|
+
@t.add_result(Result.new(2, 2, 'D', :opponent => 3))
|
296
|
+
@t.add_result(Result.new(3, 3, 'L', :opponent => 1))
|
297
|
+
@mark.results.size.should == 2
|
298
|
+
@mark.points.should == 2.0
|
299
|
+
@gary.results.size.should == 2
|
300
|
+
@gary.points.should == 0.5
|
301
|
+
@boby.results.size.should == 2
|
302
|
+
@boby.points.should == 0.5
|
303
|
+
end
|
304
|
+
|
305
|
+
it "can be added symmetrically or asymmetrically with respect to rateability" do
|
306
|
+
@t.add_result(Result.new(1, 1, 'W', :opponent => 2))
|
307
|
+
@mark.results[0].rateable.should be_true
|
308
|
+
@gary.results[0].rateable.should be_true
|
309
|
+
@t.add_result(Result.new(2, 1, 'W', :opponent => 3), false)
|
310
|
+
@mark.results[1].rateable.should be_true
|
311
|
+
@boby.results[0].rateable.should be_false
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should have a defined player" do
|
315
|
+
lambda { @t.add_result(Result.new(1, 4, 'L', :opponent => 1)) }.should raise_error(/player.*exist/)
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should have a defined opponent" do
|
319
|
+
lambda { @t.add_result(Result.new(1, 1, 'W', :opponent => 4)) }.should raise_error(/opponent.*exist/)
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should be consistent with the tournament's number of rounds" do
|
323
|
+
lambda { @t.add_result(Result.new(4, 1, 'W', :opponent => 2)) }.should raise_error(/round/)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
context "finding players" do
|
328
|
+
before(:all) do
|
329
|
+
@t = Tournament.new('Bangor Bash', '2009-11-09')
|
330
|
+
@t.add_player(Player.new('Bobby', 'Fischer', 1, :fed => 'USA'))
|
331
|
+
@t.add_player(Player.new('Garry', 'Gary Kasparov', 2, :fed => 'RUS'))
|
332
|
+
@t.add_player(Player.new('Mark', 'Orr', 3, :fed => 'IRL'))
|
333
|
+
end
|
334
|
+
|
335
|
+
it "should find players based on loose equality" do
|
336
|
+
@t.find_player(Player.new('Mark', 'Orr', 4, :fed => 'IRL')).num.should == 3
|
337
|
+
@t.find_player(Player.new('Mark', 'Orr', 4, :fed => 'USA')).should be_nil
|
338
|
+
@t.find_player(Player.new('Mark', 'Sax', 4, :fed => 'IRL')).should be_nil
|
339
|
+
@t.find_player(Player.new('John', 'Orr', 4, :fed => 'IRL')).should be_nil
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
context "teams" do
|
344
|
+
before(:each) do
|
345
|
+
@t = Tournament.new('Bangor Bash', '2009-11-09')
|
346
|
+
end
|
347
|
+
|
348
|
+
it "should be able to create a new team, add it and retrieve it" do
|
349
|
+
team = Team.new('Wandering Dragons')
|
350
|
+
@t.add_team(team).should be_an_instance_of Team
|
351
|
+
@t.get_team(' wandering dragons ').should be_an_instance_of Team
|
352
|
+
@t.get_team('Blundering Bishops').should be_nil
|
353
|
+
end
|
354
|
+
|
355
|
+
it "should be able to create and add a new team and retrieve it" do
|
356
|
+
@t.add_team('Blundering Bishops').should be_an_instance_of Team
|
357
|
+
@t.get_team(' blundering bishops ').should be_an_instance_of Team
|
358
|
+
@t.get_team('Wandering Dragons').should be_nil
|
359
|
+
end
|
360
|
+
|
361
|
+
it "should throw and exception if there is an attempt to add a team with a name that matches an existing team" do
|
362
|
+
lambda { @t.add_team('Blundering Bishops') }.should_not raise_error
|
363
|
+
lambda { @t.add_team('Wandering Dragons') }.should_not raise_error
|
364
|
+
lambda { @t.add_team(' wandering dragons ') }.should raise_error(/similar.*exists/)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context "validation" do
|
369
|
+
before(:each) do
|
370
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09')
|
371
|
+
@t.add_player(@mark = Player.new('Mark', 'Orr', 1))
|
372
|
+
@t.add_player(@gary = Player.new('Gary', 'Kasparov', 2))
|
373
|
+
@t.add_player(@boby = Player.new('Bobby', 'Fischer', 3))
|
374
|
+
@t.add_result(Result.new(1, 1, 'W', :opponent => 2))
|
375
|
+
@t.add_result(Result.new(2, 2, 'W', :opponent => 3))
|
376
|
+
@t.add_result(Result.new(3, 3, 'L', :opponent => 1))
|
377
|
+
@t.add_round_date('2009-11-09')
|
378
|
+
@t.add_round_date('2009-11-10')
|
379
|
+
@t.add_round_date('2009-11-11')
|
380
|
+
end
|
381
|
+
|
382
|
+
it "should be valid" do
|
383
|
+
@t.invalid.should be_false
|
384
|
+
end
|
385
|
+
|
386
|
+
it "should have side effect of setting number of rounds" do
|
387
|
+
@t.rounds.should be_nil
|
388
|
+
@t.invalid
|
389
|
+
@t.rounds.should == 3
|
390
|
+
end
|
391
|
+
|
392
|
+
it "should detect an inconsistent start date" do
|
393
|
+
@t.start = '2009-11-10'
|
394
|
+
lambda { @t.validate! }.should raise_error(/first round.*before.*start/)
|
395
|
+
end
|
396
|
+
|
397
|
+
it "should detect an inconsistent finish date" do
|
398
|
+
@t.finish = '2009-11-10'
|
399
|
+
lambda { @t.validate! }.should raise_error(/last round.*after.*end/)
|
400
|
+
end
|
401
|
+
|
402
|
+
it "should have side effect of setting missing finish date" do
|
403
|
+
@t.finish.should be_nil
|
404
|
+
@t.invalid
|
405
|
+
@t.finish.should == '2009-11-11'
|
406
|
+
end
|
407
|
+
|
408
|
+
it "should detect inconsistent round dates" do
|
409
|
+
@t.add_round_date('2009-11-12')
|
410
|
+
lambda { @t.validate! }.should raise_error(/round dates.*match.*rounds/)
|
411
|
+
end
|
412
|
+
|
413
|
+
it "should have the side effect of providing missing ranks if the rerank option is set" do
|
414
|
+
@t.players.select{ |p| p.rank }.size.should == 0
|
415
|
+
@t.invalid(:rerank => true)
|
416
|
+
@t.player(1).rank.should == 1
|
417
|
+
@t.player(2).rank.should == 2
|
418
|
+
@t.player(3).rank.should == 3
|
419
|
+
end
|
420
|
+
|
421
|
+
it "should have the side effect of correcting bad ranks if the rerank option is set" do
|
422
|
+
@t.player(1).rank = 2
|
423
|
+
@t.player(2).rank = 1
|
424
|
+
@t.player(3).rank = 3
|
425
|
+
@t.invalid(:rerank => true)
|
426
|
+
@t.player(1).rank.should == 1
|
427
|
+
@t.player(2).rank.should == 2
|
428
|
+
@t.player(3).rank.should == 3
|
429
|
+
end
|
430
|
+
|
431
|
+
it "should detect missranked players" do
|
432
|
+
@t.player(1).rank = 2
|
433
|
+
@t.player(2).rank = 1
|
434
|
+
@t.player(3).rank = 3
|
435
|
+
lambda { @t.validate! }.should raise_error(/player 2.*above.*player 1/)
|
436
|
+
end
|
437
|
+
|
438
|
+
it "should be valid if there are teams, every player is in one of them, and no team has an invalid member" do
|
439
|
+
team1 = Team.new('International Masters')
|
440
|
+
team2 = Team.new('World Champions')
|
441
|
+
@t.add_team(team1)
|
442
|
+
@t.add_team(team2)
|
443
|
+
@t.invalid.should match(/not.*member/)
|
444
|
+
team1.add_member(1)
|
445
|
+
team2.add_member(2)
|
446
|
+
team2.add_member(3)
|
447
|
+
@t.invalid.should be_false
|
448
|
+
team1.add_member(4)
|
449
|
+
@t.invalid.should match(/not.*valid/)
|
450
|
+
end
|
451
|
+
|
452
|
+
it "should not be valid if one player is in more than one team" do
|
453
|
+
team1 = Team.new('XInternational Masters')
|
454
|
+
team1.add_member(1)
|
455
|
+
team2 = Team.new('XWorld Champions')
|
456
|
+
team2.add_member(2)
|
457
|
+
team2.add_member(3)
|
458
|
+
@t.add_team(team1)
|
459
|
+
@t.add_team(team2)
|
460
|
+
@t.invalid.should be_false
|
461
|
+
team1.add_member(2)
|
462
|
+
@t.invalid.should match(/already.*member/)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
context "renumbering" do
|
467
|
+
before(:each) do
|
468
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09')
|
469
|
+
@t.add_player(@mark = Player.new('Mark', 'Orr', 20))
|
470
|
+
@t.add_player(@boby = Player.new('Bobby', 'Fischer', 10))
|
471
|
+
@t.add_player(@gary = Player.new('Gary', 'Kasparov', 30))
|
472
|
+
@t.add_result(Result.new(1, 20, 'W', :opponent => 10))
|
473
|
+
@t.add_result(Result.new(2, 30, 'W', :opponent => 10))
|
474
|
+
@t.add_result(Result.new(3, 20, 'W', :opponent => 30))
|
475
|
+
end
|
476
|
+
|
477
|
+
it "sample tournament is valid but unranked" do
|
478
|
+
@t.invalid.should be_false
|
479
|
+
@t.player(10).rank.should be_nil
|
480
|
+
@t.players.map{ |p| p.num }.join('|').should == '10|20|30'
|
481
|
+
@t.players.map{ |p| p.last_name }.join('|').should == 'Fischer|Orr|Kasparov'
|
482
|
+
end
|
483
|
+
|
484
|
+
it "should be renumberable by name in the absence of ranking" do
|
485
|
+
@t.renumber
|
486
|
+
@t.invalid.should be_false
|
487
|
+
@t.players.map{ |p| p.num }.join('|').should == '1|2|3'
|
488
|
+
@t.players.map{ |p| p.last_name }.join('|').should == 'Fischer|Kasparov|Orr'
|
489
|
+
end
|
490
|
+
|
491
|
+
it "should be renumberable by rank if the tournament is ranked" do
|
492
|
+
@t.rerank.renumber
|
493
|
+
@t.invalid.should be_false
|
494
|
+
@t.players.map{ |p| p.num }.join('|').should == '1|2|3'
|
495
|
+
@t.players.map{ |p| p.rank }.join('|').should == '1|2|3'
|
496
|
+
@t.players.map{ |p| p.last_name }.join('|').should == 'Orr|Kasparov|Fischer'
|
497
|
+
end
|
498
|
+
|
499
|
+
it "should be renumberable by name even if the tourament is ranked" do
|
500
|
+
@t.rerank.renumber(:name)
|
501
|
+
@t.invalid.should be_false
|
502
|
+
@t.players.map{ |p| p.num }.join('|').should == '1|2|3'
|
503
|
+
@t.players.map{ |p| p.last_name }.join('|').should == 'Fischer|Kasparov|Orr'
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
context "reranking" do
|
508
|
+
before(:each) do
|
509
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09')
|
510
|
+
@t.add_player(@boby = Player.new('Bobby', 'Fischer', 1))
|
511
|
+
@t.add_player(@gary = Player.new('Gary', 'Kasparov', 2))
|
512
|
+
@t.add_player(@boby = Player.new('Micky', 'Mouse', 3))
|
513
|
+
@t.add_player(@boby = Player.new('Minnie', 'Mouse', 4))
|
514
|
+
@t.add_player(@boby = Player.new('Gearoidin', 'Ui Laighleis', 5))
|
515
|
+
@t.add_player(@mark = Player.new('Mark', 'Orr', 6))
|
516
|
+
@t.add_result(Result.new(1, 1, 'W', :opponent => 6, :colour => 'W'))
|
517
|
+
@t.add_result(Result.new(2, 1, 'W', :opponent => 3, :colour => 'B'))
|
518
|
+
@t.add_result(Result.new(3, 1, 'W', :opponent => 5, :colour => 'W'))
|
519
|
+
@t.add_result(Result.new(1, 2, 'W', :opponent => 5, :colour => 'B'))
|
520
|
+
@t.add_result(Result.new(2, 2, 'W', :opponent => 4, :colour => 'W'))
|
521
|
+
@t.add_result(Result.new(3, 2, 'W', :opponent => 3, :colour => 'B'))
|
522
|
+
@t.add_result(Result.new(1, 3, 'W', :opponent => 4, :colour => 'W'))
|
523
|
+
@t.add_result(Result.new(3, 4, 'W', :opponent => 6, :colour => 'W'))
|
524
|
+
@t.add_result(Result.new(2, 5, 'D', :opponent => 6, :colour => 'W'))
|
525
|
+
end
|
526
|
+
|
527
|
+
it "should initially be valid but unranked" do
|
528
|
+
@t.invalid.should be_false
|
529
|
+
@t.player(1).rank.should be_nil
|
530
|
+
end
|
531
|
+
|
532
|
+
it "should have correct default tie break scores" do
|
533
|
+
scores = @t.tie_break_scores
|
534
|
+
scores[1].should == 'Fischer, Bobby'
|
535
|
+
scores[5].should == 'Ui Laighleis, Gearoidin'
|
536
|
+
end
|
537
|
+
|
538
|
+
it "should have correct actual scores" do
|
539
|
+
@t.player(1).points.should == 3.0
|
540
|
+
@t.player(2).points.should == 3.0
|
541
|
+
@t.player(3).points.should == 1.0
|
542
|
+
@t.player(4).points.should == 1.0
|
543
|
+
@t.player(5).points.should == 0.5
|
544
|
+
@t.player(6).points.should == 0.5
|
545
|
+
end
|
546
|
+
|
547
|
+
it "should have correct Buchholz tie break scores" do
|
548
|
+
scores = @t.tie_break_scores("Buchholz")
|
549
|
+
scores[1].should == 2.0
|
550
|
+
scores[2].should == 2.5
|
551
|
+
scores[3].should == 7.0
|
552
|
+
scores[4].should == 4.5
|
553
|
+
scores[5].should == 6.5
|
554
|
+
scores[6].should == 4.5
|
555
|
+
end
|
556
|
+
|
557
|
+
it "Buchholz should be sensitive to unplayed games" do
|
558
|
+
@t.player(1).find_result(1).opponent = nil
|
559
|
+
@t.player(6).find_result(1).opponent = nil
|
560
|
+
scores = @t.tie_break_scores("Buchholz")
|
561
|
+
scores[1].should == 1.5 # 0.5 from Orr changed to 0
|
562
|
+
scores[2].should == 2.5 # didn't play Fischer or Orr so unaffected
|
563
|
+
scores[3].should == 6.5 # 3 from Fischer's changed to 2.5
|
564
|
+
scores[4].should == 5.0 # 0.5 from Orr changed to 1 (because Orr's unrated loss to Fischer now counts as a draw)
|
565
|
+
scores[5].should == 6.5 # 3 from Fischer changed to 2.5, 0.5 from Orr changed to 1 (cancels out)
|
566
|
+
scores[6].should == 1.5 # 3 from Fischer changed to 0
|
567
|
+
end
|
568
|
+
|
569
|
+
it "should have correct Neustadtl tie break scores" do
|
570
|
+
scores = @t.tie_break_scores(:neustadtl)
|
571
|
+
scores[1].should == 2.0
|
572
|
+
scores[2].should == 2.5
|
573
|
+
scores[3].should == 1.0
|
574
|
+
scores[4].should == 0.5
|
575
|
+
scores[5].should == 0.25
|
576
|
+
scores[6].should == 0.25
|
577
|
+
end
|
578
|
+
|
579
|
+
it "Neustadtl should be sensitive to unplayed games" do
|
580
|
+
@t.player(1).find_result(1).opponent = nil
|
581
|
+
@t.player(6).find_result(1).opponent = nil
|
582
|
+
scores = @t.tie_break_scores("Neustadtl")
|
583
|
+
scores[1].should == 1.5 # 0.5 from Orr changed to 0
|
584
|
+
scores[2].should == 2.5 # didn't play Fischer or Orr so unaffected
|
585
|
+
scores[3].should == 1.0 # win against Minnie unaffected
|
586
|
+
scores[4].should == 1.0 # 0.5 from Orr changed to 1 (because Orr's unrated loss to Fischer now counts as a draw)
|
587
|
+
scores[5].should == 0.5 # 0.25 from Orr changed to 0.5
|
588
|
+
scores[6].should == 0.25 # loss against Fisher and unplayed against Fisher equivalent
|
589
|
+
end
|
590
|
+
|
591
|
+
it "should have correct Harkness tie break scores" do
|
592
|
+
scores = @t.tie_break_scores('harkness')
|
593
|
+
scores[1].should == 0.5
|
594
|
+
scores[2].should == 1.0
|
595
|
+
scores[3].should == 3.0
|
596
|
+
scores[4].should == 1.0
|
597
|
+
scores[5].should == 3.0
|
598
|
+
scores[6].should == 1.0
|
599
|
+
end
|
600
|
+
|
601
|
+
it "should have correct Modified Median tie break scores" do
|
602
|
+
scores = @t.tie_break_scores('Modified Median')
|
603
|
+
scores[1].should == 1.5
|
604
|
+
scores[2].should == 2.0
|
605
|
+
scores[3].should == 4.0
|
606
|
+
scores[4].should == 1.5
|
607
|
+
scores[5].should == 3.5
|
608
|
+
scores[6].should == 1.5
|
609
|
+
end
|
610
|
+
|
611
|
+
it "should have correct tie break scores for number of blacks" do
|
612
|
+
scores = @t.tie_break_scores('Blacks')
|
613
|
+
scores[3].should == 0
|
614
|
+
scores[4].should == 2
|
615
|
+
end
|
616
|
+
|
617
|
+
it "number of blacks should should be sensitive to unplayed games" do
|
618
|
+
@t.player(2).find_result(1).opponent = nil
|
619
|
+
@t.player(4).find_result(1).opponent = nil
|
620
|
+
scores = @t.tie_break_scores(:blacks)
|
621
|
+
scores[3].should == 0
|
622
|
+
scores[4].should == 1
|
623
|
+
end
|
624
|
+
|
625
|
+
it "should have correct tie break scores for number of wins" do
|
626
|
+
scores = @t.tie_break_scores(:wins)
|
627
|
+
scores[1].should == 3
|
628
|
+
scores[6].should == 0
|
629
|
+
end
|
630
|
+
|
631
|
+
it "number of wins should should be sensitive to unplayed games" do
|
632
|
+
@t.player(1).find_result(1).opponent = nil
|
633
|
+
@t.player(6).find_result(1).opponent = nil
|
634
|
+
scores = @t.tie_break_scores('WINS')
|
635
|
+
scores[1].should == 2
|
636
|
+
scores[6].should == 0
|
637
|
+
end
|
638
|
+
|
639
|
+
it "should use names for tie breaking by default" do
|
640
|
+
@t.rerank
|
641
|
+
@t.player(1).rank.should == 1 # 3.0/"Fischer"
|
642
|
+
@t.player(2).rank.should == 2 # 3.0/"Kasparov"
|
643
|
+
@t.player(3).rank.should == 3 # 1.0/"Mouse,Mickey"
|
644
|
+
@t.player(4).rank.should == 4 # 1.0/"Mouse,Minnie"
|
645
|
+
@t.player(6).rank.should == 5 # 0.5/"Ui"
|
646
|
+
@t.player(5).rank.should == 6 # 0.5/"Orr"
|
647
|
+
end
|
648
|
+
|
649
|
+
it "should be configurable to use Buchholz" do
|
650
|
+
@t.rerank('Buchholz')
|
651
|
+
@t.player(2).rank.should == 1 # 3.0/2.5
|
652
|
+
@t.player(1).rank.should == 2 # 3.0/2.0
|
653
|
+
@t.player(3).rank.should == 3 # 1.0/7.0
|
654
|
+
@t.player(4).rank.should == 4 # 1.0/4.5
|
655
|
+
@t.player(5).rank.should == 5 # 0.5/6.5
|
656
|
+
@t.player(6).rank.should == 6 # 0.5/4.5
|
657
|
+
end
|
658
|
+
|
659
|
+
it "should be configurable to use Neustadtl" do
|
660
|
+
@t.rerank(:neustadtl)
|
661
|
+
@t.player(2).rank.should == 1 # 3.0/2.5
|
662
|
+
@t.player(1).rank.should == 2 # 3.0/2.0
|
663
|
+
@t.player(3).rank.should == 3 # 1.0/1.0
|
664
|
+
@t.player(4).rank.should == 4 # 1.0/0.5
|
665
|
+
@t.player(6).rank.should == 5 # 0.5/0.25/"Orr"
|
666
|
+
@t.player(5).rank.should == 6 # 0.5/0.25/"Ui"
|
667
|
+
end
|
668
|
+
|
669
|
+
it "should be configurable to use number of blacks" do
|
670
|
+
@t.rerank(:blacks)
|
671
|
+
@t.player(2).rank.should == 1 # 3.0/2
|
672
|
+
@t.player(1).rank.should == 2 # 3.0/1
|
673
|
+
@t.player(4).rank.should == 3 # 1.0/2
|
674
|
+
@t.player(3).rank.should == 4 # 1.0/1
|
675
|
+
@t.player(6).rank.should == 5 # 0.5/2
|
676
|
+
@t.player(5).rank.should == 6 # 0.5/1
|
677
|
+
end
|
678
|
+
|
679
|
+
it "should be configurable to use number of wins" do
|
680
|
+
@t.rerank(:wins)
|
681
|
+
@t.player(1).rank.should == 1 # 3.0/3/"Fi"
|
682
|
+
@t.player(2).rank.should == 2 # 3.0/3/"Ka"
|
683
|
+
@t.player(3).rank.should == 3 # 1.0/1/"Mic"
|
684
|
+
@t.player(4).rank.should == 4 # 1.0/1/"Min"
|
685
|
+
@t.player(6).rank.should == 5 # 0.5/0/"Orr"
|
686
|
+
@t.player(5).rank.should == 6 # 0.5/0/"Ui"
|
687
|
+
end
|
688
|
+
|
689
|
+
it "should exhibit equivalence between Neustadtl and Sonneborn-Berger" do
|
690
|
+
@t.rerank('Sonneborn-Berger')
|
691
|
+
(1..6).inject(''){ |t,r| t << @t.player(r).rank.to_s }.should == '213465'
|
692
|
+
end
|
693
|
+
|
694
|
+
it "should be able to use more than one method" do
|
695
|
+
@t.rerank(:neustadtl, :buchholz)
|
696
|
+
@t.player(2).rank.should == 1 # 3.0/2.5
|
697
|
+
@t.player(1).rank.should == 2 # 3.0/2.0
|
698
|
+
@t.player(3).rank.should == 3 # 1.0/1.0
|
699
|
+
@t.player(4).rank.should == 4 # 1.0/0.5
|
700
|
+
@t.player(5).rank.should == 5 # 0.5/0.25/6.5
|
701
|
+
@t.player(6).rank.should == 6 # 0.5/0.25/4.5
|
702
|
+
end
|
703
|
+
|
704
|
+
it "should throw exception on invalid tie break method" do
|
705
|
+
lambda { @t.rerank(:no_such_tie_break_method) }.should raise_error(/invalid.*method/)
|
706
|
+
end
|
707
|
+
|
708
|
+
it "should throw exception on invalid tie break method via validation" do
|
709
|
+
lambda { @t.validate!(:rerank => :stupid_tie_break_method) }.should raise_error(/invalid.*method/)
|
710
|
+
end
|
711
|
+
|
712
|
+
it "should be possible as a side effect of validation" do
|
713
|
+
@t.invalid(:rerank => :buchholz).should be_false
|
714
|
+
@t.player(2).rank.should == 1 # 3/3
|
715
|
+
@t.player(1).rank.should == 2 # 3/2
|
716
|
+
@t.player(3).rank.should == 3 # 1/7
|
717
|
+
@t.player(4).rank.should == 4 # 1/4
|
718
|
+
@t.player(5).rank.should == 5 # 1/6
|
719
|
+
@t.player(6).rank.should == 6 # 0/5
|
720
|
+
end
|
721
|
+
|
722
|
+
it "should be possible as a side effect of validation with multiple tie break methods" do
|
723
|
+
@t.invalid(:rerank => [:neustadtl, :buchholz]).should be_false
|
724
|
+
@t.player(2).rank.should == 1 # 3/3
|
725
|
+
@t.player(1).rank.should == 2 # 3/2
|
726
|
+
@t.player(3).rank.should == 3 # 1/7
|
727
|
+
@t.player(4).rank.should == 4 # 1/4
|
728
|
+
@t.player(5).rank.should == 5 # 1/6
|
729
|
+
@t.player(6).rank.should == 6 # 0/5
|
730
|
+
end
|
731
|
+
end
|
732
|
+
end
|
733
|
+
end
|