Class: ICU::RatedPlayer
- Inherits:
-
Object
- Object
- ICU::RatedPlayer
- Defined in:
- lib/icu_ratings/player.rb
Overview
Adding Players to Tournaments
You don’t directly create players, rather you add them to tournaments with the add_player method.
t = ICU::RatedTournament.new
t.add_player(1)
There is only one mandatory parameter - the player number - which can be any integer value except player numbers must be unique in each tournament:
t.add_player(2) # fine
t.add_player(2) # attempt to add a second player with the same number - exception!
Retrieving Players from Tournaments
Player objects can be retrieved from ICU::RatedTournament objects with the latter’s player method in conjunction with the appropriate player number:
p = t.player(2)
p.num # 2
Or the player object can be saved from the return value from add_player:
p = t.add_player(-2)
p.num # -2
If the number supplied to player is an invalid player number, the method returns nil.
Different types of players are signalled by different combinations of the three optional parameters: rating, kfactor and games.
Full Ratings
Rated players have a full rating and a K-factor and are added by including valid values for those two parameters:
p = t.add_player(3, :rating => 2000, :kfactor => 16)
p.type # :rated
Provisional Ratings
Players that don’t yet have a full rating but do have a provisonal rating estimated on some number of games played prior to the tournament are indicated by values for the rating and games parameters:
p = t.add_player(4, :rating => 1600, :games => 10)
p.type # :provisional
The value for the number of games should not exceed 19 since players with 20 or more games should have a full rating.
Fixed Ratings
Players with fixed ratings just have a rating - no K-factor or number of previous games. When the tournament is rated, these players will have their tournament performance ratings calculated but the value returned by the method new_rating will just be the rating they started with. Typically these are foreign players with FIDE ratings who are not members of the ICU and for whom ICU ratings are not desired.
p = t.add_player(6, :rating => 2500)
p.type # :foreign
No Rating
Unrated players who do not have any previous rated games at all are indicated by leaving out any values for rating, kfactor or games.
p = t.add_player(5)
p.type # :unrated
Invalid Combinations
The above four types of players (rated, provisional, unrated, foreign) are the only valid ones and any attempt to add players with other combinations of the attributes rating, kfactor and games will cause an exception. For example:
t.add_player(7, :rating => 2000, :kfactor => 16, :games => 10) # exception! - cannot have both kfactor and games
t.add_plater(7, :kfactor => 16) # exception! - kfactor makes no sense without a rating
String Input Values
Although rating and kfactor are held as Float values and games and num (the player number) as Fixnums, all these parameters can be specified using strings, even when padded with whitespace.
p = t.add_player(" 0 ", :rating => " 2000.5 ", :kfactor => " 20.5 ")
p.num # 0 (Fixnum)
p. # 2000.5 (Float)
p.kfactor # 20.5 (Float)
Calculation of K-factors
Rather than pre-calculating the value to set for a rated player’s K-factor, the RatedPlayer class can itself calculate K-factors if the releavant information is supplied. ICU K-factors depend not only on a player’s rating, but also on their age and experience. Therefore, supply a hash, instead of a numerical value, for the kfactor attribute with values set for date-of-birth (dob) and date joined (joined):
t = Tournament.new(:start => "2010-07-10")
p = t.add_player(1, :rating => 2225, :kfactor => { :dob => "1993-12-20", :joined => "2004-11-28" })
p.kfactor # 16.0
For this to work the tournament’s optional start date must be set to enable the player’s age and experience at the start of the tournament be to calculated. The ICU K-factor rules are:
-
16 for players rated 2100 and over, otherwise
-
40 for players aged under 21, otherwise
-
32 for players who have been members for less than 8 years, otherwise
-
24
If you want to calculate K-factors accrding to some other, non-ICU scheme, then override the static method kfactor of the RatedPlayer class and pass in a hash of whatever key-value pairs it requires as the value associated with kfactor key in the add_player method.
Description Parameter
There is one other optional parameter, desc (short for “description”). It has no effect on player type or rating calculations and it cannot be used to retrieve players from a tournament (only the player number can be used for that). Its only use is to attach additional arbitary data to players. Any object can be used and descriptions don’t have to be unique. The attribute’s typical use, if it’s used at all, is expected to be for player names and/or ID numbers, in the form of String values.
t.add_player(8, :rating => 2800, :desc => 'Gary Kasparov (4100018)')
t.player(8).desc # "Gary Kasparov (4100018)"
After the Tournament is Rated
After the rate! method has been called on the ICU::RatedTournament object, the results of the rating calculations are available via various methods of the player objects:
- new_rating
-
This is the player’s new rating. For rated players it is their old rating plus their rating_change plus their bonus (if any). For provisional players it is their performance rating including their previous games. For unrated players it is their tournament performance rating. New ratings are not calculated for foreign players so this method just returns their start rating.
- rating_change
-
This is calculated from a rated player’s old rating, their K-factor and the sum of expected scores in each game. The same as the difference between the old and new ratings (unless there is a bonus). Not available for other player types.
- performance
-
This returns the tournament rating performance for rated, unrated and foreign players. For provisional players it returns a weighted average of the player’s tournament performance and their previous games. For provisional and unrated players it is the same as new_rating.
- expected_score
-
This returns the sum of expected scores over all results for all player types. For rated players, this number times the K-factor gives their rating change. It is calculated for provisional, unrated and foreign players but not actually used to estimate new ratings (for provisional and unrated players performance estimates are used instead).
- bonus
-
The bonus received by a rated player (usually zero). Only available for rated players.
- pb_rating
-
A rated player’s pre-bonus rating (rounded). Only for rated players and returns nil for players who are ineligible for a bonus.
- pb_performance
-
A rated player’s pre-bonus performance (rounded). Only for rated players and returns nil for players ineligible for a bonus.
Unrateable Players
If a tournament contains groups of provisonal or unrated players who play games only amongst themselves and not against any rated or foreign opponents, they can’t be rated. This is indicated by a value of nil returned from the new_rating method.
Direct Known Subclasses
Defined Under Namespace
Classes: FrgnRating, FullRating, NoneRating, ProvRating
Instance Attribute Summary collapse
-
#desc ⇒ Object
Returns the value of attribute desc.
-
#num ⇒ Object
readonly
Returns the value of attribute num.
-
#performance ⇒ Object
readonly
Returns the value of attribute performance.
-
#results ⇒ Object
readonly
Returns the value of attribute results.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
Class Method Summary collapse
-
.check_games(arg) ⇒ Object
:nodoc:.
-
.check_kfactor(arg) ⇒ Object
:nodoc:.
-
.check_num(arg) ⇒ Object
:nodoc:.
-
.check_rating(arg) ⇒ Object
:nodoc:.
-
.factory(num, args = {}) ⇒ Object
:nodoc:.
-
.kfactor(args) ⇒ Object
Calculate a K-factor according to ICU rules.
Instance Method Summary collapse
-
#==(other) ⇒ Object
:nodoc:.
-
#add_result(result) ⇒ Object
:nodoc:.
-
#average_performance(performance, games) ⇒ Object
:nodoc:.
-
#estimate_performance ⇒ Object
:nodoc:.
- #expected_score ⇒ Object
-
#rate!(update_bonus = false) ⇒ Object
:nodoc:.
-
#reset ⇒ Object
:nodoc:.
- #score ⇒ Object
-
#update_performance(thresh) ⇒ Object
:nodoc:.
Instance Attribute Details
#desc ⇒ Object
Returns the value of attribute desc.
164 165 166 |
# File 'lib/icu_ratings/player.rb', line 164 def desc @desc end |
#num ⇒ Object (readonly)
Returns the value of attribute num.
163 164 165 |
# File 'lib/icu_ratings/player.rb', line 163 def num @num end |
#performance ⇒ Object (readonly)
Returns the value of attribute performance.
163 164 165 |
# File 'lib/icu_ratings/player.rb', line 163 def performance @performance end |
#results ⇒ Object (readonly)
Returns the value of attribute results.
163 164 165 |
# File 'lib/icu_ratings/player.rb', line 163 def results @results end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
163 164 165 |
# File 'lib/icu_ratings/player.rb', line 163 def type @type end |
Class Method Details
.check_games(arg) ⇒ Object
:nodoc:
201 202 203 204 205 206 |
# File 'lib/icu_ratings/player.rb', line 201 def self.check_games(arg) # :nodoc: return unless arg games = arg.to_i raise "invalid number of games (#{arg})" if games <= 0 || games >= 20 games end |
.check_kfactor(arg) ⇒ Object
:nodoc:
194 195 196 197 198 199 |
# File 'lib/icu_ratings/player.rb', line 194 def self.check_kfactor(arg) # :nodoc: return unless arg kfactor = arg.to_f raise "invalid player k-factor (#{arg})" if kfactor <= 0.0 kfactor end |
.check_num(arg) ⇒ Object
:nodoc:
181 182 183 184 185 |
# File 'lib/icu_ratings/player.rb', line 181 def self.check_num(arg) # :nodoc: num = arg.to_i raise "invalid player num (#{arg})" if num == 0 && !arg.to_s.match(/^\s*\d/) num end |
.check_rating(arg) ⇒ Object
:nodoc:
187 188 189 190 191 192 |
# File 'lib/icu_ratings/player.rb', line 187 def self.(arg) # :nodoc: return unless arg = arg.to_f raise "invalid player rating (#{arg})" if == 0.0 && !arg.to_s.match(/^\s*\d/) end |
.factory(num, args = {}) ⇒ Object
:nodoc:
166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/icu_ratings/player.rb', line 166 def self.factory(num, args={}) # :nodoc: num = check_num(num) = (args[:rating]) kfactor = check_kfactor(args[:kfactor]) games = check_games(args[:games]) desc = args[:desc] case when && kfactor && !games then FullRating.new(num, desc, , kfactor) when && !kfactor && games then ProvRating.new(num, desc, , games) when && !kfactor && !games then FrgnRating.new(num, desc, ) when ! && !kfactor && !games then NoneRating.new(num, desc) else raise "invalid combination of player attributes" end end |
.kfactor(args) ⇒ Object
Calculate a K-factor according to ICU rules.
209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/icu_ratings/player.rb', line 209 def self.kfactor(args) %w{rating start dob joined}.each { |a| raise "missing #{a} for K-factor calculation" unless args[a.to_sym] } case when args[:rating] >= 2100 then 16 when ICU::Util::Date.age(args[:dob], args[:start]) < 21 then 40 when ICU::Util::Date.age(args[:joined], args[:start]) < 8 then 32 else 24 end end |
Instance Method Details
#==(other) ⇒ Object
:nodoc:
390 391 392 393 |
# File 'lib/icu_ratings/player.rb', line 390 def ==(other) # :nodoc: return false unless other.is_a? ICU::RatedPlayer num == other.num end |
#add_result(result) ⇒ Object
:nodoc:
341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/icu_ratings/player.rb', line 341 def add_result(result) # :nodoc: raise "invalid result (#{result.class})" unless result.is_a? ICU::RatedResult raise "players cannot score results against themselves" if self == result.opponent duplicate = false @results.each do |r| if r.round == result.round raise "inconsistent result in round #{r.round}" unless r == result duplicate = true end end return if duplicate @results << result @results.sort!{ |a,b| a.round <=> b.round } end |
#average_performance(performance, games) ⇒ Object
:nodoc:
373 374 375 |
# File 'lib/icu_ratings/player.rb', line 373 def average_performance(performance, games) # :nodoc: performance / games end |
#estimate_performance ⇒ Object
:nodoc:
361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/icu_ratings/player.rb', line 361 def estimate_performance # :nodoc: games, performance = results.inject([0,0.0]) do |sum, result| = result.opponent.(:opponent) if sum[0]+= 1 sum[1]+= + (2 * result.score - 1) * 400.0 end sum end @estimated_performance = average_performance(performance, games) if games > 0 end |
#expected_score ⇒ Object
333 334 335 |
# File 'lib/icu_ratings/player.rb', line 333 def expected_score @results.inject(0.0) { |e, r| e + (r.expected_score || 0.0) } end |
#rate!(update_bonus = false) ⇒ Object
:nodoc:
356 357 358 359 |
# File 'lib/icu_ratings/player.rb', line 356 def rate!(update_bonus=false) # :nodoc: @results.each { |r| r.rate!(self) } self.update_bonus if update_bonus && respond_to?(:update_bonus) end |
#reset ⇒ Object
:nodoc:
223 224 225 226 |
# File 'lib/icu_ratings/player.rb', line 223 def reset # :nodoc: @performance = nil @estimated_performance = nil end |
#score ⇒ Object
337 338 339 |
# File 'lib/icu_ratings/player.rb', line 337 def score @results.inject(0.0) { |e, r| e + r.score } end |
#update_performance(thresh) ⇒ Object
:nodoc:
377 378 379 380 381 382 383 384 385 386 387 388 |
# File 'lib/icu_ratings/player.rb', line 377 def update_performance(thresh) # :nodoc: stable = case when @performance && @estimated_performance then (@performance - @estimated_performance).abs < thresh when !@performance && !@estimated_performance then true else false end @performance = @estimated_performance if @estimated_performance stable end |