sanichi-chess_icu 0.3.5 → 0.4.0

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
- :minor: 3
4
- :patch: 5
3
+ :minor: 4
4
+ :patch: 0
@@ -2,7 +2,7 @@
2
2
 
3
3
  chess_icu_files = Array.new
4
4
  chess_icu_files.concat %w{util name federation}
5
- chess_icu_files.concat %w{player result tournament}
5
+ chess_icu_files.concat %w{player result team tournament}
6
6
  chess_icu_files.concat %w{fcsv krause}.map{ |f| "tournament_#{f}"}
7
7
 
8
8
  dir = File.dirname(__FILE__)
@@ -0,0 +1,81 @@
1
+ module ICU
2
+
3
+ =begin rdoc
4
+
5
+ == Team
6
+
7
+ A team consists of a name and one or more players referenced by numbers.
8
+ Typically the team will be attached to a tournament (ICU::Tournament)
9
+ and the numbers will the unique numbers by which the players in that
10
+ tournament are referenced. To instantiate a team, you must supply a
11
+ name.
12
+
13
+ team = ICU::Team.new('Wandering Dragons')
14
+
15
+ Then you simply add player's (numbers) to it.
16
+
17
+ team.add_player(1)
18
+ team.add_payeer(3)
19
+ team.add_player(7)
20
+
21
+ To get the current members of a team
22
+
23
+ team.members # => [1, 3, 7]
24
+
25
+ You can enquire whether a team contains a given player number.
26
+
27
+ team.contains?(3) # => true
28
+ team.contains?(4) # => false
29
+
30
+ Or whether it matches a given name (which ignoring case and removing spurious whitespace)
31
+
32
+ team.matches(' wandering dragons ') # => true
33
+ team.matches('Blundering Bishops') # => false
34
+
35
+ Whenever you reset the name of a tournament spurious whitespace is removed but case is not altered.
36
+
37
+ team.name = ' blundering bishops '
38
+ team.name # => "blundering bishops"
39
+
40
+ Attempting to add non-numbers or duplicate numbers as new team members results in an exception.
41
+
42
+ team.add(nil) # exception - not a number
43
+ team.add(3) # exception - already a member
44
+
45
+ =end
46
+
47
+ class Team
48
+
49
+ attr_reader :name, :members
50
+
51
+ # Constructor. Name must be supplied.
52
+ def initialize(name)
53
+ self.name = name
54
+ @members = Array.new
55
+ end
56
+
57
+ # Set name. Must not be blank.
58
+ def name=(name)
59
+ @name = name.strip.squeeze(' ')
60
+ raise "team can't be blank" if @name.length == 0
61
+ end
62
+
63
+ # Add a team member referenced by any integer.
64
+ def add_member(number)
65
+ pnum = number.to_i
66
+ raise "'#{number}' is not a valid as a team member player number" if pnum == 0 && !number.to_s.match(/^[^\d]*0/)
67
+ raise "can't add duplicate player number #{pnum} to team '#{@name}'" if @members.include?(pnum)
68
+ @members.push(pnum)
69
+ end
70
+
71
+ # Detect if a member exists in a team.
72
+ def include?(number)
73
+ @members.include?(number)
74
+ end
75
+
76
+ # Does the team name match the given string (ignoring case and spurious whitespace).
77
+ def matches(name)
78
+ self.name.downcase == name.strip.squeeze(' ').downcase
79
+ end
80
+ end
81
+ end
@@ -64,7 +64,7 @@ Ranking is consistent if either no players have any rank or if all players have
64
64
  =end
65
65
 
66
66
  class Tournament
67
- attr_reader :name, :rounds, :start, :finish, :round_dates, :site, :city, :fed, :type, :arbiter, :deputy, :time_control
67
+ attr_reader :name, :rounds, :start, :finish, :round_dates, :site, :city, :fed, :type, :arbiter, :deputy, :time_control, :teams
68
68
 
69
69
  # Constructor. Name and start date must be supplied. Other attributes are optional.
70
70
  def initialize(name, start, opt={})
@@ -72,6 +72,7 @@ Ranking is consistent if either no players have any rank or if all players have
72
72
  self.start = start
