class ICU::Player
A player in a tournament must have a first name, a last name and a number which is unique in the tournament but otherwise arbitary.
bobby = ICU::Player.new(' robert j ', 'fischer', 17)
Names are automatically cannonicalised (tidied up).
bobby.first_name # => 'Robert J.' bobby.last_name # => 'Fischer'
But the original untidied (other than white space cleanup and the addition of a comma to separate last name from first name) is avaiable from the original_name method:
bobby.original_name # => "fischer, robert j"
This original name, set via the constructor, is unchanged by subsequent calls to the setter methods first_name or _last_name.
booby.first_name = 'Robert James' bobby.original_name # => "fischer, robert j" You can reset _orignal_name_, if necessary, and this does not affect either _first_name_ or _last_name_. bobby.original_name = "Fischer, Robert James"
In addition, players have a number of optional attributes which can be specified via setters or in constructor hash options: id (local or national ID), fide (FIDE ID), fed (federation), title, rating (local rating), _fide_rating, rank and dob (date of birth).
peter = ICU::Player.new('Peter', 'Svidler', 21, :fed => 'rus', :title => 'g', :fide_rating = 2700) peter.dob = '17th June, 1976' peter.rank = 1
Some of these values will also be canonicalised to some extent. For example, date of birth will be turned into yyyy-mm-dd format, the chess title will be two to three capital letters always ending in M and the federation, if it’s three letters long, will be upcased.
peter.dob # => 1976-07-17 peter.title # => 'GM' peter.fed # => 'RUS'
It is preferable to add results (ICU::Result
) to a player via the tournament (ICU::Tournament
) object’s add_result method rather than the method of the same name belonging to player instances. Doing so allows mirrored results to be added to both players with one call (e.g. one player won, so the other lost). A player’s results can later be retieved via the results accessor.
Total scores is available via the points method.
peter.points # => 5.5
A player can have up to two ID numbers (both positive integers or nil): id (local or national ID, such as ICU
number) and fide (FIDE ID).
peter.id = 16790 # ICU peter.fide = 4102142 # FIDE
Players can be compared to see if they’re roughly or exactly the same, which may be useful in detecting duplicates. If the names match and the federations don’t disagree then two players are equal according to the _==_ operator. The player number is irrelevant.
john1 = ICU::Player.new('John', 'Smith', 12) john2 = ICU::Player.new('John', 'Smith', 22, :fed = 'IRL') john2 = ICU::Player.new('John', 'Smith', 32, :fed = 'ENG') john1 == john2 # => true (federations don't disagree because one is unset) john2 == john3 # => false (federations disagree)
If, in addition, rating, dob, gender, id and fide do not disagree then two players are equal according to the stricter criteria of eql?.
mark1 = ICU::Player.new('Mark', 'Orr', 31, :fed = 'IRL', :rating => 2100) mark2 = ICU::Player.new('Mark', 'Orr', 33, :fed = 'IRL', :rating => 2100, :title => 'IM') mark3 = ICU::Player.new('Mark', 'Orr', 37, :fed = 'IRL', :rating => 2200, :title => 'IM') mark1.eql?(mark2) # => true (ratings agree and titles don't disagree) mark2.eql?(mark3) # => false (the ratings are not the same)
The presence of two players in the same tournament that are equal according to _==_ but unequal according to _eql?__ is likely to indicate a data entry error.
If two instances represent the same player and are equal according to _==_ then the id, fide, rating, title and fed attributes of the two can be merged. For example:
fox1 = ICU::Player.new('Tony', 'Fox', 12, :id => 456) fox2 = ICU::Player.new('Tony', 'Fox', 21, :rating => 2100, :fed => 'IRL', :gender => 'M') fox1.merge(fox2)
Any attributes present in the second player but not in the first are copied to the first. All other attributes are unaffected.
fox1.rating # => 2100 fox1.fed # => 'IRL' fox1.gender # => 'M'
Attributes
Public Class Methods
Constructor. Must supply both names and a unique number for the tournament.
# File lib/icu_tournament/player.rb, line 108 def initialize(first_name, last_name, num, opt={}) self.first_name = first_name self.last_name = last_name @original_name = Name.new(first_name, last_name).original self.num = num [:id, :fide_id, :fed, :title, :rating, :fide_rating, :rank, :dob, :gender].each do |atr| self.send("#{atr}=", opt[atr]) unless opt[atr].nil? end @results = [] end
Public Instance Methods
Loose equality test. Passes if the names match and the federations are not different.
# File lib/icu_tournament/player.rb, line 211 def ==(other) return true if equal?(other) return false unless other.is_a? Player return false unless @first_name == other.first_name return false unless @last_name == other.last_name return false if @fed && other.fed && @fed != other.fed true end
Add a result. Don’t use this method directly - use ICU::Tournament#add_result
instead.
# File lib/icu_tournament/player.rb, line 170 def add_result(result) raise "invalid result" unless result.class == ICU::Result raise "player number (#{@num}) is not matched to result player number (#{result.inspect})" unless @num == result.player already = @results.find_all { |r| r.round == result.round } return if already.size == 1 && already[0].eql?(result) raise "player #{@num} already has a result in round #{result.round} (#{already[0].inspect}) which does not match the one being added (#{result.inspect})" unless already.size == 0 if @results.size == 0 || @results.last.round <= result.round @results << result else i = (0..@results.size-1).find { |n| @results[n].round > result.round } @results.insert(i, result) end end
Strict equality test. Passes if the playes are loosly equal and also if their IDs, rating, gender and title are not different.
# File lib/icu_tournament/player.rb, line 221 def eql?(other) return true if equal?(other) return false unless self == other [:id, :fide_id, :rating, :fide_rating, :title, :gender].each do |m| return false if self.send(m) && other.send(m) && self.send(m) != other.send(m) end true end
Federation. Is either unknown (nil) or a string containing at least three letters.
# File lib/icu_tournament/player.rb, line 143 def fed=(fed) obj = ICU::Federation.find(fed) @fed = obj ? obj.code : nil raise "invalid federation (#{fed})" if @fed.nil? && fed.to_s.strip.length > 0 end
Lookup a result by round number and return it (or nil if there is no such result).
# File lib/icu_tournament/player.rb, line 185 def find_result(round) @results.find { |r| r.round == round } end
Canonicalise and set the first name(s).
# File lib/icu_tournament/player.rb, line 120 def first_name=(first_name) name = Name.new(first_name, 'Last') @first_name = name.first end
Gender. Is either unknown (nil) or one of M or F.
# File lib/icu_tournament/player.rb, line 162 def gender=(gender) @gender = gender.to_s.strip[0,1].upcase @gender = nil if @gender == '' @gender = 'F' if @gender == 'W' raise "invalid gender (#{gender})" unless @gender.nil? || @gender.match(/^[MF]$/) end
Canonicalise and set the last name(s).
# File lib/icu_tournament/player.rb, line 126 def last_name=(last_name) name = Name.new('First', last_name) raise "invalid last name" unless name.last.length > 0 && name.first.length > 0 @last_name = name.last end
Merge in some of the details of another player.
# File lib/icu_tournament/player.rb, line 231 def merge(other) raise "cannot merge two players that are not equal" unless self == other [:id, :fide_id, :rating, :fide_rating, :title, :fed, :gender].each do |m| self.send("#{m}=", other.send(m)) unless self.send(m) end end
Return the full name, last name first.
# File lib/icu_tournament/player.rb, line 138 def name "#{last_name}, #{first_name}" end
Reset the original name.
# File lib/icu_tournament/player.rb, line 133 def original_name=(original_name) @original_name = ICU::Util::String.to_utf8(original_name) end
Return the player’s total points.
# File lib/icu_tournament/player.rb, line 198 def points @results.inject(0.0) { |t, r| t += r.points } end
Lookup a result by round number, remove it and return it (or return nil if there is no such result).
# File lib/icu_tournament/player.rb, line 190 def remove_result(round) result = find_result(round) return unless result @results.delete(result) result end
Renumber the player according to the supplied hash. Return self.
# File lib/icu_tournament/player.rb, line 203 def renumber(map) raise "player number #{@num} not found in renumbering hash" unless map[@num] self.num = map[@num] @results.each{ |r| r.renumber(map) } self end
Chess title. Is either unknown (nil) or one of: GM, IM, FM, CM, NM, or any of these preceeded by the letter W.
# File lib/icu_tournament/player.rb, line 151 def title=(title) @title = title.to_s.strip.upcase @title << 'M' if @title.match(/[A-LN-Z]$/) @title = 'IM' if @title == 'M' @title = 'WIM' if @title == 'WM' @title = '' if @title.match(/^(AFM|AIM|AGM)$/) @title = nil if @title == '' raise "invalid chess title (#{title})" unless @title.nil? || @title.match(/^W?[GIFCN]M$/) end
Format a player’s 001 record as it would appear in a Krause formatted file (including the final newline).
# File lib/icu_tournament/tournament_krause.rb, line 546 def to_krause(rounds, arg) defaults = ICU::Tournament::Krause::OPTIONS.map(&:first) # Optional columns. case when arg[:except].instance_of?(Array) optional = (Set.new(defaults) - arg[:except].map!(&:to_s).map!(&:to_sym)).to_a when arg[:only].instance_of?(Array) optional = arg[:only].map!(&:to_s).map!(&:to_sym) else optional = defaults end optional = optional.inject({}) { |m, a| m[a] = true; m } # Get the values to use. val = defaults.inject({}) do |m, a| if optional[a] if arg[:fide] && (a == :rating || a == :id) m[a] = send("fide_#{a}") else m[a] = send(a) end end m end # Output the mandatory and optional values. krause = '001' krause << sprintf(' %4d', @num) krause << sprintf(' %1s', case val[:gender]; when 'M' then 'm'; when 'F' then 'w'; else ''; end) krause << sprintf(' %2s', case val[:title]; when nil then ''; when 'IM' then 'm'; when 'WIM' then 'wm'; else val[:title][0, val[:title].length-1].downcase; end) krause << sprintf(' %-33s', "#{@last_name},#{@first_name}") krause << sprintf(' %4s', val[:rating]) krause << sprintf(' %3s', val[:fed]) krause << sprintf(' %11s', val[:id]) krause << sprintf(' %10s', val[:dob]) krause << sprintf(' %4.1f', points) krause << sprintf(' %4s', val[:rank]) # And finally the round scores. (1..rounds).each do |r| result = find_result(r) krause << sprintf(' %8s', result ? result.to_krause : '') end krause << "\n" end
Format a player’s record as it would appear in an SP text export file.
# File lib/icu_tournament/tournament_sp.rb, line 333 def to_sp_text(rounds, format) attrs = [num.to_s, name, id.to_s, ('%.1f' % points).sub(/\.0/, '')] (1..rounds).each do |r| result = find_result(r) attrs << (result ? result.to_sp_text : " : ") end format % attrs end