Class: ICU::Player

Inherits:
Object
  • Object
show all
Extended by:
Util::Accessor
Defined in:
lib/icu_tournament/player.rb,
lib/icu_tournament/tournament_sp.rb,
lib/icu_tournament/tournament_spx.rb,
lib/icu_tournament/tournament_krause.rb

Overview

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'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Util::Accessor

attr_accessor, attr_date, attr_date_or_nil, attr_integer, attr_integer_or_nil, attr_positive, attr_positive_or_nil, attr_string, attr_string_or_nil

Constructor Details

#initialize(first_name, last_name, num, opt = {}) ⇒ Player

Constructor. Must supply both names and a unique number for the tournament.



108
109
110
111
112
113
114
115
116
117
# 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

Instance Attribute Details

#fedObject

Returns the value of attribute fed.



105
106
107
# File 'lib/icu_tournament/player.rb', line 105

def fed
  @fed
end

#first_nameObject

Returns the value of attribute first_name.



105
106
107
# File 'lib/icu_tournament/player.rb', line 105

def first_name
  @first_name
end

#genderObject

Returns the value of attribute gender.



105
106
107
# File 'lib/icu_tournament/player.rb', line 105

def gender
  @gender
end

#last_nameObject

Returns the value of attribute last_name.



105
106
107
# File 'lib/icu_tournament/player.rb', line 105

def last_name
  @last_name
end

#original_nameObject

Returns the value of attribute original_name.



105
106
107
# File 'lib/icu_tournament/player.rb', line 105

def original_name
  @original_name
end

#resultsObject (readonly)

Returns the value of attribute results.



105
106
107
# File 'lib/icu_tournament/player.rb', line 105

def results
  @results
end

#titleObject

Returns the value of attribute title.



105
106
107
# File 'lib/icu_tournament/player.rb', line 105

def title
  @title
end

Instance Method Details

#==(other) ⇒ Object

Loose equality test. Passes if the names match and the federations are not different.



210
211
212
213
214
215
216
217
# File 'lib/icu_tournament/player.rb', line 210

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_result(result) ⇒ Object

Add a result. Don’t use this method directly - use ICU::Tournament#add_result instead.



169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/icu_tournament/player.rb', line 169

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

#eql?(other) ⇒ Boolean

Strict equality test. Passes if the playes are loosly equal and also if their IDs, rating, gender and title are not different.

Returns:

  • (Boolean)


220
221
222
223
224
225
226
227
# File 'lib/icu_tournament/player.rb', line 220

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

#find_result(round) ⇒ Object

Lookup a result by round number and return it (or nil if there is no such result).



184
185
186
# File 'lib/icu_tournament/player.rb', line 184

def find_result(round)
  @results.find { |r| r.round == round }
end

#merge(other) ⇒ Object

Merge in some of the details of another player.



230
231
232
233
234
235
# File 'lib/icu_tournament/player.rb', line 230

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

#nameObject

Return the full name, last name first.



138
139
140
# File 'lib/icu_tournament/player.rb', line 138

def name
  "#{last_name}, #{first_name}"
end

#pointsObject

Return the player’s total points.



197
198
199
# File 'lib/icu_tournament/player.rb', line 197

def points
  @results.inject(0.0) { |t, r| t += r.points }
end

#remove_result(round) ⇒ Object

Lookup a result by round number, remove it and return it (or return nil if there is no such result).



189
190
191
192
193
194
# File 'lib/icu_tournament/player.rb', line 189

def remove_result(round)
  result = find_result(round)
  return unless result
  @results.delete(result)
  result
end

#renumber(map) ⇒ Object

Renumber the player according to the supplied hash. Return self.



202
203
204
205
206
207
# File 'lib/icu_tournament/player.rb', line 202

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

#to_krause(rounds, arg) ⇒ Object

Format a player’s 001 record as it would appear in a Krause formatted file (including the final newline).



533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/icu_tournament/tournament_krause.rb', line 533

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

#to_sp_text(rounds, columns, formats) ⇒ Object

Format a player’s record as it would appear in an SP export file.



333
334
335
336
337
338
339
340
# 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