73
73
  [:finish, :rounds, :site, :city, :fed, :type, :arbiter, :deputy, :time_control].each { |a| self.send("#{a}=", opt[a]) unless opt[a].nil? }
74
74
  @player = {}
75
+ @teams = []
75
76
  @round_dates = []
76
77
  end
77
78
 
@@ -178,6 +179,20 @@ Ranking is consistent if either no players have any rank or if all players have
178
179
  raise "invalid tournament time control (#{time_control})" unless @time_control.nil? || @time_control.match(/[1-9]\d/)
179
180
  end
180
181
 
182
+ # Add a new team. The argument is either a team (possibly already with members) or the name of a new team.
183
+ # The team's name must be unique in the tournament. Returns the the team instance.
184
+ def add_team(team)
185
+ team = Team.new(team.to_s) unless team.is_a? Team
186
+ raise "a team with a name similar to '#{team.name}' already exists" if self.get_team(team.name)
187
+ @teams << team
188
+ team
189
+ end
190
+
191
+ # Return the team object that matches a given name, or nil if not found.
192
+ def get_team(name)
193
+ @teams.find{ |t| t.matches(name) }
194
+ end
195
+
181
196
  # Add a new player to the tournament. Must have a unique player number.
182
197
  def add_player(player)
183
198
  raise "invalid player" unless player.class == ICU::Player
@@ -248,6 +263,7 @@ Ranking is consistent if either no players have any rank or if all players have
248
263
  check_players
249
264
  check_rounds
250
265
  check_dates
266
+ check_teams
251
267
  check_ranks(:allow_none => true)
252
268
  true
253
269
  end
@@ -290,6 +306,24 @@ Ranking is consistent if either no players have any rank or if all players have
290
306
  @finish = @round_dates[-1] unless @finish
291
307
  end
292
308
  end
309
+
310
+ # Check teams. Either there are none or:
311
+ # * every team member is a valid player, and
312
+ # * every player is a member of exactly one team.
313
+ def check_teams
314
+ return if @teams.size == 0
315
+ member = Hash.new
316
+ @teams.each do |t|
317
+ t.members.each do |m|
318
+ raise "member #{m} of team '#{t.name}' is not a valid player number for this tournament" unless @player[m]
319
+ raise "member #{m} of team '#{t.name}' is already a member of team #{member[m]}" if member[m]
320
+ member[m] = t.name
321
+ end
322
+ end
323
+ @player.keys.each do |p|
324
+ raise "player #{p} is not a member of any team" unless member[p]
325
+ end
326
+ end
293
327
 
294
328
  # Check if the players ranking is consistent, which will be true if:
295
329
  # * every player has a rank
@@ -65,7 +65,7 @@ The following lists Krause data identification numbers, their description and, w
65
65
 
66
66
  [001 Player record] Use _players_ to get all players or _player_ with a player number to get a single instance.
67
67
  [012 Name] Get or set with _name_. Free text. A tounament name is mandatory.
68
- [013 Teams] Not implemented yet.
68
+ [013 Teams] Create an ICU::Team, add player numbers to it, use _add_team_ to add to tournament, _get_team_/_teams_ to retrive it/them.
69
69
  [022 City] Get or set with _city_. Free text.
70
70
  [032 Federation] Get or set with _fed_. Getter returns either _nil_ or a three letter code. Setter can take various formats (see ICU::Federation).
71
71
  [042 Start date] Get or set with _start_. Getter returns <em>yyyy-mm-dd</em> format, but setter can use any reasonable date format. Start date is mandadory.
@@ -124,7 +124,7 @@ The following lists Krause data identification numbers, their description and, w
124
124
  case din
125
125
  when '001' then add_player # player and results record
126
126
  when '012' then set_name # name (mandatory)
127
- when '013' then add_comment # team name and members (not implemented yet)
127
+ when '013' then add_team # team name and members
128
128
  when '022' then @tournament.city = @data # city
129
129
  when '032' then @tournament.fed = @data # federation
130
130
  when '042' then set_start # start date (mandatory)
@@ -177,6 +177,11 @@ The following lists Krause data identification numbers, their description and, w
177
177
  krause << "102 #{t.arbiter}\n" if t.arbiter
178
178
  krause << "112 #{t.deputy}\n" if t.deputy
