icu_ratings 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,162 +1,158 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module ICU
4
-
5
- =begin rdoc
6
-
7
- == Adding Players to Tournaments
8
-
9
- You don't directly create players, rather you add them to tournaments with the _add_player_ method.
10
-
11
- t = ICU::RatedTournament.new
12
- t.add_player(1)
13
-
14
- There is only one mandatory parameter - the player number - which can be any integer value
15
- except player numbers must be unique in each tournament:
16
-
17
- t.add_player(2) # fine
18
- t.add_player(2) # attempt to add a second player with the same number - exception!
19
-
20
- == Retrieving Players from Tournaments
21
-
22
- Player objects can be retrieved from ICU::RatedTournament objects with the latter's _player_ method
23
- in conjunction with the appropriate player number:
24
-
25
- p = t.player(2)
26
- p.num # 2
27
-
28
- Or the player object can be saved from the return value from _add_player_:
29
-
30
- p = t.add_player(-2)
31
- p.num # -2
32
-
33
- If the number supplied to _player_ is an invalid player number, the method returns _nil_.
34
-
35
- Different types of players are signalled by different combinations of the three optional
36
- parameters: _rating_, _kfactor_ and _games_.
37
-
38
- == Full Ratings
39
-
40
- Rated players have a full rating and a K-factor and are added by including valid values for those two parameters:
41
-
42
- p = t.add_player(3, :rating => 2000, :kfactor => 16)
43
- p.type # :rated
44
-
45
- == Provisional Ratings
46
-
47
- Players that don't yet have a full rating but do have a provisonal rating estimated on some number
48
- of games played prior to the tournament are indicated by values for the _rating_ and _games_ parameters:
49
-
50
- p = t.add_player(4, :rating => 1600, :games => 10)
51
- p.type # :provisional
52
-
53
- The value for the number of games should not exceed 19 since players with 20 or more games
54
- should have a full rating.
55
-
56
- == Fixed Ratings
57
-
58
- Players with fixed ratings just have a rating - no K-factor or number of previous games.
59
- When the tournament is rated, these players will have their tournament performance ratings
60
- calculated but the value returned by the method _new_rating_ will just be the rating they
61
- started with. Typically these are foreign players with FIDE ratings who are not members of
62
- the ICU and for whom ICU ratings are not desired.
63
-
64
- p = t.add_player(6, :rating => 2500)
65
- p.type # :foreign
66
-
67
- == No Rating
68
-
69
- Unrated players who do not have any rated games at all are indicated by leaving out any values for
70
- _rating_, _kfactor_ or _games_.
71
-
72
- p = t.add_player(5)
73
- p.type # :unrated
74
-
75
- == Invalid Combinations
76
-
77
- The above four types of players (_rated_, _provisional_, _unrated_, _foreign_) are the only
78
- valid ones and any attempt to add players with other combinations of the attributes
79
- _rating_, _kfactor_ and _games_ will cause an exception. For example:
80
-
81
- t.add_player(7, :rating => 2000, :kfactor => 16, :games => 10) # exception! - cannot have both kfactor and games
82
- t.add_plater(7, :kfactor => 16) # exception! - kfactor makes no sense without a rating
83
-
84
- == String Input Values
85
-
86
- Although _rating_ and _kfactor_ are held as Float values and _games_ and _num_ (the player number) as Fixnums,
87
- all these parameters can be specified using strings, even when padded with whitespace.
88
-
89
- p = t.add_player(" 0 ", :rating => " 2000.5 ", :kfactor => " 20.5 ")
90
- p.num # 0 (Fixnum)
91
- p.rating # 2000.5 (Float)
92
- p.kfactor # 20.5 (Float)
93
-
94
- == Calculation of K-factors
95
-
96
- Rather than pre-calculating the value to set for a rated player's K-factor, the RatedPlayer class can itself
97
- calculate K-factors if the releavant information is supplied. ICU K-factors depend not only on a player's
98
- rating, but also on their age and experience. Therefore, supply a hash, instead of a numerical value, for the
99
- _kfactor_ attribute with values set for date-of-birth (_dob_) and date joined (_joined_):
100
-
101
- t = Tournament.new(:start => "2010-07-10")
102
- p = t.add_player(1, :rating => 2225, :kfactor => { :dob => "1993-12-20", :joined => "2004-11-28" })
103
- p.kfactor # 16.0
104
-
105
- For this to work the tournament's optional start date must be set to enable the player's age and
106
- experience at the start of the tournament be to calculated. The ICU K-factor rules are:
107
-
108
- * 16 for players rated 2100 and over, otherwise
109
- * 40 for players aged under 21, otherwise
110
- * 32 for players who have been members for less than 8 years, otherwise
111
- * 24
112
-
113
- If you want to calculate K-factors accrding to some other, non-ICU scheme, then override the
114
- static method _kfactor_ of the RatedPlayer class and pass in a hash of whatever key-value pairs
115
- it requires as the value associated with _kfactor_ key in the _add_player_ method.
116
-
117
- == Description Parameter
118
-
119
- There is one other optional parameter, _desc_ (short for "description"). It has no effect on player
120
- type or rating calculations and it cannot be used to retrieve players from a tournament (only the
121
- player number can be used for that). Its only use is to attach additional arbitary data to players.
122
- Any object can be used and descriptions don't have to be unique. The attribute's typical use,
123
- if it's used at all, is expected to be for player names in the form of String values.
124
-
125
- t.add_player(8, :rating => 2800, :desc => 'Gary Kasparov (4100018)')
126
- t.player(8).desc # "Gary Kasparov (4100018)"
127
-
128
- == After the Tournament is Rated
129
-
130
- After the <em>rate!</em> method has been called on the ICU::RatedTournament object, the results
131
- of the rating calculations are available via various methods of the player objects:
132
-
133
- _new_rating_:: This is the player's new rating. For rated players it is their old rating
134
- plus their _rating_change_. For provisional players it is their performance
135
- rating including their previous games. For unrated players it is their
136
- tournament performance rating. New rarings are not calculated for foreign
137
- players so this method just returned their start _rating_.
138
- _rating_change_:: This is the difference between the old and new ratings for rated players,
139
- based on sum of expected scores in each games and the player's K-factor.
140
- Zero for all other types of players.
141
- _performance_:: This returns the tournament rating performance for rated, unrated and
142
- foreign players. For provisional players it returns a weighted average
143
- of the player's tournament performance and their previous games. For
144
- provisional and unrated players it is the same as _new_rating_.
145
- _expected_score_:: This returns the sum of expected scores over all results for all player types.
146
- For rated players, this number times the K-factor gives their rating change.
147
- It is calculated for provisional, unrated and foreign players but not actually
148
- used to estimate new ratings (for provisional and unrated players performance
149
- estimates are used instead).
150
-
151
- == Unrateable Players
152
-
153
- If a tournament contains groups of provisonal or unrated players who play games
154
- only amongst themselves and not against any rated or foreign opponents, they can't
155
- be rated. This is indicated by a value of _nil_ returned from the _new_rating_
156
- method.
157
-
158
- =end
159
-
4
+ #
5
+ # == Adding Players to Tournaments
6
+ #
7
+ # You don't directly create players, rather you add them to tournaments with the _add_player_ method.
8
+ #
9
+ # t = ICU::RatedTournament.new
10
+ # t.add_player(1)
11
+ #
12
+ # There is only one mandatory parameter - the player number - which can be any integer value
13
+ # except player numbers must be unique in each tournament:
14
+ #
15
+ # t.add_player(2) # fine
16
+ # t.add_player(2) # attempt to add a second player with the same number - exception!
17
+ #
18
+ # == Retrieving Players from Tournaments
19
+ #
20
+ # Player objects can be retrieved from ICU::RatedTournament objects with the latter's _player_ method
21
+ # in conjunction with the appropriate player number:
22
+ #
23
+ # p = t.player(2)
24
+ # p.num # 2
25
+ #
26
+ # Or the player object can be saved from the return value from _add_player_:
27
+ #
28
+ # p = t.add_player(-2)
29
+ # p.num # -2
30
+ #
31
+ # If the number supplied to _player_ is an invalid player number, the method returns _nil_.
32
+ #
33
+ # Different types of players are signalled by different combinations of the three optional
34
+ # parameters: _rating_, _kfactor_ and _games_.
35
+ #
36
+ # == Full Ratings
37
+ #
38
+ # Rated players have a full rating and a K-factor and are added by including valid values for those two parameters:
39
+ #
40
+ # p = t.add_player(3, :rating => 2000, :kfactor => 16)
41
+ # p.type # :rated
42
+ #
43
+ # == Provisional Ratings
44
+ #
45
+ # Players that don't yet have a full rating but do have a provisonal rating estimated on some number
46
+ # of games played prior to the tournament are indicated by values for the _rating_ and _games_ parameters:
47
+ #
48
+ # p = t.add_player(4, :rating => 1600, :games => 10)
49
+ # p.type # :provisional
50
+ #
51
+ # The value for the number of games should not exceed 19 since players with 20 or more games
52
+ # should have a full rating.
53
+ #
54
+ # == Fixed Ratings
55
+ #
56
+ # Players with fixed ratings just have a rating - no K-factor or number of previous games.
57
+ # When the tournament is rated, these players will have their tournament performance ratings
58
+ # calculated but the value returned by the method _new_rating_ will just be the rating they
59
+ # started with. Typically these are foreign players with FIDE ratings who are not members of
60
+ # the ICU and for whom ICU ratings are not desired.
61
+ #
62
+ # p = t.add_player(6, :rating => 2500)
63
+ # p.type # :foreign
64
+ #
65
+ # == No Rating
66
+ #
67
+ # Unrated players who do not have any rated games at all are indicated by leaving out any values for
68
+ # _rating_, _kfactor_ or _games_.
69
+ #
70
+ # p = t.add_player(5)
71
+ # p.type # :unrated
72
+ #
73
+ # == Invalid Combinations
74
+ #
75
+ # The above four types of players (_rated_, _provisional_, _unrated_, _foreign_) are the only
76
+ # valid ones and any attempt to add players with other combinations of the attributes
77
+ # _rating_, _kfactor_ and _games_ will cause an exception. For example:
78
+ #
79
+ # t.add_player(7, :rating => 2000, :kfactor => 16, :games => 10) # exception! - cannot have both kfactor and games
80
+ # t.add_plater(7, :kfactor => 16) # exception! - kfactor makes no sense without a rating
81
+ #
82
+ # == String Input Values
83
+ #
84
+ # Although _rating_ and _kfactor_ are held as Float values and _games_ and _num_ (the player number) as Fixnums,
85
+ # all these parameters can be specified using strings, even when padded with whitespace.
86
+ #
87
+ # p = t.add_player(" 0 ", :rating => " 2000.5 ", :kfactor => " 20.5 ")
88
+ # p.num # 0 (Fixnum)
89
+ # p.rating # 2000.5 (Float)
90
+ # p.kfactor # 20.5 (Float)
91
+ #
92
+ # == Calculation of K-factors
93
+ #
94
+ # Rather than pre-calculating the value to set for a rated player's K-factor, the RatedPlayer class can itself
95
+ # calculate K-factors if the releavant information is supplied. ICU K-factors depend not only on a player's
96
+ # rating, but also on their age and experience. Therefore, supply a hash, instead of a numerical value, for the
97
+ # _kfactor_ attribute with values set for date-of-birth (_dob_) and date joined (_joined_):
98
+ #
99
+ # t = Tournament.new(:start => "2010-07-10")
100
+ # p = t.add_player(1, :rating => 2225, :kfactor => { :dob => "1993-12-20", :joined => "2004-11-28" })
101
+ # p.kfactor # 16.0
102
+ #
103
+ # For this to work the tournament's optional start date must be set to enable the player's age and
104
+ # experience at the start of the tournament be to calculated. The ICU K-factor rules are:
105
+ #
106
+ # * 16 for players rated 2100 and over, otherwise
107
+ # * 40 for players aged under 21, otherwise
108
+ # * 32 for players who have been members for less than 8 years, otherwise
109
+ # * 24
110
+ #
111
+ # If you want to calculate K-factors accrding to some other, non-ICU scheme, then override the
112
+ # static method _kfactor_ of the RatedPlayer class and pass in a hash of whatever key-value pairs
113
+ # it requires as the value associated with _kfactor_ key in the _add_player_ method.
114
+ #
115
+ # == Description Parameter
116
+ #
117
+ # There is one other optional parameter, _desc_ (short for "description"). It has no effect on player
118
+ # type or rating calculations and it cannot be used to retrieve players from a tournament (only the
119
+ # player number can be used for that). Its only use is to attach additional arbitary data to players.
120
+ # Any object can be used and descriptions don't have to be unique. The attribute's typical use,
121
+ # if it's used at all, is expected to be for player names in the form of String values.
122
+ #
123
+ # t.add_player(8, :rating => 2800, :desc => 'Gary Kasparov (4100018)')
124
+ # t.player(8).desc # "Gary Kasparov (4100018)"
125
+ #
126
+ # == After the Tournament is Rated
127
+ #
128
+ # After the <em>rate!</em> method has been called on the ICU::RatedTournament object, the results
129
+ # of the rating calculations are available via various methods of the player objects:
130
+ #
131
+ # _new_rating_:: This is the player's new rating. For rated players it is their old rating
132
+ # plus their _rating_change_. For provisional players it is their performance
133
+ # rating including their previous games. For unrated players it is their
134
+ # tournament performance rating. New rarings are not calculated for foreign
135
+ # players so this method just returned their start _rating_.
136
+ # _rating_change_:: This is the difference between the old and new ratings for rated players,
137
+ # based on sum of expected scores in each games and the player's K-factor.
138
+ # Zero for all other types of players.
139
+ # _performance_:: This returns the tournament rating performance for rated, unrated and
140
+ # foreign players. For provisional players it returns a weighted average
141
+ # of the player's tournament performance and their previous games. For
142
+ # provisional and unrated players it is the same as _new_rating_.
143
+ # _expected_score_:: This returns the sum of expected scores over all results for all player types.
144
+ # For rated players, this number times the K-factor gives their rating change.
145
+ # It is calculated for provisional, unrated and foreign players but not actually
146
+ # used to estimate new ratings (for provisional and unrated players performance
147
+ # estimates are used instead).
148
+ #
149
+ # == Unrateable Players
150
+ #
151
+ # If a tournament contains groups of provisonal or unrated players who play games
152
+ # only amongst themselves and not against any rated or foreign opponents, they can't
153
+ # be rated. This is indicated by a value of _nil_ returned from the _new_rating_
154
+ # method.
155
+ #
160
156
  class RatedPlayer
