sanichi-chess_icu 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 3
4
- :patch: 3
4
+ :patch: 4
@@ -33,6 +33,27 @@ Note that the players should be added first because the _add_result_ method will
33
33
  raise an exception if the players it references through their tournament numbers
34
34
  (10, 20 and 30 in this example) have not already been added to the tournament.
35
35
 
36
+ A tournament can be validated with either the _validate!_ or _invalid_ methods.
37
+ On success, the first returns true while the second returns false.
38
+ On error, the first throws an exception while the second returns a description of the message.
39
+
40
+ Validations checks that:
41
+
42
+ * there are at least two players
43
+ * every player has a least one result
44
+ * the round numbers are consistent
45
+ * the tournament dates are consistent
46
+ * the player ranks are consistent (only if the _rank_ or _rerank_ option is set)
47
+
48
+ Both messages are capable of taking the following boolean valued hash options:
49
+
50
+ * _rank_ - check the player ranks
51
+ * _rerank_ - check the player ranks and automatically repair if absent or not consistent
52
+
53
+ For example:
54
+
55
+ tournament.validate!(:rank => true)
56
+
36
57
  =end
37
58
 
38
59
  class Tournament
@@ -175,78 +196,105 @@ raise an exception if the players it references through their tournament numbers
175
196
  @player[result.opponent].add_result(reverse)
176
197
  end
177
198
  end
199
+
200
+ # Rerank the tournament.
201
+ def rerank
202
+ @player.values.map{ |p| [p, p.points] }.sort do |a,b|
203
+ d = b[1] <=> a[1]
204
+ d = a[0].last_name <=> b[0].last_name if d == 0
205
+ d = a[0].first_name <=> b[0].first_name if d == 0
206
+ d
207
+ end.each_with_index do |v,i|
208
+ v[0].rank = i + 1
209
+ end
210
+ end
211
+
212
+ # Is a tournament invalid? Either returns false (if it's valid) or an error message.
213
+ def invalid(options={})
214
+ begin
215
+ validate!(options)
216
+ rescue => err
217
+ return err.message
218
+ end
219
+ false
220
+ end
221
+
222
+ # Raise an exception if a tournament is not valid.
223
+ # Covers all the ways a tournament can be invalid not already enforced by the setters.
224
+ def validate!(options={})
225
+ begin check_ranks rescue rerank end if options[:rerank]
226
+ check_players
227
+ check_dates
228
+ check_rounds
229
+ check_ranks if options[:rank]
230
+ true
231
+ end
232
+
233
+ private
234
+
235
+ # Check players.
236
+ def check_players
237
+ raise "the number of players (#{@player.size}) must be at least 2" if @player.size < 2
238
+ @player.each { |num, p| raise "player #{num} has no results" if p.results.size == 0 }
239
+ end
178
240
 
179
- # Return true if the players ranking is consistent, false otherwise.
180
- # The players ranking is consistent if:
241
+ # Check dates are consistent.
242
+ def check_dates
243
+ # If there is a start date and an end date, the start should not come after the end.
244
+ raise "start date (#{start}) is after end date (#{finish})" if start && finish && start > finish
245
+ end
246
+
247
+ # Round should go from 1 to a maximum, there should be at least one result in every round and,
248
+ # if the number of rounds has been set, it should agree with the largest round from the results.
249
+ def check_rounds
250
+ round = Hash.new
251
+ round_last = 0
252
+ @player.values.each do |p|
253
+ p.results.each do |r|
254
+ round[r.round] = true
255
+ round_last = r.round if r.round > round_last
256
+ end
257
+ end
258
+ (1..round_last).each { |r| raise "there are no results for round #{r}" unless round[r] }
259
+ if rounds
260
+ raise "declared number of rounds is #{rounds} but there are results in later rounds, such as #{round_last}" if rounds < round_last
261
+ raise "declared number of rounds is #{rounds} but there are no results with rounds greater than #{round_last}" if rounds > round_last
262
+ else
263
+ self.rounds = round_last
264
+ end
265
+ end
266
+
267
+ # Check if the players ranking is consistent, which will be true if:
181
268
  # * every player has a rank
182
269
  # * no two players have the same rank
183
270
  # * the highest rank is 1
184
271
  # * the lowest rank is equal to the total of players
185
- def ranking_consistent?(option={})
272
+ def check_ranks
186
273
  # No two players can have the same rank.
187
274
  ranks = Hash.new
188
275
  @player.values.each do |p|
189
276
  if p.rank
190
- return false if ranks[p.rank]
277
+ raise "two players have the same rank #{p.rank}" if ranks[p.rank]
191
278
  ranks[p.rank] = p