179
179
  krause << "122 #{t.time_control}\n" if t.time_control
180
+ t.teams.each do |team|
181
+ krause << sprintf('013 %-31s', team.name)
182
+ team.members.each{ |m| krause << sprintf(' %4d', m) }
183
+ krause << "\n"
184
+ end
180
185
  if t.round_dates.size > 0
181
186
  krause << "132 #{' ' * 85}"
182
187
  t.round_dates.each{ |d| krause << d.sub(/^../, ' ') }
@@ -246,6 +251,17 @@ The following lists Krause data identification numbers, their description and, w
246
251
  result.points
247
252
  end
248
253
 
254
+ def add_team
255
+ raise error "team record less than minimum length" if @line.length < 40
256
+ team = Team.new(@data[0, 31])
257
+ index = 32
258
+ while @data.length >= index + 4
259
+ team.add_member(@data[index, 4])
260
+ index+= 5
261
+ end
262
+ @tournament.add_team(team)
263
+ end
264
+
249
265
  def add_round_dates
250
266
  raise "round dates record less than minimum length" if @line.length < 99
251
267
  index = 87
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module ICU
4
+ describe Team do
5
+ context "a typical team" do
6
+ before(:each) do
7
+ @t = Team.new('Wandering Dragons')
8
+ @t.add_member(0)
9
+ @t.add_member('3')
10
+ @t.add_member(' -7 ')
11
+ end
12
+
13
+ it "should have a name" do
14
+ @t.name.should == 'Wandering Dragons'
15
+ end
16
+
17
+ it "should have some members" do
18
+ @t.should have(3).members
19
+ @t.include?(0).should be_true
20
+ @t.include?(3).should be_true
21
+ @t.include?(-7).should be_true
22
+ @t.include?(7).should be_false
23
+ end
24
+
25
+ it "should match names ignoring case and irrelevant whitespace" do
26
+ @t.matches('Wandering Dragons').should be_true
27
+ @t.matches(' wandering dragons ').should be_true
28
+ @t.matches(' wanderingdragons ').should be_false
29
+ @t.matches('Blundering Bishops').should be_false
30
+ end
31
+
32
+ it "should have a changeable name with irrelevant whitespace being trimmed" do
33
+ @t.name = ' blue dragons '
34
+ @t.name.should == 'blue dragons'
35
+ end
36
+
37
+ it "should blowup if an attempt is made to blank the name" do
38
+ lambda { @t.name = ' ' }.should raise_error(/blank/)
39
+ end
40
+
41
+ it "should blowup if an attempt is made to add a non-number" do
42
+ lambda { @t.add_member(' ') }.should raise_error(/number/)
43
+ end
44
+
45
+ it "should blow up if an attempt is made to add a duplicate number" do
46
+ lambda { @t.add_member(3) }.should raise_error(/duplicate/)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -37,7 +37,7 @@ module ICU
37
37
  102 Hans Scmidt
38
38
  112 Gerry Graham, Herbert Scarry
39
39
  122 60 in 2hr, 30 in 1hr, rest in 1hr
40
- 013 Coaching Team 1 2
40
+ 013 Coaching Team 1 2 3
41
41
  0 1 2 3 4 5 6 7 8 9 0 1 2
42
42
  0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
43
43
  132 08.06.07 08.06.08 08.06.09
@@ -93,7 +93,6 @@ KRAUSE
93
93
  062 3
94
94
  072 3
95
95
  082 1
96
- 013 Coaching Team 1 2
97
96
  0 1 2 3 4 5 6 7 8 9 0 1 2
98
97
  0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
99
98
  COMMENTS
@@ -162,6 +161,8 @@ KRAUSE
162
161
  102 Hans Scmidt
163
162
  112 Gerry Graham, Herbert Scarry
164
163
  122 60 in 2hr, 30 in 1hr, rest in 1hr
164
+ 013 Boys 2 3
165
+ 013 Girls 1 4
165
166
  132 08-06-07 08-06-08 08-06-09
166
167
  001 1 w Ui Laighleis,Gearoidin 1985 IRL 2501171 1964-06-10 2.0 2 2 b 0 3 w + 4 b 1
167
168
  001 2 m m Orr,Mark 2258 IRL 2500035 1955-11-09 2.5 1 1 w 1 0000 - = 3 b 1