161
157
  attr_reader :num, :rating, :kfactor, :games
162
158
  attr_accessor :desc, :bonus
@@ -1,98 +1,94 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module ICU
4
-
5
- =begin rdoc
6
-
7
- == Adding Results to Tournaments
8
-
9
- You don't create results directly with a constructor, instead, you add them to a tournament
10
- using the _add_result_ method, giving the round number, the player numbers and the score
11
- of the first player relative to the second:
12
-
13
- t = ICU::RatedTournament.new
14
- t.add_player(10)
15
- t.add_player(20)
16
- t.add_result(1, 10, 20, 'W')
17
-
18
- The above example expresses the result that in round 1 player 10 won against player 20. An exception is raised
19
- if either of the two players does not already exist in the tournament, if either player already has a game
20
- with another opponent in that round or if the two players already have a different result against each other
21
- in that round. Note that the result is added to both players: in the above example a win in round 1 against
22
- player 20 is added to player 10's results and a loss against player 10 in round 1 is added to player 20's results.
23
- It's OK (but unnecessary) to add the same result again from the other player's prespective as long as
24
- the score is consistent.
25
-
26
- t.add_result(1, 20, 10, 'L') # unnecessary (nothing would change) but would not cause an exception
27
- t.add_result(1, 20, 10, 'D') # inconsistent result - would raise an exception
28
-
29
- == Specifying the Score
30
-
31
- The method _score_ will always return a Float value (either 0.0, 0.5 or 1.0).
32
- When specifying a score using the _add_result_ of ICU::Tourmanent the same values
33
- can be used as can other, equally valid alternatives:
34
-
35
- win:: "1", "1.0", "W", "w" (String), 1 (Fixnum), 1.0 (Float)
36
- loss:: "0", "0.0", "L", "l" (String), 0 (Fixnum), 0.0 (Float)
37
- draw:: "½", "D", "d" (String), 0.5 (Float)
38
-
39
- Strings padded with whitespace also work (e.g. " 1.0 " and " W ").
40
-
41
- == Specifying the Players
42
-
43
- As described above, one way to specify the two players is via player numbers. Equally possible is player objects:
44
-
45
- t = ICU::RatedTournament.new
46
- p = t.add_player(10)
47
- q = t.add_plater(20)
48
- t.add_result(1, p, q, 'W')
49
-
50
- Or indeed (although this is unnecessary):
51
-
52
- t = ICU::RatedTournament.new
53
- t.add_player(10)
54
- t.add_plater(20)
55
- t.add_result(1, t.player(10), t.player(20), 'W')
56
-
57
- A players cannot have a results against themselves:
58
-
59
- t.add_player(2, 10, 10, 'D') # exception!
60
-
61
- == Retrieving Results
62
-
63
- Results belong to players (ICU::RatedPlayer objects) and are stored in an array accessed by the method _results_.
64
- Each result has a _round_ number, an _opponent_ object (also an ICU::RatedPlayer object) and a _score_ (1.0, 0.5 or 0.0):
65
-
66
- p = t.player(10)
67
- p.results.size # 1
68
- r = p.results[0]
69
- r.round # 1
70
- r.opponent.num # 20
71
- r.score # 1.0 (Float)
72
-
73
- The _results_ method returns results in round order, irrespective of what order they were added in:
74
-
75
- t = ICU::RatedTournament.new
76
- [0,1,2,3,4].each { |num| t.add_player(num) }
77
- [3,1].each { |rnd| t.add_result(rnd, 0, rnd, 'W') }
78
- [4,2].each { |rnd| t.add_result(rnd, 0, rnd, 'L') }
79
- t.player(0).results.map{ |r| r.round }.join(',') # "1,2,3,4"
80
-
81
- == Unrated Results
82
-
83
- Results that are not for rating, such as byes, walkovers and defaults, should not be
84
- added to the tournament. Instead, players can simply have no results for certain rounds.
85
- Indeed, it's even valid for players not to have any results at all (although, in that
86
- case, for those players, no new rating can be calculated from the tournament).
87
-
88
- == After the Tournament is Rated
89
-
90
- The main rating calculations are avaiable from player methods (see ICU::RatedPlayer)
91
- but additional details are available via methods of each player's individual results:
92
- _expected_score_, _rating_change_.
93
-
94
- =end
95
-
4
+ #
5
+ # == Adding Results to Tournaments
6
+ #
7
+ # You don't create results directly with a constructor, instead, you add them to a tournament
8
+ # using the _add_result_ method, giving the round number, the player numbers and the score
9
+ # of the first player relative to the second:
10
+ #
11
+ # t = ICU::RatedTournament.new
12
+ # t.add_player(10)
13
+ # t.add_player(20)
14
+ # t.add_result(1, 10, 20, 'W')
15
+ #
16
+ # The above example expresses the result that in round 1 player 10 won against player 20. An exception is raised
17
+ # if either of the two players does not already exist in the tournament, if either player already has a game
18
+ # with another opponent in that round or if the two players already have a different result against each other
19
+ # in that round. Note that the result is added to both players: in the above example a win in round 1 against
20
+ # player 20 is added to player 10's results and a loss against player 10 in round 1 is added to player 20's results.
21
+ # It's OK (but unnecessary) to add the same result again from the other player's prespective as long as
22
+ # the score is consistent.
23
+ #
24
+ # t.add_result(1, 20, 10, 'L') # unnecessary (nothing would change) but would not cause an exception
25
+ # t.add_result(1, 20, 10, 'D') # inconsistent result - would raise an exception
26
+ #
27
+ # == Specifying the Score
28
+ #
29
+ # The method _score_ will always return a Float value (either 0.0, 0.5 or 1.0).
30
+ # When specifying a score using the _add_result_ of ICU::Tourmanent the same values
31
+ # can be used as can other, equally valid alternatives:
32
+ #
33
+ # win:: "1", "1.0", "W", "w" (String), 1 (Fixnum), 1.0 (Float)
34
+ # loss:: "0", "0.0", "L", "l" (String), 0 (Fixnum), 0.0 (Float)
35
+ # draw:: "½", "D", "d" (String), 0.5 (Float)
36
+ #
37
+ # Strings padded with whitespace also work (e.g. " 1.0 " and " W ").
38
+ #
39
+ # == Specifying the Players
40
+ #
41
+ # As described above, one way to specify the two players is via player numbers. Equally possible is player objects:
42
+ #
43
+ # t = ICU::RatedTournament.new
44
+ # p = t.add_player(10)
45
+ # q = t.add_plater(20)
46
+ # t.add_result(1, p, q, 'W')
47
+ #
48
+ # Or indeed (although this is unnecessary):
49
+ #
50
+ # t = ICU::RatedTournament.new
51
+ # t.add_player(10)
52
+ # t.add_plater(20)
53
+ # t.add_result(1, t.player(10), t.player(20), 'W')
54
+ #
55
+ # A players cannot have a results against themselves:
56
+ #
57
+ # t.add_player(2, 10, 10, 'D') # exception!
58
+ #
59
+ # == Retrieving Results
60
+ #
61
+ # Results belong to players (ICU::RatedPlayer objects) and are stored in an array accessed by the method _results_.
62
+ # Each result has a _round_ number, an _opponent_ object (also an ICU::RatedPlayer object) and a _score_ (1.0, 0.5 or 0.0):
63
+ #
64
+ # p = t.player(10)
65
+ # p.results.size # 1
66
+ # r = p.results[0]
67
+ # r.round # 1
68
+ # r.opponent.num # 20
69
+ # r.score # 1.0 (Float)
70
+ #
71
+ # The _results_ method returns results in round order, irrespective of what order they were added in:
72
+ #
73
+ # t = ICU::RatedTournament.new
74
+ # [0,1,2,3,4].each { |num| t.add_player(num) }
75
+ # [3,1].each { |rnd| t.add_result(rnd, 0, rnd, 'W') }
76
+ # [4,2].each { |rnd| t.add_result(rnd, 0, rnd, 'L') }
77
+ # t.player(0).results.map{ |r| r.round }.join(',') # "1,2,3,4"
78
+ #
79
+ # == Unrated Results
80
+ #
81
+ # Results that are not for rating, such as byes, walkovers and defaults, should not be
82
+ # added to the tournament. Instead, players can simply have no results for certain rounds.
83
+ # Indeed, it's even valid for players not to have any results at all (although, in that
84
+ # case, for those players, no new rating can be calculated from the tournament).
85
+ #
86
+ # == After the Tournament is Rated
87
+ #
88
+ # The main rating calculations are avaiable from player methods (see ICU::RatedPlayer)
89
+ # but additional details are available via methods of each player's individual results:
90
+ # _expected_score_, _rating_change_.
91
+ #
96
92
  class RatedResult
