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 CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 2
3
- :patch: 6
3
+ :patch: 7
4
4
  :major: 0
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 contains at least three letters.
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 a set of possibilities (after a little cleaning up).
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
- # Elo rating. Is either unknown (nil) or a positive integer.
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 subsume(other)
137
- raise "cannot merge two players that are not strictly equal" unless eql?(other)
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)) if 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
- We can fill in opponent details as follows:
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 result now represents a win by player 10 with the black pieces over player number 13 in round 2.
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 an opponent is:
44
+ The result of the same game from the perspective of the opponent is:
45
45
 
46
46
  tluser = result.reverse
47
47
 
@@ -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.subsume(@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.subsume(opponent)
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 "subsumption" do
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.subsume(@p2)
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.subsume(p2).eql?(@p2.subsume(@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.subsume(@p3) }.should raise_error(/cannot merge.*not strictly equal/)
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.6
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-18 00:00:00 -07:00
12
+ date: 2009-04-19 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15