sanichi-chess_icu 0.2.6 → 0.2.7
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 +90 -10
- data/lib/result.rb +4 -4
- data/lib/tournament_fcsv.rb +2 -2
- data/spec/player_spec.rb +4 -4
- metadata +2 -2
data/VERSION.yml
CHANGED
data/lib/player.rb
CHANGED
@@ -1,8 +1,84 @@
|
|
1
1
|
module ICU
|
2
|
+
|
3
|
+
=begin rdoc
|
4
|
+
|
5
|
+
== Player
|
6
|
+
|
7
|
+
A player in a tournament must have a first name, a last name and a number
|
8
|
+
which is unique in the tournament but otherwise arbitary.
|
9
|
+
|
10
|
+
bobby = ICU::Player.new('robert j', 'fischer', 17)
|
11
|
+
|
12
|
+
Names are automatically cannonicalised (tidied up).
|
13
|
+
|
14
|
+
bobby.first_name # => 'Robert J.'
|
15
|
+
bobby.last_name # => 'Fischer'
|
16
|
+
|
17
|
+
In addition, players have a number of optional attributes which can be specified
|
18
|
+
via setters or in constructor hash options: _id_ (either FIDE or national),
|
19
|
+
_fed_ (federation), _title_, _rating_, _rank_ and _dob_ (date of birth).
|
20
|
+
|
21
|
+
peter = ICU::Player.new('Peter', 'Svidler', 21, :fed => 'rus', :title => 'g', :rating = 2700)
|
22
|
+
peter.dob = '17th June, 1976'
|
23
|
+
peter.rank = 1
|
24
|
+
|
25
|
+
Some of these values will also be canonicalised to some extent. For example,
|
26
|
+
the date of birth conforms to a _yyyy-mm-dd_ format, the chess title will be two
|
27
|
+
to three capital letters always ending in _M_ and the federation, if it's three
|
28
|
+
letters long, will be upcased.
|
29
|
+
|
30
|
+
peter.dob # => 1976-07-17
|
31
|
+
peter.title # => 'GM'
|
32
|
+
peter.fed # => 'RUS'
|
33
|
+
|
34
|
+
Results (ICU::Result) should be added to players via the tournament (ICU::Tournament) object
|
35
|
+
to which the player instance have been added. Their total score is available via the _points_ method.
|
36
|
+
|
37
|
+
peter.points # => 5.5
|
38
|
+
|
39
|
+
Players can be compared to see if they're roughly or exactly the same, which may be useful in detecting duplicates.
|
40
|
+
If the names match and the federations don't disagree then two players are equal according to the _==_ operator.
|
41
|
+
The player number is irrelevant.
|
42
|
+
|
43
|
+
john1 = ICU::Player.new('John', 'Smith', 12)
|
44
|
+
john2 = ICU::Player.new('John', 'Smith', 22, :fed = 'IRL')
|
45
|
+
john2 = ICU::Player.new('John', 'Smith', 32, :fed = 'ENG')
|
46
|
+
|
47
|
+
john1 == john2 # => true (federations don't disagree because one is unset)
|
48
|
+
john2 == john3 # => false (federations disagree)
|
49
|
+
|
50
|
+
If, in addition, none of _rating_, _dob_ and _id_ disagree then two players are equal according to _eql?_.
|
51
|
+
|
52
|
+
mark1 = ICU::Player.new('Mark', 'Orr', 31, :fed = 'IRL', :rating => 2100)
|
53
|
+
mark2 = ICU::Player.new('Mark', 'Orr', 33, :fed = 'IRL', :rating => 2100, :title => 'IM')
|
54
|
+
mark3 = ICU::Player.new('Mark', 'Orr', 37, :fed = 'IRL', :rating => 2200, :title => 'IM')
|
55
|
+
|
56
|
+
mark1.eql?(mark2) # => true (ratings agree and titles don't disagree)
|
57
|
+
mark2.eql?(mark3) # => false (the ratings are unequl)
|
58
|
+
|
59
|
+
The presence of two players in the same tournament that are equal according to _==_ but unequal
|
60
|
+
according to _eql?__ is likely to indicate a data entry error.
|
61
|
+
|
62
|
+
If two instances represent the same player and are equal according to _==_ then the _id_, _rating_,
|
63
|
+
_title_ and _fed_ attributes of the two can be merged. For example:
|
64
|
+
|
65
|
+
fox1 = ICU::Player.new('Tony', 'Fox', 12, :id => 456)
|
66
|
+
fox2 = ICU::Player.new('Tony', 'Fox', 21, :rating => 2100, :fed => 'IRL')
|
67
|
+
fox1.merge(fox2)
|
68
|
+
|
69
|
+
Any attributes present in the second player but not in the first are copied to the first.
|
70
|
+
All other attributes are unaffected.
|
71
|
+
|
72
|
+
fox1.rating # => 2100
|
73
|
+
fox1.fed # => 'IRL'
|
74
|
+
|
75
|
+
=end
|
76
|
+
|
2
77
|
class Player
|
3
78
|
attr_accessor :first_name, :last_name, :num, :id, :fed, :title, :rating, :rank, :dob
|
4
79
|
attr_reader :results
|
5
80
|
|
81
|
+
# Constructor. Must supply both names and a unique number for the tournament.
|
6
82
|
def initialize(first_name, last_name, num, opt={})
|
7
83
|
self.first_name = first_name
|
8
84
|
self.last_name = last_name
|
@@ -13,23 +89,26 @@ module ICU
|
|
13
89
|
@results = []
|
14
90
|
end
|
15
91
|
|
92
|
+
# Canonicalise and set the first name(s).
|
16
93
|
def first_name=(first_name)
|
17
94
|
name = Name.new(first_name, 'Last')
|
18
95
|
raise "invalid first name" unless name.first.length > 0
|
19
96
|
@first_name = name.first
|
20
97
|
end
|
21
98
|
|
99
|
+
# Canonicalise and set the last name(s).
|
22
100
|
def last_name=(last_name)
|
23
101
|
name = Name.new('First', last_name)
|
24
102
|
raise "invalid last name" unless name.last.length > 0 && name.first.length > 0
|
25
103
|
@last_name = name.last
|
26
104
|
end
|
27
105
|
|
106
|
+
# Return the full name, last name first.
|
28
107
|
def name
|
29
108
|
"#{last_name}, #{first_name}"
|
30
109
|
end
|
31
110
|
|
32
|
-
# Player number. Any integer.
|
111
|
+
# Player number. Any integer is valid including zero and negative numbers, as long as it's unique in the tournament.
|
33
112
|
def num=(num)
|
34
113
|
@num = case num
|
35
114
|
when Fixnum then num
|
@@ -49,7 +128,7 @@ module ICU
|
|
49
128
|
raise "invalid ID (#{id})" unless @id.nil? || @id > 0
|
50
129
|
end
|
51
130
|
|
52
|
-
# Federation. Is either unknown (nil) or
|
131
|
+
# Federation. Is either unknown (nil) or a string containing at least three letters.
|
53
132
|
def fed=(fed)
|
54
133
|
@fed = fed.to_s.strip
|
55
134
|
@fed.upcase! if @fed.length == 3
|
@@ -57,7 +136,8 @@ module ICU
|
|
57
136
|
raise "invalid federation (#{fed})" unless @fed.nil? || @fed.match(/[a-z]{3}/i)
|
58
137
|
end
|
59
138
|
|
60
|
-
# Chess title. Is either unknown (nil) or one of
|
139
|
+
# Chess title. Is either unknown (nil) or one of: _GM_, _IM_, _FM_, _CM_, _NM_,
|
140
|
+
# or any of these preceeded by the letter _W_.
|
61
141
|
def title=(title)
|
62
142
|
@title = title.to_s.strip.upcase
|
63
143
|
@title << 'M' if @title.match(/[A-LN-Z]$/)
|
@@ -65,7 +145,7 @@ module ICU
|
|
65
145
|
raise "invalid chess title (#{title})" unless @title.nil? || @title.match(/^W?[GIFCN]M$/)
|
66
146
|
end
|
67
147
|
|
68
|
-
#
|
148
|
+
# Rating. Is either unknown (nil) or a positive integer.
|
69
149
|
def rating=(rating)
|
70
150
|
@rating = case rating
|
71
151
|
when nil then nil
|
@@ -76,7 +156,7 @@ module ICU
|
|
76
156
|
raise "invalid rating (#{rating})" unless @rating.nil? || @rating > 0
|
77
157
|
end
|
78
158
|
|
79
|
-
# Rank in the tournament. Is either unknown (nil) or a positive integer.
|
159
|
+
# Rank in the tournament. Is either unknown (nil) or a positive integer. Must be unique in the tournament.
|
80
160
|
def rank=(rank)
|
81
161
|
@rank = case rank
|
82
162
|
when nil then nil
|
@@ -112,7 +192,7 @@ module ICU
|
|
112
192
|
@results.inject(0.0) { |t, r| t += r.points }
|
113
193
|
end
|
114
194
|
|
115
|
-
# Loose equality test.
|
195
|
+
# Loose equality test. Passes if the names match and the federations are not different.
|
116
196
|
def ==(other)
|
117
197
|
return true if equal?(other)
|
118
198
|
return false unless other.is_a? Player
|
@@ -122,7 +202,7 @@ module ICU
|
|
122
202
|
true
|
123
203
|
end
|
124
204
|
|
125
|
-
# Strict equality test.
|
205
|
+
# Strict equality test. Passes if the playes are loosly equal and also if their ID, rating and title are not different.
|
126
206
|
def eql?(other)
|
127
207
|
return true if equal?(other)
|
128
208
|
return false unless self == other
|
@@ -133,10 +213,10 @@ module ICU
|
|
133
213
|
end
|
134
214
|
|
135
215
|
# Merge in some of the details of another player.
|
136
|
-
def
|
137
|
-
raise "cannot merge two players that are not
|
216
|
+
def merge(other)
|
217
|
+
raise "cannot merge two players that are not equal" unless self == other
|
138
218
|
[:id, :rating, :title, :fed].each do |m|
|
139
|
-
self.send("#{m}=", other.send(m))
|
219
|
+
self.send("#{m}=", other.send(m)) unless self.send(m)
|
140
220
|
end
|
141
221
|
end
|
142
222
|
end
|
data/lib/result.rb
CHANGED
@@ -19,16 +19,16 @@ a bye or walkover since there is no opponent. Without an opponent, it is unratea
|
|
19
19
|
|
20
20
|
result.rateable # => false
|
21
21
|
|
22
|
-
|
22
|
+
The player's colour and the number of their opponent can be set as follows:
|
23
23
|
|
24
|
-
result.opponent = 13
|
25
24
|
result.colour = 'B'
|
25
|
+
result.opponent = 13
|
26
26
|
|
27
27
|
Specifying an opponent always makes a result rateable.
|
28
28
|
|
29
29
|
result.rateable # => true
|
30
30
|
|
31
|
-
This
|
31
|
+
This example now represents a win by player 10 with the black pieces over player number 13 in round 2.
|
32
32
|
Alternatively, all this can been specified in the constructor.
|
33
33
|
|
34
34
|
result = ICU::Result.new(2, 10, 'W', :opponent => 13, :colour => 'B')
|
@@ -41,7 +41,7 @@ or include it in the constructor:
|
|
41
41
|
|
42
42
|
result = ICU::Result.new(2, 10, 'W', :opponent => 13, :colour => 'B', :rateable => false)
|
43
43
|
|
44
|
-
The result of the same game from the perspective of
|
44
|
+
The result of the same game from the perspective of the opponent is:
|
45
45
|
|
46
46
|
tluser = result.reverse
|
47
47
|
|
data/lib/tournament_fcsv.rb
CHANGED
@@ -175,7 +175,7 @@ However, none of the opponents' results are rateable. For example:
|
|
175
175
|
if old_player
|
176
176
|
raise "two players with the same name (#{@player.name}) have conflicting details" unless old_player.eql?(@player)
|
177
177
|
raise "same player (#{@player.name}) has more than one set of results" if old_player.id
|
178
|
-
old_player.
|
178
|
+
old_player.merge(@player)
|
179
179
|
@player = old_player
|
180
180
|
else
|
181
181
|
@tournament.add_player(@player)
|
@@ -200,7 +200,7 @@ However, none of the opponents' results are rateable. For example:
|
|
200
200
|
raise "two players with the same name (#{opponent.name}) have conflicting details" unless old_player.eql?(opponent)
|
201
201
|
result.opponent = old_player.num
|
202
202
|
if old_player.id
|
203
|
-
old_player.
|
203
|
+
old_player.merge(opponent)
|
204
204
|
old_result = @player.find_result(@round)
|
205
205
|
raise "missing result for player (#{@player.name}) in round #{@round}" unless old_result
|
206
206
|
raise "mismatched results for player (#{old_player.name}) in round #{@round}" unless result == old_result
|
data/spec/player_spec.rb
CHANGED
@@ -191,7 +191,7 @@ module ICU
|
|
191
191
|
end
|
192
192
|
end
|
193
193
|
|
194
|
-
context "
|
194
|
+
context "merge" do
|
195
195
|
before(:each) do
|
196
196
|
@p1 = Player.new('Mark', 'Orr', 1, :id => 1350)
|
197
197
|
@p2 = Player.new('Mark', 'Orr', 2, :rating => 2100, :title => 'IM', :fed => 'IRL')
|
@@ -199,7 +199,7 @@ module ICU
|
|
199
199
|
end
|
200
200
|
|
201
201
|
it "takes on the ID, rating, title and fed of the other player but not the player number" do
|
202
|
-
@p1.
|
202
|
+
@p1.merge(@p2)
|
203
203
|
@p1.num.should == 1
|
204
204
|
@p1.id.should == 1350
|
205
205
|
@p1.rating.should == 2100
|
@@ -210,11 +210,11 @@ module ICU
|
|
210
210
|
it "should have a kind of symmetry" do
|
211
211
|
p1 = @p1.dup
|
212
212
|
p2 = @p2.dup
|
213
|
-
p1.
|
213
|
+
p1.merge(p2).eql?(@p2.merge(@p1))
|
214
214
|
end
|
215
215
|
|
216
216
|
it "cannot be done with unequal objects" do
|
217
|
-
lambda { @p1.
|
217
|
+
lambda { @p1.merge(@p3) }.should raise_error(/cannot merge.*not equal/)
|
218
218
|
end
|
219
219
|
end
|
220
220
|
|
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.2.
|
4
|
+
version: 0.2.7
|
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-04-
|
12
|
+
date: 2009-04-19 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|