97
93
  # The round number.
98
94
  def round
@@ -1,78 +1,74 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module ICU
4
-
5
- =begin rdoc
6
-
7
- == Creating Tournaments
8
-
9
- ICU::RatedTournament objects are created directly.
10
-
11
- t = ICU::RatedTournament.new
12
-
13
- They have some optional parameters which can be set via the constructor or by calling
14
- the same-named setter methods. One is called _desc_ (short for description) the value
15
- of which can be any object but will, if utilized, typically be the name of the
16
- tournament as a string.
17
-
18
- t = ICU::RatedTournament.new(:desc => "Irish Championships 2010")
19
- puts t.desc # "Irish Championships 2010"
20
-
21
- Another optional parameter is _start_ for the start date. A Date object or a string that can be
22
- parsed as a string can be used to set it. The European convention is preferred for dates like
23
- "03/06/2013" (3rd of June, not 6th of March). Attempting to set an invalid date will raise an
24
- exception.
25
-
26
- t = ICU::RatedTournament.new(:start => "01/07/2010")
27
- puts t.start.class # Date
28
- puts t.start.to_s # "2010-07-01"
29
-
30
- Also, there is the option _no_bonuses_. Bonuses are a feature of the ICU rating system. If you are
31
- rating a non-ICU tournament (such as a FIDE tournament) where bonues are not awarded use this option.
32
-
33
- t = ICU::RatedTournament.new(:desc => 'Linares', :start => "07/02/2009", :no_bonuses => true)
34
-
35
- Note, however, that the ICU system also has its own unique way of treating provisional, unrated and
36
- foreign players, so the only FIDE tournaments that can be rated using this software are those that
37
- consist solely of rated players.
38
-
39
- == Rating Tournaments
40
-
41
- To rate a tournament, first add the players (see ICU::RatedPlayer for details):
42
-
43
- t.add_player(1, :rating => 2534, :kfactor => 16)
44
- # ...
45
-
46
- Then add the results (see ICU::RatedResult for details):
47
-
48
- t.add_result(1, 1, 2, 'W')
49
- # ...
50
-
51
- Then rate the tournament by calling the <em>rate!</em> method:
52
-
53
- t.rate!
54
-
55
- Now the results of the rating calculations can be retrieved from the players in the tournement
56
- or their results. For example, player 1's new rating would be:
57
-
58
- t.player(1).new_rating
59
-
60
- See ICU::RatedPlayer and ICU::RatedResult for more details.
61
-
62
- == Error Handling
63
-
64
- Some of the above methods have the potential to raise RuntimeError exceptions.
65
- In the case of _add_player_ and _add_result_, the use of invalid arguments
66
- would cause such an error. Theoretically, the <em>rate!</em> method could also throw an
67
- exception if the iterative algorithm it uses to estimate performance ratings
68
- of unrated players failed to converge. However an instance of non-convergence
69
- has yet to be observed in practice.
70
-
71
- Since exception throwing is how errors are signalled, you should arrange for them
72
- to be caught and handled in some suitable place in your code.
73
-
74
- =end
75
-
4
+ #
5
+ # == Creating Tournaments
6
+ #
7
+ # ICU::RatedTournament objects are created directly.
8
+ #
9
+ # t = ICU::RatedTournament.new
10
+ #
11
+ # They have some optional parameters which can be set via the constructor or by calling
12
+ # the same-named setter methods. One is called _desc_ (short for description) the value
13
+ # of which can be any object but will, if utilized, typically be the name of the
14
+ # tournament as a string.
15
+ #
16
+ # t = ICU::RatedTournament.new(:desc => "Irish Championships 2010")
17
+ # puts t.desc # "Irish Championships 2010"
18
+ #
19
+ # Another optional parameter is _start_ for the start date. A Date object or a string that can be
20
+ # parsed as a string can be used to set it. The European convention is preferred for dates like
21
+ # "03/06/2013" (3rd of June, not 6th of March). Attempting to set an invalid date will raise an
22
+ # exception.
23
+ #
24
+ # t = ICU::RatedTournament.new(:start => "01/07/2010")
25
+ # puts t.start.class # Date
26
+ # puts t.start.to_s # "2010-07-01"
27
+ #
28
+ # Also, there is the option _no_bonuses_. Bonuses are a feature of the ICU rating system. If you are
29
+ # rating a non-ICU tournament (such as a FIDE tournament) where bonues are not awarded use this option.
30
+ #
31
+ # t = ICU::RatedTournament.new(:desc => 'Linares', :start => "07/02/2009", :no_bonuses => true)
32
+ #
33
+ # Note, however, that the ICU system also has its own unique way of treating provisional, unrated and
34
+ # foreign players, so the only FIDE tournaments that can be rated using this software are those that
35
+ # consist solely of rated players.
36
+ #
37
+ # == Rating Tournaments
38
+ #
39
+ # To rate a tournament, first add the players (see ICU::RatedPlayer for details):
40
+ #
41
+ # t.add_player(1, :rating => 2534, :kfactor => 16)
42
+ # # ...
43
+ #
44
+ # Then add the results (see ICU::RatedResult for details):
45
+ #
46
+ # t.add_result(1, 1, 2, 'W')
47
+ # # ...
48
+ #
49
+ # Then rate the tournament by calling the <em>rate!</em> method:
50
+ #
51
+ # t.rate!
52
+ #
53
+ # Now the results of the rating calculations can be retrieved from the players in the tournement
54
+ # or their results. For example, player 1's new rating would be:
55
+ #
56
+ # t.player(1).new_rating
57
+ #
58
+ # See ICU::RatedPlayer and ICU::RatedResult for more details.
59
+ #
60
+ # == Error Handling
61
+ #
62
+ # Some of the above methods have the potential to raise RuntimeError exceptions.
63
+ # In the case of _add_player_ and _add_result_, the use of invalid arguments
64
+ # would cause such an error. Theoretically, the <em>rate!</em> method could also throw an
65
+ # exception if the iterative algorithm it uses to estimate performance ratings
66
+ # of unrated players failed to converge. However an instance of non-convergence
67
+ # has yet to be observed in practice.
68
+ #
69
+ # Since exception throwing is how errors are signalled, you should arrange for them
70
+ # to be caught and handled in some suitable place in your code.
71
+ #
76
72
  class RatedTournament