192
279
  end
193
280
  end
194
-
195
- # The special case of complete absence of ranking information is an option.
196
- return true if ranks.size == 0 && option[:allow_none]
197
281
 
198
- # Otherwie, every player has to have a rank.
199
- return false unless ranks.size == @player.size
282
+ # Otherwise, every player has to have a rank.
283
+ raise "every player has to have a rank" unless ranks.size == @player.size
200
284
 
201
285
  # The higest and lowest ranks respectively should be 1 and the number of players.
202
286
  by_rank = @player.values.sort{ |a,b| a.rank <=> b.rank}
203
- return false unless by_rank[0].rank == 1
204
- return false unless by_rank[-1].rank == ranks.size
287
+ raise "the highest rank must be 1" unless by_rank[0].rank == 1
288
+ raise "the lowest rank must be #{ranks.size}" unless by_rank[-1].rank == ranks.size
205
289
 
206
290
  # If scores are ordered by ranks, they should go from highest to lowest.
207
291
  if by_rank.size > 1
208
292
  (1..by_rank.size-1).each do |i|
209
293
  p1 = by_rank[i-1]
210
294
  p2 = by_rank[i]
211
- return false if p1.points < p2.points
295
+ raise "player #{p1.num} with #{p1.points} points is ranked above player #{p2.num} with #{p2.points} points" if p1.points < p2.points
212
296
  end
213
297
  end
214
-
215
- true
216
- end
217
-
218
- # Rerank the tournament.
219
- def rerank
220
- @player.values.map{ |p| [p, p.points] }.sort do |a,b|
221
- d = b[1] <=> a[1]
222
- d = a[0].last_name <=> b[0].last_name if d == 0
223
- d = a[0].first_name <=> b[0].first_name if d == 0
224
- d
225
- end.each_with_index do |v,i|
226
- v[0].rank = i + 1
227
- end
228
- end
229
-
230
- # Raise an exception if a tournament is not valid.
231
- def validate!
232
- error = invalid
233
- raise error if error
234
- end
235
-
236
- # Is a tournament invalid? Either returns false (if it's valid) or an error message.
237
- # Covers all the ways a tournament can be invalid not already enforced by the setters.
238
- def invalid
239
- # There must be at least two players.
240
- return "number of players (#{@player.size}) must be at least 2" if @player.size < 2
241
-
242
- # Every player must have at least one result.
243
- @player.each { |num, p| return "player #{num} has no results" if p.results.size == 0 }
244
-
245
- # The ranking should be consistent, with the proviso that no ranking at all is allowed.
246
- return "ranking is not consistent" unless ranking_consistent?(:allow_none => true)
247
-
248
- # If there is a start date and an end date, the start should not come after the end.
249
- return "start date (#{start}) is after end date (#{finish})" if start && finish && start > finish
250
298
  end
251
299
  end
252
300
  end
@@ -149,11 +149,12 @@ The following lists Krause data identification numbers, their description and, w
149
149
  end
150
150
  end
151
151
 
152
- # Now that we have everything, perform final checks and tidy ups.
153
- finish_up
152
+ # Certain attributes are mandatory and should have been specifically set.
153
+ raise "tournament name missing" unless @name_set
154
+ raise "tournament start date missing" unless @start_set
154
155
 
155
- # Finally, exercise the tournament object's own internal validation.
156
- @tournament.validate!
156
+ # Finally, exercise the tournament object's internal validation, reranking if neccessary.
157
+ @tournament.validate!(:rerank => true)
157
158
 
158
159
  @tournament
159
160
  end
@@ -239,21 +240,6 @@ The following lists Krause data identification numbers, their description and, w
239
240
  @comments << @line
240
241
  @comments << "\n"
241
242
  end
242
-
243
- def finish_up
244
- # Certain attributes are mandatory and should have been specifically set.
245
- raise "tournament name missing" unless @name_set
246
- raise "tournament start date missing" unless @start_set
247
-
248
- # Set the number of rounds.
249
- @tournament.rounds = @tournament.players.inject(0) do |pa, p|
250
- pm = p.results.inject(0){ |ra, r| ra < r.round ? r.round : ra }
251
- pa < pm ? pm : pa
252
- end
253
-
254
- # Rerank the tournament if there are no ranking values or if there are, but they're not consistent.
255
- @tournament.rerank unless @tournament.ranking_consistent?
256
- end
257
243
  end
258
244
  end
259
245
 
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.3.3
4
+ version: 0.3.4
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-05 00:00:00 -07:00
12
+ date: 2009-05-09 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency