sanichi-chess_icu 0.2.7 → 0.2.8
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 +19 -9
- data/lib/tournament.rb +66 -4
- data/lib/tournament_fcsv.rb +39 -0
- data/spec/player_spec.rb +16 -0
- data/spec/tournament_fcsv_spec.rb +31 -0
- data/spec/tournament_spec.rb +124 -0
- metadata +1 -1
data/VERSION.yml
CHANGED
data/lib/player.rb
CHANGED
@@ -47,14 +47,15 @@ The player number is irrelevant.
|
|
47
47
|
john1 == john2 # => true (federations don't disagree because one is unset)
|
48
48
|
john2 == john3 # => false (federations disagree)
|
49
49
|
|
50
|
-
If, in addition,
|
50
|
+
If, in addition, _rating_, _dob_, _gender_ and _id_ do not disagree then two players are equal
|
51
|
+
according to the stricter criteria of _eql?_.
|
51
52
|
|
52
53
|
mark1 = ICU::Player.new('Mark', 'Orr', 31, :fed = 'IRL', :rating => 2100)
|
53
54
|
mark2 = ICU::Player.new('Mark', 'Orr', 33, :fed = 'IRL', :rating => 2100, :title => 'IM')
|
54
55
|
mark3 = ICU::Player.new('Mark', 'Orr', 37, :fed = 'IRL', :rating => 2200, :title => 'IM')
|
55
56
|
|
56
57
|
mark1.eql?(mark2) # => true (ratings agree and titles don't disagree)
|
57
|
-
mark2.eql?(mark3) # => false (the ratings are
|
58
|
+
mark2.eql?(mark3) # => false (the ratings are not the same)
|
58
59
|
|
59
60
|
The presence of two players in the same tournament that are equal according to _==_ but unequal
|
60
61
|
according to _eql?__ is likely to indicate a data entry error.
|
@@ -63,7 +64,7 @@ If two instances represent the same player and are equal according to _==_ then
|
|
63
64
|
_title_ and _fed_ attributes of the two can be merged. For example:
|
64
65
|
|
65
66
|
fox1 = ICU::Player.new('Tony', 'Fox', 12, :id => 456)
|
66
|
-
fox2 = ICU::Player.new('Tony', 'Fox', 21, :rating => 2100, :fed => 'IRL')
|
67
|
+
fox2 = ICU::Player.new('Tony', 'Fox', 21, :rating => 2100, :fed => 'IRL', :gender => 'M')
|
67
68
|
fox1.merge(fox2)
|
68
69
|
|
69
70
|
Any attributes present in the second player but not in the first are copied to the first.
|
@@ -71,11 +72,12 @@ All other attributes are unaffected.
|
|
71
72
|
|
72
73
|
fox1.rating # => 2100
|
73
74
|
fox1.fed # => 'IRL'
|
75
|
+
fox1.gender # => 'M'
|
74
76
|
|
75
77
|
=end
|
76
78
|
|
77
79
|
class Player
|
78
|
-
attr_accessor :first_name, :last_name, :num, :id, :fed, :title, :rating, :rank, :dob
|
80
|
+
attr_accessor :first_name, :last_name, :num, :id, :fed, :title, :rating, :rank, :dob, :gender
|
79
81
|
attr_reader :results
|
80
82
|
|
81
83
|
# Constructor. Must supply both names and a unique number for the tournament.
|
@@ -83,7 +85,7 @@ All other attributes are unaffected.
|
|
83
85
|
self.first_name = first_name
|
84
86
|
self.last_name = last_name
|
85
87
|
self.num = num
|
86
|
-
[:id, :fed, :title, :rating, :rank, :dob].each do |atr|
|
88
|
+
[:id, :fed, :title, :rating, :rank, :dob, :gender].each do |atr|
|
87
89
|
self.send("#{atr}=", opt[atr]) unless opt[atr].nil?
|
88
90
|
end
|
89
91
|
@results = []
|
@@ -174,7 +176,15 @@ All other attributes are unaffected.
|
|
174
176
|
raise "invalid DOB (#{dob})" if @dob.nil? && dob.length > 0
|
175
177
|
end
|
176
178
|
|
177
|
-
#
|
179
|
+
# Gender. Is either unknown (nil) or one of _M_ or _F_.
|
180
|
+
def gender=(gender)
|
181
|
+
@gender = gender.to_s.strip[0,1].upcase
|
182
|
+
@gender = nil if @gender == ''
|
183
|
+
@gender = 'F' if @gender == 'W'
|
184
|
+
raise "invalid gender (#{gender})" unless @gender.nil? || @gender.match(/^[MF]$/)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Add a result. Use the same name method in ICU::Tournament instead.
|
178
188
|
def add_result(result)
|
179
189
|
raise "invalid result" unless result.class == ICU::Result
|
180
190
|
raise "player number (#{@num}) is not matched to result player number (#{result.player})" unless @num == result.player
|
@@ -202,11 +212,11 @@ All other attributes are unaffected.
|
|
202
212
|
true
|
203
213
|
end
|
204
214
|
|
205
|
-
# Strict equality test. Passes if the playes are loosly equal and also if their ID, rating and title are not different.
|
215
|
+
# Strict equality test. Passes if the playes are loosly equal and also if their ID, rating, gender and title are not different.
|
206
216
|
def eql?(other)
|
207
217
|
return true if equal?(other)
|
208
218
|
return false unless self == other
|
209
|
-
[:id, :rating, :title].each do |m|
|
219
|
+
[:id, :rating, :title, :gender].each do |m|
|
210
220
|
return false if self.send(m) && other.send(m) && self.send(m) != other.send(m)
|
211
221
|
end
|
212
222
|
true
|
@@ -215,7 +225,7 @@ All other attributes are unaffected.
|
|
215
225
|
# Merge in some of the details of another player.
|
216
226
|
def merge(other)
|
217
227
|
raise "cannot merge two players that are not equal" unless self == other
|
218
|
-
[:id, :rating, :title, :fed].each do |m|
|
228
|
+
[:id, :rating, :title, :fed, :gender].each do |m|
|
219
229
|
self.send("#{m}=", other.send(m)) unless self.send(m)
|
220
230
|
end
|
221
231
|
end
|
data/lib/tournament.rb
CHANGED
@@ -30,19 +30,19 @@ Would result in the following output.
|
|
30
30
|
0.5 Mark Orr
|
31
31
|
|
32
32
|
Note that the players should be added first because the _add_result_ method will
|
33
|
-
raise an exception if the players it references through their numbers
|
34
|
-
and 30 in this example) have not already been added to the tournament.
|
33
|
+
raise an exception if the players it references through their tournament numbers
|
34
|
+
(10, 20 and 30 in this example) have not already been added to the tournament.
|
35
35
|
|
36
36
|
=end
|
37
37
|
|
38
38
|
class Tournament
|
39
|
-
attr_reader :name, :start, :rounds, :site
|
39
|
+
attr_reader :name, :start, :finish, :rounds, :site, :city, :fed, :type, :arbiter, :deputy, :time_control
|
40
40
|
|
41
41
|
# Constructor. Name and start date must be supplied. Other attributes are optional.
|
42
42
|
def initialize(name, start, opt={})
|
43
43
|
self.name = name
|
44
44
|
self.start = start
|
45
|
-
[:rounds, :site].each { |a| self.send("#{a}=", opt[a]) unless opt[a].nil? }
|
45
|
+
[:finish, :rounds, :site, :city, :fed, :type, :arbiter, :deputy, :time_control].each { |a| self.send("#{a}=", opt[a]) unless opt[a].nil? }
|
46
46
|
@player = {}
|
47
47
|
end
|
48
48
|
|
@@ -52,6 +52,29 @@ and 30 in this example) have not already been added to the tournament.
|
|
52
52
|
@name = name.to_s.strip
|
53
53
|
end
|
54
54
|
|
55
|
+
# Set the tournament city. Can be _nil.
|
56
|
+
def city=(city)
|
57
|
+
city = city.to_s.strip
|
58
|
+
if city == ''
|
59
|
+
@city = nil
|
60
|
+
else
|
61
|
+
raise "invalid tournament city (#{city})" unless city.match(/[a-z]/i)
|
62
|
+
@city = city
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Set the tournament federation. Can be _nil.
|
67
|
+
def fed=(fed)
|
68
|
+
fed = fed.to_s.strip
|
69
|
+
if fed == ''
|
70
|
+
@fed = nil
|
71
|
+
else
|
72
|
+
raise "invalid tournament federation (#{fed})" unless fed.match(/^[-a-z ]+/i) && fed.length >= 3
|
73
|
+
fed.upcase! if fed.length == 3
|
74
|
+
@fed = fed
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
55
78
|
# Set a start date in yyyy-mm-dd format.
|
56
79
|
def start=(start)
|
57
80
|
start = start.to_s.strip
|
@@ -59,6 +82,17 @@ and 30 in this example) have not already been added to the tournament.
|
|
59
82
|
raise "invalid start date (#{start})" unless @start
|
60
83
|
end
|
61
84
|
|
85
|
+
# Set an end date in yyyy-mm-dd format.
|
86
|
+
def finish=(finish)
|
87
|
+
finish = finish.to_s.strip
|
88
|
+
if finish == ''
|
89
|
+
@finish = nil
|
90
|
+
else
|
91
|
+
@finish = Util.parsedate(finish)
|
92
|
+
raise "invalid finish date (#{finish})" unless @finish
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
62
96
|
# Set the number of rounds. Is either unknown (_nil_) or a positive integer.
|
63
97
|
def rounds=(rounds)
|
64
98
|
@rounds = case rounds
|
@@ -78,6 +112,34 @@ and 30 in this example) have not already been added to the tournament.
|
|
78
112
|
raise "invalid site (#{site})" unless @site.nil? || @site.match(/^https?:\/\/[-\w]+(\.[-\w]+)+(\/[^\s]*)?$/i)
|
79
113
|
end
|
80
114
|
|
115
|
+
# Set the tournament type. Should be either unknown (_nil_) or contain some letters.
|
116
|
+
def type=(type)
|
117
|
+
@type = type.to_s.strip
|
118
|
+
@type = nil if @type == ''
|
119
|
+
raise "invalid tournament type (#{type})" unless @type.nil? || @type.match(/[a-z]/i)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Set the tournament arbiter. Should be either unknown (_nil_) or contain some letters.
|
123
|
+
def arbiter=(arbiter)
|
124
|
+
@arbiter = arbiter.to_s.strip
|
125
|
+
@arbiter = nil if @arbiter == ''
|
126
|
+
raise "invalid tournament arbiter (#{arbiter})" unless @arbiter.nil? || @arbiter.match(/[a-z]/i)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Set the tournament deputy. Should be either unknown (_nil_) or contain some letters.
|
130
|
+
def deputy=(deputy)
|
131
|
+
@deputy = deputy.to_s.strip
|
132
|
+
@deputy = nil if @deputy == ''
|
133
|
+
raise "invalid tournament deputy (#{deputy})" unless @deputy.nil? || @deputy.match(/[a-z]/i)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Set the time control. Should be either unknown (_nil_) or contain some numbers.
|
137
|
+
def time_control=(time_control)
|
138
|
+
@time_control = time_control.to_s.strip
|
139
|
+
@time_control = nil if @time_control == ''
|
140
|
+
raise "invalid tournament time control (#{time_control})" unless @time_control.nil? || @time_control.match(/[1-9]\d/)
|
141
|
+
end
|
142
|
+
|
81
143
|
# Add a new player to the tournament. Must have a unique player number.
|
82
144
|
def add_player(player)
|
83
145
|
raise "invalid player" unless player.class == ICU::Player
|
data/lib/tournament_fcsv.rb
CHANGED
@@ -73,6 +73,10 @@ However, none of the opponents' results are rateable. For example:
|
|
73
73
|
opponent = tournament.players(2)
|
74
74
|
opponent.name # => "Taylor, Peter P."
|
75
75
|
opponent.results[0].rateable # => false
|
76
|
+
|
77
|
+
A tournament can be serialized back to CSV format (the reverse of parsing) with the serialise method.
|
78
|
+
|
79
|
+
csv = parser.serialize(tournament)
|
76
80
|
|
77
81
|
=end
|
78
82
|
|
@@ -134,6 +138,41 @@ However, none of the opponents' results are rateable. For example:
|
|
134
138
|
|
135
139
|
@tournament
|
136
140
|
end
|
141
|
+
|
142
|
+
# Serialise a tournament back into CSV format.
|
143
|
+
def serialise(t)
|
144
|
+
return nil unless t.class == ICU::Tournament;
|
145
|
+
FasterCSV.generate do |csv|
|
146
|
+
csv << ["Event", t.name]
|
147
|
+
csv << ["Start", t.start]
|
148
|
+
csv << ["Rounds", t.rounds]
|
149
|
+
csv << ["Website", t.site]
|
150
|
+
t.players.each do |p|
|
151
|
+
next unless p.id
|
152
|
+
csv << []
|
153
|
+
csv << ["Player", p.id, p.last_name, p.first_name]
|
154
|
+
(1..t.rounds).each do |n|
|
155
|
+
data = []
|
156
|
+
data << n
|
157
|
+
r = p.find_result(n)
|
158
|
+
data << case r.score; when 'W' then '1'; when 'L' then '0'; else '='; end
|
159
|
+
if r.rateable
|
160
|
+
data << r.colour
|
161
|
+
o = t.player(r.opponent)
|
162
|
+
data << o.last_name
|
163
|
+
data << o.first_name
|
164
|
+
data << o.rating
|
165
|
+
data << o.title
|
166
|
+
data << o.fed
|
167
|
+
else
|
168
|
+
data << '-'
|
169
|
+
end
|
170
|
+
csv << data
|
171
|
+
end
|
172
|
+
csv << ["Total", p.points]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
137
176
|
|
138
177
|
private
|
139
178
|
|
data/spec/player_spec.rb
CHANGED
@@ -144,6 +144,22 @@ module ICU
|
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
|
+
context "gender" do
|
148
|
+
it "defaults to nil" do
|
149
|
+
Player.new('Mark', 'Orr', 3).gender.should be_nil
|
150
|
+
Player.new('Mark', 'Orr', 3, :gender => ' ').gender.should be_nil
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should be either M or F" do
|
154
|
+
Player.new('Mark', 'Orr', 3, :gender => 'male').gender.should == 'M'
|
155
|
+
Player.new('April', 'Cronin', 3, :gender => 'woman').gender.should == 'F'
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should raise an exception if the gender is not specified properly" do
|
159
|
+
lambda { Player.new('Mark', 'Orr', 3, :gender => 'X') }.should raise_error(/invalid gender/)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
147
163
|
context "results and points" do
|
148
164
|
it "should initialise to an empty array" do
|
149
165
|
results = Player.new('Mark', 'Orr', 3).results
|
@@ -386,6 +386,37 @@ CSV
|
|
386
386
|
lambda { @f.parse!(csv) }.should raise_error(/line 13.*same name.*conflicting/i)
|
387
387
|
end
|
388
388
|
end
|
389
|
+
|
390
|
+
context "serialisation" do
|
391
|
+
before(:each) do
|
392
|
+
@csv = <<CSV
|
393
|
+
Event,"Edinburgh Masters, 2007"
|
394
|
+
Start,2007-08-09
|
395
|
+
Rounds,2
|
396
|
+
Website,http://www.chesscenter.com/twic/twic.html
|
397
|
+
|
398
|
+
Player,3364,Ui Laighleis,Gearoidin
|
399
|
+
1,0,W,Kasparov,Gary,2800,GM,RUS
|
400
|
+
2,1,B,Cronin,April,2005,,IRL
|
401
|
+
Total,1.0
|
402
|
+
|
403
|
+
Player,1350,Orr,Mark
|
404
|
+
1,=,W,Cronin,April,2005,,IRL
|
405
|
+
2,=,-
|
406
|
+
Total,1.0
|
407
|
+
CSV
|
408
|
+
@f = ForeignCSV.new
|
409
|
+
@t = @f.parse!(@csv)
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should serialize back to the original" do
|
413
|
+
@f.serialise(@t).should == @csv
|
414
|
+
end
|
415
|
+
|
416
|
+
it "should return nil on invalid input" do
|
417
|
+
@f.serialise('Rubbish').should be_nil
|
418
|
+
end
|
419
|
+
end
|
389
420
|
end
|
390
421
|
end
|
391
422
|
end
|
data/spec/tournament_spec.rb
CHANGED
@@ -37,6 +37,61 @@ module ICU
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
+
# Tournament city.
|
41
|
+
context "city" do
|
42
|
+
before(:each) do
|
43
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09', :city => 'Edinburgh')
|
44
|
+
end
|
45
|
+
|
46
|
+
it "may be specified in constructor" do
|
47
|
+
@t.city.should == 'Edinburgh'
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can be replaced by accessor" do
|
51
|
+
@t.city = 'Glasgow'
|
52
|
+
@t.city.should == 'Glasgow'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "can be set to nil" do
|
56
|
+
@t.city = ''
|
57
|
+
@t.city.should be_nil
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should not be without letters if set" do
|
61
|
+
lambda { @t.city = '123' }.should raise_error(/invalid.*city/)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Tournament federation.
|
66
|
+
context "federation" do
|
67
|
+
before(:each) do
|
68
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09', :fed => 'SCO')
|
69
|
+
end
|
70
|
+
|
71
|
+
it "may be specified in constructor" do
|
72
|
+
@t.fed.should == 'SCO'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "can be replaced by accessor" do
|
76
|
+
@t.fed = 'IRL'
|
77
|
+
@t.fed.should == 'IRL'
|
78
|
+
end
|
79
|
+
|
80
|
+
it "can be set to nil" do
|
81
|
+
@t.fed = ''
|
82
|
+
@t.fed.should be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it "three letters will automatically be upcased" do
|
86
|
+
@t.fed = 'rus'
|
87
|
+
@t.fed.should == 'RUS'
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should not be without letters if set" do
|
91
|
+
lambda { @t.fed = '123' }.should raise_error(/invalid.*federation/)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
40
95
|
# Tournament start date.
|
41
96
|
context "start date" do
|
42
97
|
before(:each) do
|
@@ -58,6 +113,32 @@ module ICU
|
|
58
113
|
end
|
59
114
|
end
|
60
115
|
|
116
|
+
# Tournament finish date.
|
117
|
+
context "finish date" do
|
118
|
+
before(:each) do
|
119
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09', :finish => '12th November 2009')
|
120
|
+
end
|
121
|
+
|
122
|
+
it "may be specified in constructor" do
|
123
|
+
@t.finish.should == '2009-11-12'
|
124
|
+
end
|
125
|
+
|
126
|
+
it "can be replaced by accessor" do
|
127
|
+
@t.finish = '16th December 2009'
|
128
|
+
@t.finish.should == '2009-12-16'
|
129
|
+
end
|
130
|
+
|
131
|
+
it "can be set to nil" do
|
132
|
+
@t.finish = ''
|
133
|
+
@t.finish.should be_nil
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should be a valid date" do
|
137
|
+
lambda { Tournament.new('Edinburgh Masters', '2009-11-09', :finish => 'next week') }.should raise_error(/invalid.*date/)
|
138
|
+
lambda { @t.finish = 'X' }.should raise_error(/invalid.*date/)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
61
142
|
# Number of rounds.
|
62
143
|
context "rounds" do
|
63
144
|
it "defaults to nil" do
|
@@ -84,6 +165,49 @@ module ICU
|
|
84
165
|
end
|
85
166
|
end
|
86
167
|
|
168
|
+
# Type, arbiter, deputy and time control.
|
169
|
+
context "type, arbiter, deputy and time control" do
|
170
|
+
before(:each) do
|
171
|
+
@t = Tournament.new('Edinburgh Masters', '2009-11-09', :type => 'Swiss', :arbiter => 'Gerry Graham', :deputy => 'Herbert Scarry', :time_control => '120 minutes')
|
172
|
+
end
|
173
|
+
|
174
|
+
it "may be specified in constructor" do
|
175
|
+
@t.type.should == 'Swiss'
|
176
|
+
@t.arbiter.should == 'Gerry Graham'
|
177
|
+
@t.deputy.should == 'Herbert Scarry'
|
178
|
+
@t.time_control.should == '120 minutes'
|
179
|
+
end
|
180
|
+
|
181
|
+
it "can be replaced by accessor" do
|
182
|
+
@t.type = 'all-play-all'
|
183
|
+
@t.type.should == 'all-play-all'
|
184
|
+
@t.arbiter = 'Michael Crowe'
|
185
|
+
@t.arbiter.should == 'Michael Crowe'
|
186
|
+
@t.deputy = 'Mark Orr'
|
187
|
+
@t.deputy.should == 'Mark Orr'
|
188
|
+
@t.time_control = '90 minutes'
|
189
|
+
@t.time_control.should == '90 minutes'
|
190
|
+
end
|
191
|
+
|
192
|
+
it "can be set to nil" do
|
193
|
+
@t.type = ''
|
194
|
+
@t.type.should be_nil
|
195
|
+
@t.arbiter = ''
|
196
|
+
@t.arbiter.should be_nil
|
197
|
+
@t.deputy = ''
|
198
|
+
@t.deputy.should be_nil
|
199
|
+
@t.time_control = ''
|
200
|
+
@t.time_control.should be_nil
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should be valid" do
|
204
|
+
lambda { @t.type = '123' }.should raise_error(/invalid.*type/)
|
205
|
+
lambda { @t.arbiter = '123' }.should raise_error(/invalid.*arbiter/)
|
206
|
+
lambda { @t.deputy = '123' }.should raise_error(/invalid.*deputy/)
|
207
|
+
lambda { @t.time_control = 'abc' }.should raise_error(/invalid.*time control/)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
87
211
|
# Tournament players.
|
88
212
|
context "players" do
|
89
213
|
before(:each) do
|