77
73
  attr_accessor :desc
78
74
  attr_reader :start, :no_bonuses
@@ -3,33 +3,18 @@ require 'date' # needed for 1.9.1
3
3
  module ICU
4
4
  class Util
5
5
 
6
- =begin rdoc
7
-
8
- == Parsing dates
9
-
10
- The method _parsedate!_ parses strings into date objects, interpreting nn/nn/nnnn as dd/mm/yyyy. It raises an exception on error.
11
-
12
- Util.parsedate!('1955-11-09') # => Date (1955-11-09)
13
- Util.parsedate!('02/03/2009') # => Date (2009-03-02)
14
- Util.parsedate!('02/23/2009') # => Date (2009-02-23)
15
- Util.parsedate!('16th June 1986') # => Date (1986-06-16)
16
- Util.parsedate!('not a date') # exception raised
17
-
18
- Note that the parse method of the Date class behaves differently in Ruby 1.8.7 and 1.9.1.
19
- In 1.8.7 it assumes American dates and will raise ArgumentError on "30/03/2003".
20
- In 1.9.1 it assumes European dates and will raise ArgumentError on "03/30/2003".
21
-
22
- == Diffing dates
23
-
24
- The method _age_ returns the difference of two dates:
25
-
26
- Util.age(born, date) # age in years at the given date (Float)
27
- Util.age(born) # age in years now (today)
28
-
29
- Internally it uses _parsedate!_ so can throw a exception if an invalid date is supplied.
30
-
31
- =end
32
-
6
+ # Parses strings into date objects, interpreting nn/nn/nnnn as dd/mm/yyyy. It raises an exception on error.
7
+ #
8
+ # Util.parsedate!('1955-11-09') # => Date (1955-11-09)
9
+ # Util.parsedate!('02/03/2009') # => Date (2009-03-02)
10
+ # Util.parsedate!('02/23/2009') # => Date (2009-02-23)
11
+ # Util.parsedate!('16th June 1986') # => Date (1986-06-16)
12
+ # Util.parsedate!('not a date') # exception raised
13
+ #
14
+ # Note that the parse method of the Date class behaves differently in Ruby 1.8.7 and 1.9.1.
15
+ # In 1.8.7 it assumes American dates and will raise ArgumentError on "30/03/2003".
16
+ # In 1.9.1 it assumes European dates and will raise ArgumentError on "03/30/2003".
17
+ #
33
18
  def self.parsedate!(date)