@@ -294,7 +294,32 @@ module ICU
294
294
  @t.find_player(Player.new('John', 'Orr', 4, :fed => 'IRL')).should be_nil
295
295
  end
296
296
  end
297
+
298
+ context "teams" do
299
+ before(:each) do
300
+ @t = Tournament.new('Bangor Bash', '2009-11-09')
301
+ end
297
302
 
303
+ it "should be able to create a new team, add it and retrieve it" do
304
+ team = Team.new('Wandering Dragons')
305
+ @t.add_team(team).should be_an_instance_of Team
306
+ @t.get_team(' wandering dragons ').should be_an_instance_of Team
307
+ @t.get_team('Blundering Bishops').should be_nil
308
+ end
309
+
310
+ it "should be able to create and add a new team and retrieve it" do
311
+ @t.add_team('Blundering Bishops').should be_an_instance_of Team
312
+ @t.get_team(' blundering bishops ').should be_an_instance_of Team
313
+ @t.get_team('Wandering Dragons').should be_nil
314
+ end
315
+
316
+ it "should throw and exception if there is an attempt to add a team with a name that matches an existing team" do
317
+ lambda { @t.add_team('Blundering Bishops') }.should_not raise_error
318
+ lambda { @t.add_team('Wandering Dragons') }.should_not raise_error
319
+ lambda { @t.add_team(' wandering dragons ') }.should raise_error(/similar.*exists/)
320
+ end
321
+ end
322
+
298
323
  context "validation" do
299
324
  before(:each) do
300
325
  @t = Tournament.new('Edinburgh Masters', '2009-11-09')
@@ -364,6 +389,33 @@ module ICU
364
389
  @t.player(3).rank = 3
365
390
  lambda { @t.validate! }.should raise_error(/player 2.*above.*player 1/)
366
391
  end
392
+
393
+ it "should be valid if there are teams, every player is in one of them, and no team has an invalid member" do
394
+ team1 = Team.new('International Masters')
395
+ team2 = Team.new('World Champions')
396
+ @t.add_team(team1)
397
+ @t.add_team(team2)
398
+ @t.invalid.should match(/not.*member/)
399
+ team1.add_member(1)
400
+ team2.add_member(2)
401
+ team2.add_member(3)
402
+ @t.invalid.should be_false
403
+ team1.add_member(4)
404
+ @t.invalid.should match(/not.*valid/)
405
+ end
406
+
407
+ it "should not be valid if one player is in more than one team" do
408
+ team1 = Team.new('XInternational Masters')
409
+ team1.add_member(1)
410
+ team2 = Team.new('XWorld Champions')
411
+ team2.add_member(2)
412
+ team2.add_member(3)
413
+ @t.add_team(team1)
414
+ @t.add_team(team2)
415
+ @t.invalid.should be_false
416
+ team1.add_member(2)
417
+ @t.invalid.should match(/already.*member/)
418
+ end
367
419
  end
368
420
  end
369
421
  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.3.5
4
+ version: 0.4.0
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-09 00:00:00 -07:00
12
+ date: 2009-05-10 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -42,6 +42,7 @@ files:
42
42
  - lib/name.rb
43
43
  - lib/player.rb
44
44
  - lib/result.rb
45
+ - lib/team.rb
45
46
  - lib/tournament.rb
46
47
  - lib/tournament_fcsv.rb
47
48
  - lib/tournament_krause.rb
@@ -51,6 +52,7 @@ files:
51
52
  - spec/player_spec.rb
52
53
  - spec/result_spec.rb
53
54
  - spec/spec_helper.rb
55
+ - spec/team_spec.rb
54
56
  - spec/tournament_fcsv_spec.rb
55
57
  - spec/tournament_krause_spec.rb
56
58
  - spec/tournament_spec.rb
@@ -87,6 +89,7 @@ test_files:
87
89
  - spec/player_spec.rb
88
90
  - spec/result_spec.rb
89
91
  - spec/spec_helper.rb
92
+ - spec/team_spec.rb
90
93
  - spec/tournament_fcsv_spec.rb
91
94
  - spec/tournament_krause_spec.rb
92
95
  - spec/tournament_spec.rb