sanichi-chess_icu 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION.yml +1 -1
- data/lib/player.rb +8 -0
- data/lib/result.rb +12 -1
- data/lib/team.rb +9 -0
- data/lib/tournament.rb +43 -4
- data/spec/player_spec.rb +19 -0
- data/spec/result_spec.rb +19 -0
- data/spec/team_spec.rb +9 -0
- data/spec/tournament_krause_spec.rb +29 -3
- data/spec/tournament_spec.rb +34 -0
- metadata +2 -2
data/VERSION.yml
CHANGED
data/lib/player.rb
CHANGED
@@ -205,6 +205,14 @@ All other attributes are unaffected.
|
|
205
205
|
@results.inject(0.0) { |t, r| t += r.points }
|
206
206
|
end
|
207
207
|
|
208
|
+
# Renumber the player according to the supplied hash. Return self.
|
209
|
+
def renumber!(map)
|
210
|
+
raise "player number #{@num} not found in renumbering hash" unless map[@num]
|
211
|
+
self.num = map[@num]
|
212
|
+
@results.each{ |r| r.renumber!(map) }
|
213
|
+
self
|
214
|
+
end
|
215
|
+
|
208
216
|
# Loose equality test. Passes if the names match and the federations are not different.
|
209
217
|
def ==(other)
|
210
218
|
return true if equal?(other)
|
data/lib/result.rb
CHANGED
@@ -159,7 +159,7 @@ The _points_ read-only accessor always returns a floating point number: either 0
|
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
162
|
-
#
|
162
|
+
# Return a reversed version (from the opponent's perspective) of a result.
|
163
163
|
def reverse(rateable=nil)
|
164
164
|
return unless @opponent
|
165
165
|
r = Result.new(@round, @opponent, @score == 'W' ? 'L' : (@score == 'L' ? 'W' : 'D'))
|
@@ -170,6 +170,17 @@ The _points_ read-only accessor always returns a floating point number: either 0
|
|
170
170
|
r
|
171
171
|
end
|
172
172
|
|
173
|
+
# Renumber the player and opponent (if there is one) according to the supplied hash. Return self.
|
174
|
+
def renumber!(map)
|
175
|
+
raise "result player number #{@player} not found in renumbering hash" unless map[@player]
|
176
|
+
self.player = map[@player]
|
177
|
+
if @opponent
|
178
|
+
raise "result opponent number #{@opponent} not found in renumbering hash" unless map[@opponent]
|
179
|
+
self.opponent = map[@opponent]
|
180
|
+
end
|
181
|
+
self
|
182
|
+
end
|
183
|
+
|
173
184
|
# Loose equality. True if the round, player and opponent numbers, colour and score all match.
|
174
185
|
def ==(other)
|
175
186
|
return unless other.is_a? Result
|
data/lib/team.rb
CHANGED
@@ -68,6 +68,15 @@ Attempting to add non-numbers or duplicate numbers as new team members results i
|
|
68
68
|
@members.push(pnum)
|
69
69
|
end
|
70
70
|
|
71
|
+
# Renumber the players according to the supplied hash. Return self.
|
72
|
+
def renumber!(map)
|
73
|
+
@members.each_with_index do |pnum, index|
|
74
|
+
raise "player number #{pnum} not found in renumbering hash" unless map[pnum]
|
75
|
+
@members[index] = map[pnum]
|
76
|
+
end
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
71
80
|
# Detect if a member exists in a team.
|
72
81
|
def include?(number)
|
73
82
|
@members.include?(number)
|
data/lib/tournament.rb
CHANGED
@@ -52,7 +52,7 @@ Side effects of calling _validate!_ or _invalid_ include:
|
|
52
52
|
|
53
53
|
If the _rerank_ option is set, as in this example:
|
54
54
|
|
55
|
-
|
55
|
+
t.validate!(:rerank => true)
|
56
56
|
|
57
57
|
then there are additional side effects of validating a tournament:
|
58
58
|
|
@@ -61,6 +61,16 @@ then there are additional side effects of validating a tournament:
|
|
61
61
|
|
62
62
|
Ranking is consistent if either no players have any rank or if all players have a rank and no player is ranked higher than another player with more points.
|
63
63
|
|
64
|
+
The players in a tournament can be renumbered by rank or name. After any renumbering the player numbers,
|
65
|
+
which initially can be any collection of unique integers, will start at 1 and go up to the number of players.
|
66
|
+
|
67
|
+
t.renumber!(:name) # renumber by name
|
68
|
+
t.renumber!(:rank) # renumber by rank
|
69
|
+
t.renumber! # same - rank is the default
|
70
|
+
|
71
|
+
A side effect of renumbering by rank is that if the tournament started without any player ranking or
|
72
|
+
with inconsitent ranking, it will be reranked (i.e. the method _rerank_ will be called on it).
|
73
|
+
|
64
74
|
=end
|
65
75
|
|
66
76
|
class Tournament
|
@@ -234,7 +244,7 @@ Ranking is consistent if either no players have any rank or if all players have
|
|
234
244
|
end
|
235
245
|
end
|
236
246
|
|
237
|
-
# Rerank the tournament.
|
247
|
+
# Rerank the tournament by score, resolving ties using name.
|
238
248
|
def rerank
|
239
249
|
@player.values.map{ |p| [p, p.points] }.sort do |a,b|
|
240
250
|
d = b[1] <=> a[1]
|
@@ -245,6 +255,29 @@ Ranking is consistent if either no players have any rank or if all players have
|
|
245
255
|
v[0].rank = i + 1
|
246
256
|
end
|
247
257
|
end
|
258
|
+
|
259
|
+
# Renumber the players according to a given criterion. Return self.
|
260
|
+
def renumber!(criterion = :rank)
|
261
|
+
map = Hash.new
|
262
|
+
|
263
|
+
# Decide how to rank.
|
264
|
+
if criterion == :name
|
265
|
+
@player.values.sort_by{ |p| p.name }.each_with_index{ |p, i| map[p.num] = i + 1 }
|
266
|
+
else
|
267
|
+
begin check_ranks rescue rerank end
|
268
|
+
@player.values.each{ |p| map[p.num] = p.rank}
|
269
|
+
end
|
270
|
+
|
271
|
+
# Apply ranking.
|
272
|
+
@teams.each{ |t| t.renumber!(map) }
|
273
|
+
@player = @player.values.inject({}) do |hash, player|
|
274
|
+
player.renumber!(map)
|
275
|
+
hash[player.num] = player
|
276
|
+
hash
|
277
|
+
end
|
278
|
+
|
279
|
+
self
|
280
|
+
end
|
248
281
|
|
249
282
|
# Is a tournament invalid? Either returns false (if it's valid) or an error message.
|
250
283
|
def invalid(options={})
|
@@ -273,7 +306,13 @@ Ranking is consistent if either no players have any rank or if all players have
|
|
273
306
|
# Check players.
|
274
307
|
def check_players
|
275
308
|
raise "the number of players (#{@player.size}) must be at least 2" if @player.size < 2
|
276
|
-
@player.each
|
309
|
+
@player.each do |num, p|
|
310
|
+
raise "player #{num} has no results" if p.results.size == 0
|
311
|
+
p.results.each do |r|
|
312
|
+
next unless r.opponent
|
313
|
+
raise "opponent #{r.opponent} of player #{num} is not in the tournament" unless @player[r.opponent]
|
314
|
+
end
|
315
|
+
end
|
277
316
|
end
|
278
317
|
|
279
318
|
# Round should go from 1 to a maximum, there should be at least one result in every round and,
|
@@ -330,7 +369,7 @@ Ranking is consistent if either no players have any rank or if all players have
|
|
330
369
|
# * no two players have the same rank
|
331
370
|
# * the highest rank is 1
|
332
371
|
# * the lowest rank is equal to the total of players
|
333
|
-
def check_ranks(options)
|
372
|
+
def check_ranks(options={})
|
334
373
|
ranks = Hash.new
|
335
374
|
@player.values.each do |p|
|
336
375
|
if p.rank
|
data/spec/player_spec.rb
CHANGED
@@ -236,6 +236,25 @@ module ICU
|
|
236
236
|
end
|
237
237
|
end
|
238
238
|
|
239
|
+
context "renumber the player numbers" do
|
240
|
+
before(:each) do
|
241
|
+
@p = Player.new('Mark', 'Orr', 10)
|
242
|
+
@p.add_result(Result.new(1, 10, 'W', :opponent => 20))
|
243
|
+
@p.add_result(Result.new(2, 10, 'W', :opponent => 30))
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should renumber successfully if the map has the relevant player numbers" do
|
247
|
+
map = { 10 => 1, 20 => 2, 30 => 3 }
|
248
|
+
@p.renumber!(map).num.should == 1
|
249
|
+
@p.results.map{ |r| r.opponent }.sort.join('').should == '23'
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should raise exception if a player number is not in the map" do
|
253
|
+
lambda { @p.renumber!({ 100 => 1, 20 => 2, 30 => 3 }) }.should raise_error(/player.*10.*not found/)
|
254
|
+
lambda { @p.renumber!({ 10 => 1, 200 => 2, 30 => 3 }) }.should raise_error(/opponent.*20.*not found/)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
239
258
|
context "loose equality" do
|
240
259
|
before(:all) do
|
241
260
|
@mark1 = Player.new('Mark', 'Orr', 1)
|
data/spec/result_spec.rb
CHANGED
@@ -138,6 +138,25 @@ module ICU
|
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
|
+
context "renumber the player numbers in a result" do
|
142
|
+
before(:each) do
|
143
|
+
@r1 = Result.new(1, 4, 0)
|
144
|
+
@r2 = Result.new(2, 3, 1, :opponent => 4, :color => 'B')
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should renumber successfully if the map has the relevant player numbers" do
|
148
|
+
map = { 4 => 1, 3 => 2 }
|
149
|
+
@r1.renumber!(map).player.should == 1
|
150
|
+
@r2.renumber!(map).player.should == 2
|
151
|
+
@r1.opponent.should be_nil
|
152
|
+
@r2.opponent.should == 1
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should raise exception if a player number is not in the map" do
|
156
|
+
lambda { @r1.renumber!({ 5 => 1, 3 => 2 }) }.should raise_error(/player.*4.*not found/)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
141
160
|
context "loose equality" do
|
142
161
|
before(:each) do
|
143
162
|
@r1 = Result.new(1, 1, 0, :opponent => 2, :colour => 'W')
|
data/spec/team_spec.rb
CHANGED
@@ -45,6 +45,15 @@ module ICU
|
|
45
45
|
it "should blow up if an attempt is made to add a duplicate number" do
|
46
46
|
lambda { @t.add_member(3) }.should raise_error(/duplicate/)
|
47
47
|
end
|
48
|
+
|
49
|
+
it "should renumber successfully if the map has the relevant player numbers" do
|
50
|
+
map = { 0 => 1, 3 => 2, -7 => 3 }
|
51
|
+
@t.renumber!(map).members.sort.join('').should == '123'
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should raise exception if a player is missing from the renumber map" do
|
55
|
+
lambda { @t.renumber!({ 5 => 1, 3 => 2 }) }.should raise_error(/player.*not found/)
|
56
|
+
end
|
48
57
|
end
|
49
58
|
end
|
50
59
|
end
|
@@ -171,15 +171,15 @@ KRAUSE
|
|
171
171
|
KRAUSE
|
172
172
|
@p = Krause.new
|
173
173
|
@t = @p.parse!(@krause)
|
174
|
-
@
|
174
|
+
@q = Krause.new
|
175
175
|
end
|
176
176
|
|
177
177
|
it "should serialize back to the original if the input is fully canonicalised" do
|
178
|
-
@
|
178
|
+
@q.serialize(@t).should == @krause
|
179
179
|
end
|
180
180
|
|
181
181
|
it "should return nil on invalid input" do
|
182
|
-
@
|
182
|
+
@q.serialize('Rubbish').should be_nil
|
183
183
|
end
|
184
184
|
end
|
185
185
|
|
@@ -202,6 +202,32 @@ KRAUSE
|
|
202
202
|
@t.player(3).rank.should == 3
|
203
203
|
end
|
204
204
|
end
|
205
|
+
|
206
|
+
context "renumbering" do
|
207
|
+
before(:all) do
|
208
|
+
@krause = <<KRAUSE
|
209
|
+
012 Las Vegas National Open
|
210
|
+
042 2008-06-07
|
211
|
+
001 10 w Ui Laighleis,Gearoidin 1985 IRL 1.0 20 b 0 30 w 1
|
212
|
+
001 20 m m Orr,Mark 2258 IRL 2.0 10 w 1 30 b 1
|
213
|
+
001 30 m g Bologan,Viktor 2663 MDA 0.0 10 b 0 20 w 0
|
214
|
+
KRAUSE
|
215
|
+
@p = Krause.new
|
216
|
+
@t = @p.parse!(@krause)
|
217
|
+
@reordered = <<REORDERED
|
218
|
+
012 Las Vegas National Open
|
219
|
+
042 2008-06-07
|
220
|
+
001 1 m m Orr,Mark 2258 IRL 2.0 1 2 w 1 3 b 1
|
221
|
+
001 2 w Ui Laighleis,Gearoidin 1985 IRL 1.0 2 1 b 0 3 w 1
|
222
|
+
001 3 m g Bologan,Viktor 2663 MDA 0.0 3 2 b 0 1 w 0
|
223
|
+
REORDERED
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should serialise correctly after renumbering by rank" do
|
227
|
+
@t.renumber!
|
228
|
+
@p.serialize(@t).should == @reordered
|
229
|
+
end
|
230
|
+
end
|
205
231
|
|
206
232
|
context "errors" do
|
207
233
|
before(:each) do
|
data/spec/tournament_spec.rb
CHANGED
@@ -417,5 +417,39 @@ module ICU
|
|
417
417
|
@t.invalid.should match(/already.*member/)
|
418
418
|
end
|
419
419
|
end
|
420
|
+
|
421
|
+
context "renumbering" do
|
422
|
+
before(:each) do
|
423
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09')
|
424
|
+
@t.add_player(@mark = Player.new('Mark', 'Orr', 10))
|
425
|
+
@t.add_player(@gary = Player.new('Gary', 'Kasparov', 20))
|
426
|
+
@t.add_player(@boby = Player.new('Bobby', 'Fischer', 30))
|
427
|
+
@t.add_result(Result.new(1, 10, 'W', :opponent => 20))
|
428
|
+
@t.add_result(Result.new(2, 20, 'W', :opponent => 30))
|
429
|
+
@t.add_result(Result.new(3, 30, 'L', :opponent => 10))
|
430
|
+
end
|
431
|
+
|
432
|
+
it "sample tournament is valid but unranked" do
|
433
|
+
@t.invalid.should be_false
|
434
|
+
@t.player(10).rank.should be_nil
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should be renumberable by rank" do
|
438
|
+
@t.renumber!.invalid.should be_false
|
439
|
+
@t.players.map{ |p| p.num }.join('|').should == '1|2|3'
|
440
|
+
@t.players.map{ |p| p.first_name }.join('|').should == 'Mark|Gary|Bobby'
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should be ranked after renumbering by rank" do
|
444
|
+
@t.renumber!.invalid.should be_false
|
445
|
+
@t.players.map{ |p| p.rank }.join('|').should == '1|2|3'
|
446
|
+
end
|
447
|
+
|
448
|
+
it "should be renumberable by name" do
|
449
|
+
@t.renumber!(:name).invalid.should be_false
|
450
|
+
@t.players.map{ |p| p.num }.join('|').should == '1|2|3'
|
451
|
+
@t.players.map{ |p| p.last_name }.join('|').should == 'Fischer|Kasparov|Orr'
|
452
|
+
end
|
453
|
+
end
|
420
454
|
end
|
421
455
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sanichi-chess_icu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Orr
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-05-
|
12
|
+
date: 2009-05-17 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|