34
19
  return date.clone if date.is_a?(Date)
35
20
  string = date.to_s.strip
@@ -42,6 +27,12 @@ Internally it uses _parsedate!_ so can throw a exception if an invalid date is s
42
27
  end
43
28
  end
44
29
 
30
+ # Returns the difference of two dates:
31
+ #
32
+ # Util.age(born, date) # age in years at the given date (Float)
33
+ # Util.age(born) # age in years now (today)
34
+ #
35
+ # Internally it uses _parsedate!_ so can throw a exception if an invalid date is supplied.
45
36
  def self.age(born, date=Date.today)
46
37
  born = parsedate!(born)
47
38
  date = parsedate!(date)
@@ -1,5 +1,7 @@
1
+ # :enddoc:
2
+
1
3
  module ICU
2
4
  class Ratings
3
- VERSION = "1.0.4"
5
+ VERSION = "1.0.5"
4
6
  end
5
7
  end
@@ -160,20 +160,20 @@ module ICU
160
160
  it "after the tournament is rated" do
161
161
  @t.rate!
162
162
 
163
- @t.player(1).expected_score.should be_close(2.249, 0.001)
164
- @t.player(2).expected_score.should be_close(1.760, 0.001)
165
- @t.player(3).expected_score.should be_close(1.240, 0.001)
166
- @t.player(4).expected_score.should be_close(0.751, 0.001)
163
+ @t.player(1).expected_score.should be_within(0.001).of(2.249)
164
+ @t.player(2).expected_score.should be_within(0.001).of(1.760)
165
+ @t.player(3).expected_score.should be_within(0.001).of(1.240)
166
+ @t.player(4).expected_score.should be_within(0.001).of(0.751)
167
167
 
