icu_tournament 0.8.9

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