168
- @t.player(1).rating_change.should be_close(7.51, 0.01)
169
- @t.player(2).rating_change.should be_close(4.81, 0.01)
170
- @t.player(3).rating_change.should be_close(-7.21, 0.01)
171
- @t.player(4).rating_change.should be_close(-30.05, 0.01)
168
+ @t.player(1).rating_change.should be_within(0.01).of(7.51)
169
+ @t.player(2).rating_change.should be_within(0.01).of(4.81)
170
+ @t.player(3).rating_change.should be_within(0.01).of(-7.21)
171
+ @t.player(4).rating_change.should be_within(0.01).of(-30.05)
172
172
 
173
- @t.player(1).new_rating.should be_close(2207.5, 0.1)
174
- @t.player(2).new_rating.should be_close(2104.8, 0.1)
175
- @t.player(3).new_rating.should be_close(1992.8, 0.1)
176
- @t.player(4).new_rating.should be_close(1870.0, 0.1)
173
+ @t.player(1).new_rating.should be_within(0.1).of(2207.5)
174
+ @t.player(2).new_rating.should be_within(0.1).of(2104.8)
175
+ @t.player(3).new_rating.should be_within(0.1).of(1992.8)
176
+ @t.player(4).new_rating.should be_within(0.1).of(1870.0)
177
177
  end
178
178
  end
179
179
 
@@ -229,8 +229,8 @@ module ICU
229
229
  ].each do |item|
230
230
  num, expected_score, new_rating = item
231
231
  p = @t.player(num)
232
- p.expected_score.should be_close(expected_score, 0.001)
233
- p.new_rating.should be_close(new_rating, 0.5)
232
+ p.expected_score.should be_within(0.001).of(expected_score)
233
+ p.new_rating.should be_within(0.5).of(new_rating)
234
234
  end
235
235
  end
236
236
 
@@ -245,8 +245,8 @@ module ICU
245
245
  ].each do |item|
246
246
  num, ytd_performance, tournament_performance = item
247
247
  p = @t.player(num)
248
- p.performance.should_not be_close(ytd_performance, 0.5)
249
- p.performance.should be_close(tournament_performance, 0.5)
248
+ p.performance.should_not be_within(0.5).of(ytd_performance)
249
+ p.performance.should be_within(0.5).of(tournament_performance)
250
250
  end
251
251
  end
252
252
  end
@@ -263,10 +263,10 @@ module ICU
263
263
  end
264
264
 
265
265
  it "should get same results as ICU rating database" do
266
- @t.player(1).expected_score.should be_close(1.689, 0.001)
267
- @t.player(2).expected_score.should be_close(1.311, 0.001)
268
- @t.player(1).new_rating.should be_close(1378, 0.5)
269
- @t.player(2).new_rating.should be_close(1261, 0.5)
266
+ @t.player(1).expected_score.should be_within(0.001).of(1.689)
267
+ @t.player(2).expected_score.should be_within(0.001).of(1.311)
268
+ @t.player(1).new_rating.should be_within(0.5).of(1378)
269
+ @t.player(2).new_rating.should be_within(0.5).of(1261)
270
270
  end
271
271
  end
272
272
 
@@ -380,12 +380,12 @@ module ICU
380
380
  pc = @t.player(2)
381
381
 
382
382
  af.score.should == 4.5
383
- af.expected_score.should be_close(6.054, 0.001)
384
- af.new_rating.should be_close(2080, 0.5)
383
+ af.expected_score.should be_within(0.001).of(6.054)
384
+ af.new_rating.should be_within(0.5).of(2080)
385
385
 
386
386
  pc.score.should == 4.0
387
- pc.expected_score.should be_close(3.685, 0.001)
388
- pc.new_rating.should be_close(1984, 0.5)
387
+ pc.expected_score.should be_within(0.001).of(3.685)
388
+ pc.new_rating.should be_within(0.5).of(1984)
389
389
  end
390
390
  end
391
391
 
@@ -534,9 +534,9 @@ module ICU
534
534
  ].each do |item|
535
535
  num, expected_score, new_rating = item
536
536
  p = @t.player(num)
537
- p.expected_score.should be_close(expected_score, 0.01)
538
- p.new_rating.should be_close(new_rating, 0.5)
539
- p.results.inject(p.rating){ |t,r| t + r.rating_change }.should be_close(new_rating, 0.5)
537
+ p.expected_score.should be_within(0.01).of(expected_score)
538
+ p.new_rating.should be_within(0.5).of(new_rating)
539
+ p.results.inject(p.rating){ |t,r| t + r.rating_change }.should be_within(0.5).of(new_rating)
540
540
  end
541
541
  end
542
542
 
@@ -547,8 +547,8 @@ module ICU
547
547
  ].each do |item|
548
548
  num, expected_score, new_rating = item
549
549
  p = @t.player(num)
550
- p.expected_score.should be_close(expected_score, 0.01)
551
- p.new_rating.should be_close(new_rating, 0.5)
550
+ p.expected_score.should be_within(0.01).of(expected_score)
551
+ p.new_rating.should be_within(0.5).of(new_rating)
552
552
  end
553
553
  end
554
554
 
@@ -608,8 +608,8 @@ module ICU
608
608
  ].each do |item|
609
609
  num, expected_score, new_rating = item
610
610
  p = @t.player(num)
611
- p.expected_score.should be_close(expected_score, 0.01)
612
- p.new_rating.should be_close(new_rating, 0.5)
611
+ p.expected_score.should be_within(0.01).of(expected_score)
612
+ p.new_rating.should be_within(0.5).of(new_rating)
613
613
  end
614
614
  end
615
615
  end
@@ -661,8 +661,8 @@ module ICU
661
661
  num, score, expected_score, new_rating, bonus = item
662
662
  p = @t.player(num)
663
663
  p.score.should == score
664
- p.expected_score.should be_close(expected_score, 0.01)
665
- p.new_rating.should be_close(new_rating, 0.5)
664
+ p.expected_score.should be_within(0.01).of(expected_score)
665
+ p.new_rating.should be_within(0.5).of(new_rating)
666
666
  p.bonus.should == bonus
667
667
  end
668
668
  end
@@ -713,10 +713,10 @@ module ICU
713
713
  num, score, expected_score, performance = item
714
714
  p = @t.player(num)
715
715
  p.score.should == score
716
- p.expected_score.should be_close(expected_score, 0.01)
717
- p.performance.should be_close(performance, 0.5)
716
+ p.expected_score.should be_within(0.01).of(expected_score)
717
+ p.performance.should be_within(0.5).of(performance)
718
718
  if num == 1
719
- p.new_rating.should be_close(1836, 0.5)
719
+ p.new_rating.should be_within(0.5).of(1836)
720
720
  p.bonus.should == 71
721
721
  else
722
722
  p.new_rating.should == p.rating
@@ -773,9 +773,9 @@ module ICU
773
773
  p = @t.player(num)
774
774
  p.score.should == score
775
775
  p.bonus.should == bonus
776
- p.performance.should be_close(performance, 0.5)
777
- p.expected_score.should be_close(expected_score, 0.01)
778
- p.new_rating.should be_close(new_rating, 0.5)
776
+ p.performance.should be_within(0.5).of(performance)
777
+ p.expected_score.should be_within(0.01).of(expected_score)
778
+ p.new_rating.should be_within(0.5).of(new_rating)
779
779
  end
780
780
  end
781
781
  end
@@ -833,9 +833,9 @@ module ICU
833
833
  p = @t.player(num)
834
834
  p.score.should == score
835
835
  p.bonus.should == bonus
836
- p.performance.should be_close(performance, num == 2 ? 0.6 : 0.5)
837
- p.expected_score.should be_close(expected_score, 0.01)
838
- p.new_rating.should be_close(new_rating, 0.5)
836
+ p.performance.should be_within(num == 2 ? 0.6 : 0.5).of(performance)
837
+ p.expected_score.should be_within(0.01).of(expected_score)
838
+ p.new_rating.should be_within(0.5).of(new_rating)
839
839
  end
840
840
  end
841
841
 
@@ -846,9 +846,9 @@ module ICU
846
846
  p = @t.player(num)
847
847
  p.score.should == score
848
848
  p.bonus.should == bonus
849
- p.performance.should be_close(performance, num == 2 ? 0.6 : 0.5)
850
- p.expected_score.should be_close(expected_score, 0.01)
851
- p.new_rating.should be_close(new_rating, 0.5)
849
+ p.performance.should be_within(num == 2 ? 0.6 : 0.5).of(performance)
850
+ p.expected_score.should be_within(0.01).of(expected_score)
851
+ p.new_rating.should be_within(0.5).of(new_rating)
852
852
  end
853
853
  end
854
854
 
@@ -860,9 +860,9 @@ module ICU
860
860
  p = @t.player(num)
861
861
  p.score.should == score
862
862
  p.bonus.should == 0
863
- p.performance.should_not be_close(performance, 1.0)
864
- p.expected_score.should_not be_close(expected_score, 0.01)
865
- p.new_rating.should_not be_close(new_rating, 1.0)
863
+ p.performance.should_not be_within(1.0).of(performance)
864
+ p.expected_score.should_not be_within(0.01).of(expected_score)
865
+ p.new_rating.should_not be_within(1.0).of(new_rating)
866
866
  end
867
867
  end
868
868
  end
@@ -901,8 +901,8 @@ module ICU
901
901
  ].each do |item|
902
902
  num, expected_score, new_rating = item
903
903
  p = @t.player(num)
904
- p.expected_score.should be_close(expected_score, 0.01)
905
- p.new_rating.should be_close(new_rating, 0.5)
904
+ p.expected_score.should be_within(0.01).of(expected_score)
905
+ p.new_rating.should be_within(0.5).of(new_rating)
906
906
  end
907
907
  end
908
908
 
data/spec/util_spec.rb CHANGED
@@ -50,9 +50,9 @@ module ICU
50
50
  it "should return age in years" do
51
51
  Util.age('2001-01-01', '2001-01-01').should == 0.0
52
52
  Util.age('2001-01-01', '2002-01-01').should == 1.0
53
- Util.age('2001-01-01', '2001-01-02').should be_close(1/365.0, 0.01)
54
- Util.age('2001-01-01', '2001-02-01').should be_close(1/12.0, 0.01)
55
- Util.age('1955-11-09', '2010-01-17').should be_close(54.2, 0.01)
53
+ Util.age('2001-01-01', '2001-01-02').should be_within(0.01).of(1/365)
54
+ Util.age('2001-01-01', '2001-02-01').should be_within(0.01).of(1/12.0)
55
+ Util.age('1955-11-09', '2010-01-17').should be_within(0.01).of(54.2)
56
56
  Util.age('2001-01-01', '2000-01-01').should == -1.0
57
57
  end
58
58
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 0
8
- - 4
9
- version: 1.0.4
8
+ - 5
9
+ version: 1.0.5
10
10
  platform: ruby
11
11
  authors:
12
12
  - Mark Orr
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-10-14 00:00:00 +01:00
17
+ date: 2010-12-31 00:00:00 +00:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -27,9 +27,9 @@ dependencies:
27
27
  - !ruby/object:Gem::Version
28
28
  segments:
29
29
  - 2
30
+ - 3
30
31
  - 0
31
- - 0
32
- version: 2.0.0
32
+ version: 2.3.0
33
33
  type: :development
34
34
  version_requirements: *id001
35
35
  description: Build an object that represents a chess tournament then get it to calculate ratings of all the players.
@@ -82,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
82
82
  version: "0"
83
83
  requirements: []
84
84
 
85
- rubyforge_project:
85
+ rubyforge_project: icu_ratings
86
86
  rubygems_version: 1.3.7
87
87
  signing_key:
88
88
  specification_version: 3