elo 0.0.2.alpha → 0.0.3.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -45,16 +45,16 @@ There is more than one way to do it, choose whatever works from your other code:
45
45
 
46
46
  You can get all kinds of info from a player:
47
47
 
48
- bob.rating
49
- bob.pro?
50
- bob.starter?
51
- bob.pro_rating?
52
- bob.games_played
53
- bob.games
48
+ bob.rating # => 1084
49
+ bob.pro? # => false
50
+ bob.starter? # => true
51
+ bob.games_played # => 7
52
+ bob.games # => [ game1, game2, ... game7 ]
54
53
 
55
54
  Or get a list of players:
56
55
 
57
- Elo::Player.all
56
+ Elo::Player.all # => [ bob, jane ]
57
+ Elo::Game.all # => [ game1, game2, ... game7 ]
58
58
 
59
59
 
60
60
  == Configuration
@@ -91,14 +91,15 @@ Once you reach a pro status, you're K-factor never changes, even if your rating
91
91
 
92
92
  You need to provide Elo the amount of games played, their rating and their pro-status.
93
93
 
94
- bob = Elo::Player.new(:games_played => 29, :rating => 2300, :pro => true)
94
+ bob = Elo::Player.new(:games_played => 29, :rating => 2399, :pro => true)
95
+ bob.k_factor == 10
95
96
 
96
97
  You can define your own K-factors by adding K-factor rules.
97
- This code will change the K-factor to 12, for every player who's name starts with a B,
98
- and 16 for everybody else.
98
+ This code will change the K-factor to 12, for every player that played less than 10
99
+ games, and 16 for everybody else.
99
100
 
100
101
  Elo.configure do |config|
101
- config.k_factor(12) { name =~ /^b/i }
102
+ config.k_factor(12) { games_played < 10 }
102
103
  config.default_k_factor = 16
103
104
  config.use_FIDE_settings = false
104
105
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2.alpha
1
+ 0.0.3.alpha
@@ -0,0 +1,73 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{elo}
8
+ s.version = "0.0.3.alpha"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Iain Hecker"]
12
+ s.date = %q{2010-03-15}
13
+ s.description = %q{The Elo rating system is a method for calculating the relative skill levels of players in two-player games such as cess and Go.}
14
+ s.email = %q{iain@iain.nl}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ ".gitignore",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "doc/classes/Elo.html",
25
+ "doc/classes/Elo/Configuration.html",
26
+ "doc/classes/Elo/EloHelper.html",
27
+ "doc/classes/Elo/EloHelper/ClassMethods.html",
28
+ "doc/classes/Elo/Game.html",
29
+ "doc/classes/Elo/Player.html",
30
+ "doc/classes/Elo/Rating.html",
31
+ "doc/created.rid",
32
+ "doc/files/README_rdoc.html",
33
+ "doc/files/lib/elo_rb.html",
34
+ "doc/fr_class_index.html",
35
+ "doc/fr_file_index.html",
36
+ "doc/fr_method_index.html",
37
+ "doc/index.html",
38
+ "doc/rdoc-style.css",
39
+ "elo.gemspec",
40
+ "lib/elo.rb",
41
+ "lib/elo/configuration.rb",
42
+ "lib/elo/game.rb",
43
+ "lib/elo/helper.rb",
44
+ "lib/elo/player.rb",
45
+ "lib/elo/rating.rb",
46
+ "spec/elo_spec.rb",
47
+ "spec/spec.opts",
48
+ "spec/spec_helper.rb"
49
+ ]
50
+ s.homepage = %q{http://github.com/iain/elo}
51
+ s.rdoc_options = ["--charset=UTF-8"]
52
+ s.require_paths = ["lib"]
53
+ s.rubygems_version = %q{1.3.6}
54
+ s.summary = %q{The Elo rating system is a method for calculating the relative skill levels of players in two-player games such as cess and Go.}
55
+ s.test_files = [
56
+ "spec/elo_spec.rb",
57
+ "spec/spec_helper.rb"
58
+ ]
59
+
60
+ if s.respond_to? :specification_version then
61
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62
+ s.specification_version = 3
63
+
64
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
65
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
66
+ else
67
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
68
+ end
69
+ else
70
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
71
+ end
72
+ end
73
+
data/lib/elo.rb CHANGED
@@ -1,298 +1,26 @@
1
- # The configuration of Elo is done here.
2
- #
1
+ require 'elo/helper'
2
+ require 'elo/configuration'
3
+ require 'elo/game'
4
+ require 'elo/player'
5
+ require 'elo/rating'
6
+
3
7
  # See README.rdoc for general information about Elo.
4
8
  module Elo
5
9
 
6
- @pro_rating_boundry = 2400
7
- @starter_boundry = 30
8
- @default_rating = 1000
9
- @default_k_factor = 15
10
- @use_FIDE_settings = true
11
-
12
- module Configuration
13
-
14
- # Add a K-factor rule. The first argument is the k-factor value.
15
- # The block should return a boolean that determines if this K-factor rule applies.
16
- # The first rule that applies is the one determining the K-factor.
17
- #
18
- # The block is instance_eval'ed into the player, so you can access all it's
19
- # properties directly. The K-factor is recalculated every time a match is played.
20
- #
21
- # By default, the FIDE settings are used (see: +use_FIDE_settings+). To implement
22
- # that yourself, you could write:
23
- #
24
- # Elo.configure do |config|
25
- # config.k_factor(10) { pro? or pro_rating? }
26
- # config.k_factor(25) { starter? }
27
- # config.default_k_factor = 15
28
- # end
29
- #
30
- def k_factor(factor, &rule)
31
- k_factors << { :factor => factor, :rule => rule }
32
- end
33
-
34
- # This is the lower boundry of the rating you need to be a pro player.
35
- # This setting is used in the FIDE k-factor rules. (default = 2400)
36
- attr_accessor :pro_rating_boundry
37
-
38
- # This is the lower boundry in the amount of games played to be a starting player
39
- # This setting is used in the FIDE k-factor rules. (default = 30)
40
- attr_accessor :starter_boundry
41
-
42
- # The default k-factor is chosen when no k-factor rules apply.
43
- # K-factor rules can be added by using the +k_factor+-method. (default = 15)
44
- attr_accessor :default_k_factor
45
-
46
- # This is the rating every player starts out with. (default = 1000)
47
- attr_accessor :default_rating
48
-
49
- # Use the settings that FIDE use for determening the K-factor.
50
- # This is the case when all settings are unaltered. (default = true)
51
- #
52
- # In short:
53
- #
54
- # * K-factor is 25 when a player is a starter (less than 30 games played)
55
- # * K-factor is 10 when a player is a pro (rating above 2400, now or in the past)
56
- # * K-factor is 15 when a player in other cases
57
- #
58
- # If you want to use your own settings, either change the boundry settings,
59
- # or set this setting to false and add you're own k-factor rules.
60
- # K-factor rules can be added by using the +k_factor+-method.
61
- attr_accessor :use_FIDE_settings
62
-
63
- # Configure Elo in a block style.
64
- #
65
- # Elo.configure do |config|
66
- # config.setting = value
67
- # end
68
- def configure(&block)
69
- yield(self)
70
- end
71
-
72
- private
73
-
74
- def k_factors
75
- @k_factors ||= []
76
- end
77
-
78
- def applied_k_factors
79
- apply_fide_k_factors if use_FIDE_settings
80
- k_factors
81
- end
82
-
83
- def apply_fide_k_factors
84
- unless @applied_fide_k_factors
85
- k_factor(10) { pro? or pro_rating? }
86
- k_factor(25) { starter? }
87
- @applied_fide_k_factors = true
88
- end
89
- end
90
-
10
+ # Accessor to the configuration object, which,
11
+ # should be instantiated only once (and automatically).
12
+ def self.config
13
+ @config ||= Configuration.new
91
14
  end
92
15
 
93
- extend Configuration
94
-
95
- # Common methods for Elo classes.
96
- module EloHelper
97
-
98
- def self.included(base)
99
- base.extend ClassMethods
100
- end
101
-
102
- # Every object can be initialized with a hash, just like in ActiveRecord.
103
- def initialize(attributes)
104
- @attributes == attributes.keys
105
- attributes.each do |key, value|
106
- self.class.attr_reader key
107
- instance_variable_set("@#{key}", value)
108
- end
109
- self.class.all << self
110
- end
111
-
112
- # Get a hash of all attributes provided
113
- def attributes
114
- hash = {}
115
- @attributes.each do |attribute|
116
- hash.update attribute => send(attribute)
117
- end
118
- hash
119
- end
120
-
121
- module ClassMethods
122
-
123
- # Provides a list of all instantiated objects of the class.
124
- def all
125
- @all ||= []
126
- end
127
-
128
- end
129
-
130
- end
131
-
132
- # A player. You need at least two play a Game.
133
- class Player
134
-
135
- include EloHelper
136
-
137
- # Rating
138
- def rating
139
- @rating ||= Elo.default_rating
140
- end
141
-
142
- def games_played
143
- @games_played ||= games.size
144
- end
145
-
146
- def games
147
- @games ||= []
148
- end
149
-
150
- def pro_rating?
151
- rating > Elo.pro_rating_boundry
152
- end
153
-
154
- def starter?
155
- games_played < Elo.starter_boundry
156
- end
157
-
158
- def pro?
159
- !!@pro
160
- end
161
-
162
- # TODO
163
- def save
164
- # hook for your own model
165
- # which I don't know yet how to do
166
- end
167
-
168
- def k_factor
169
- Elo.applied_k_factors.each do |rule|
170
- return rule[:factor] if instance_eval(&rule[:rule])
171
- end
172
- Elo.default_k_factor
173
- end
174
-
175
- def versus(other_player)
176
- Game.new(:one => self, :two => other_player)
177
- end
178
-
179
- def wins_from(other_player)
180
- versus(other_player).win
181
- end
182
-
183
- def plays_draw(other_player)
184
- versus(other_player).draw
185
- end
186
-
187
- def loses_from(other_player)
188
- versus(other_player).lose
189
- end
190
-
191
- private
192
-
193
- # A Game tells the players informed to update their
194
- # scores, after it knows the result (so it can calculate the rating).
195
- #
196
- # This method is private, because it is called automatically.
197
- # Therefore it is not part of the public API of Elo.
198
- def played(game)
199
- games_played += 1
200
- games << game
201
- @rating = game.new_rating(self)
202
- @pro = true if pro_rating?
203
- save
204
- end
205
-
206
-
207
- end
208
-
209
- class Game
210
-
211
- include EloHelper
212
-
213
- # Result is from the perspective of player one.
214
- def result=(result)
215
- @result = result
216
- one.send(:played, self)
217
- two.send(:played, self)
218
- save
219
- end
220
-
221
- def win
222
- self.result = 1.0
223
- end
224
-
225
- def lose
226
- self.result = 0.0
227
- end
228
-
229
- def draw
230
- self.result = 0.5
231
- end
232
-
233
- # TODO
234
- def save
235
- end
236
-
237
- def winner=(player)
238
- self.result = (player == :one ? 1.0 : 0.0)
239
- end
240
-
241
- def loser=(player)
242
- self.result = (player == :one ? 0.0 : 1.0)
243
- end
244
-
245
- def new_rating(player)
246
- ratings[player].new_rating
247
- end
248
-
249
- private
250
-
251
- def ratings
252
- @ratings ||= { one => rating_one, two => rating_two }
253
- end
254
-
255
- def rating_one
256
- Rating.new(:result => result,
257
- :old_rating => one.rating,
258
- :other_rating => two.rating,
259
- :k_factor => one.k_factor)
260
- end
261
-
262
- def rating_two
263
- Rating.new(:result => (1.0 - result),
264
- :old_rating => two.rating,
265
- :other_rating => one.rating,
266
- :k_factor => two.k_factor)
267
- end
268
-
269
- end
270
-
271
- class Rating
272
-
273
- include EloHelper
274
-
275
- def result
276
- raise "Invalid result: #{@result.inspect}" unless valid_result?
277
- @result.to_f
278
- end
279
-
280
- def valid_result?
281
- (0..1).include? @result
282
- end
283
-
284
- def expected
285
- 1.0 / ( 1.0 + ( 10.0 ** ((other_rating.to_f - old_rating.to_f) / 400.0) ) )
286
- end
287
-
288
- def change
289
- k_factor.to_f * ( result.to_f - expected )
290
- end
291
-
292
- def new_rating
293
- (old_rating.to_f + change).to_i
294
- end
295
-
16
+ # Configure Elo in a block style.
17
+ # See Elo::Configuration for more details.
18
+ #
19
+ # Elo.configure do |config|
20
+ # config.attribute = :value
21
+ # end
22
+ def self.configure(&block)
23
+ yield(config)
296
24
  end
297
25
 
298
26
  end
@@ -0,0 +1,83 @@
1
+ module Elo
2
+
3
+ class Configuration
4
+
5
+ # This is the lower boundry of the rating you need to be a pro player.
6
+ # This setting is used in the FIDE k-factor rules. (default = 2400)
7
+ attr_accessor :pro_rating_boundry
8
+
9
+ # This is the lower boundry in the amount of games played to be a starting player
10
+ # This setting is used in the FIDE k-factor rules. (default = 30)
11
+ attr_accessor :starter_boundry
12
+
13
+ # The default k-factor is chosen when no k-factor rules apply.
14
+ # K-factor rules can be added by using the +k_factor+-method. (default = 15)
15
+ attr_accessor :default_k_factor
16
+
17
+ # This is the rating every player starts out with. (default = 1000)
18
+ attr_accessor :default_rating
19
+
20
+ # Use the settings that FIDE use for determening the K-factor.
21
+ # This is the case when all settings are unaltered. (default = true)
22
+ #
23
+ # In short:
24
+ #
25
+ # * K-factor is 25 when a player is a starter (less than 30 games played)
26
+ # * K-factor is 10 when a player is a pro (rating above 2400, now or in the past)
27
+ # * K-factor is 15 when a player in other cases
28
+ #
29
+ # If you want to use your own settings, either change the boundry settings,
30
+ # or set this setting to false and add you're own k-factor rules.
31
+ # K-factor rules can be added by using the +k_factor+-method.
32
+ attr_accessor :use_FIDE_settings
33
+
34
+ def initialize #:nodoc:
35
+ @pro_rating_boundry = 2400
36
+ @starter_boundry = 30
37
+ @default_rating = 1000
38
+ @default_k_factor = 15
39
+ @use_FIDE_settings = true
40
+ end
41
+
42
+ # Add a K-factor rule. The first argument is the k-factor value.
43
+ # The block should return a boolean that determines if this K-factor rule applies.
44
+ # The first rule that applies is the one determining the K-factor.
45
+ #
46
+ # The block is instance_eval'ed into the player, so you can access all it's
47
+ # properties directly. The K-factor is recalculated every time a match is played.
48
+ #
49
+ # By default, the FIDE settings are used (see: +use_FIDE_settings+). To implement
50
+ # that yourself, you could write:
51
+ #
52
+ # Elo.configure do |config|
53
+ # config.k_factor(10) { pro? or pro_rating? }
54
+ # config.k_factor(25) { starter? }
55
+ # config.default_k_factor = 15
56
+ # end
57
+ #
58
+ def k_factor(factor, &rule)
59
+ k_factors << { :factor => factor, :rule => rule }
60
+ end
61
+
62
+ def applied_k_factors #:nodoc:
63
+ apply_fide_k_factors if use_FIDE_settings
64
+ k_factors
65
+ end
66
+
67
+ private
68
+
69
+ def k_factors
70
+ @k_factors ||= []
71
+ end
72
+
73
+ def apply_fide_k_factors
74
+ unless @applied_fide_k_factors
75
+ k_factor(10) { pro? or pro_rating? }
76
+ k_factor(25) { starter? }
77
+ @applied_fide_k_factors = true
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,92 @@
1
+ module Elo
2
+
3
+ # A Game is a collection of two Elo::Player objects
4
+ # and a result.
5
+ # Once the result is known, it propagates the new
6
+ # ratings to the players.
7
+ class Game
8
+
9
+ include Helper
10
+
11
+ # The result is the result of the match. It's a nubmer
12
+ # from 0 to 1 from the perspective of player +:one+.
13
+ attr_reader :result
14
+
15
+ # The first Elo::Player. The result is in perspecive of
16
+ # this player.
17
+ attr_reader :one
18
+
19
+ # The second Elo::Player.
20
+ attr_reader :two
21
+
22
+ # Every time a result is set, it tells the Elo::Player
23
+ # objects to update their scores.
24
+ def process_result(result)
25
+ @result = result
26
+ one.send(:played, self)
27
+ two.send(:played, self)
28
+ save
29
+ self
30
+ end
31
+ alias result= process_result
32
+
33
+ # Player +:one+ has won!
34
+ # This is a shortcut method for setting the score to 1
35
+ def win
36
+ process_result 1.0
37
+ end
38
+
39
+ # Player +:one+ has lost!
40
+ # This is a shortcut method for setting the score to 0
41
+ def lose
42
+ process_result 0.0
43
+ end
44
+
45
+ # It was a draw.
46
+ # This is a shortcut method for setting the score to 0.5
47
+ def draw
48
+ process_result 0.5
49
+ end
50
+
51
+ # You can override this method if you store each game
52
+ # in a database or something like that.
53
+ # This method will be called when a result is known.
54
+ def save
55
+ end
56
+
57
+ # Set the winner. Provide it with a Elo::Player.
58
+ def winner=(player)
59
+ process_result(player == one ? 1.0 : 0.0)
60
+ end
61
+
62
+ # Set the loser. Provide it with a Elo::Player.
63
+ def loser=(player)
64
+ process_result(player == one ? 0.0 : 1.0)
65
+ end
66
+
67
+ # Access the Elo::Rating objects for both players.
68
+ def ratings
69
+ @ratings ||= { one => rating_one, two => rating_two }
70
+ end
71
+
72
+ private
73
+
74
+ # Create an Elo::Rating object for player one
75
+ def rating_one
76
+ Rating.new(:result => result,
77
+ :old_rating => one.rating,
78
+ :other_rating => two.rating,
79
+ :k_factor => one.k_factor)
80
+ end
81
+
82
+ # Create an Elo::Rating object for player two
83
+ def rating_two
84
+ Rating.new(:result => (1.0 - result),
85
+ :old_rating => two.rating,
86
+ :other_rating => one.rating,
87
+ :k_factor => two.k_factor)
88
+ end
89
+
90
+ end
91
+
92
+ end
@@ -0,0 +1,29 @@
1
+ module Elo
2
+
3
+ module Helper
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ # Every object can be initialized with a hash,
10
+ # almost, but not quite, entirely unlike ActiveRecord.
11
+ def initialize(attributes = {})
12
+ attributes.each do |key, value|
13
+ instance_variable_set("@#{key}", value)
14
+ end
15
+ self.class.all << self
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ # Provides a list of all instantiated objects of the class.
21
+ def all
22
+ @all ||= []
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,110 @@
1
+ module Elo
2
+
3
+ # A player. You need at least two play a Game.
4
+ class Player
5
+
6
+ include Helper
7
+
8
+ # The rating you provided, or the default rating from configuration
9
+ def rating
10
+ @rating ||= Elo.config.default_rating
11
+ end
12
+
13
+ # The number of games played is needed for calculating the K-factor.
14
+ def games_played
15
+ @games_played ||= games.size
16
+ end
17
+
18
+ # A list of games played by the player.
19
+ def games
20
+ @games ||= []
21
+ end
22
+
23
+ # Is the player considered a pro, because his/her rating crossed
24
+ # the threshold configured? This is needed for calculating the K-factor.
25
+ def pro_rating?
26
+ rating >= Elo.config.pro_rating_boundry
27
+ end
28
+
29
+ # Is the player just starting? Provide the boundry for
30
+ # the amount of games played in the configuration.
31
+ # This is needed for calculating the K-factor.
32
+ def starter?
33
+ games_played < Elo.config.starter_boundry
34
+ end
35
+
36
+ # FIDE regulations specify that once you reach a pro status
37
+ # (see +pro_rating?+), you are considered a pro for life.
38
+ #
39
+ # You might need to specify it manually, when depending on
40
+ # external persistence of players.
41
+ #
42
+ # Elo::Player.new(:pro => true)
43
+ def pro?
44
+ !!@pro
45
+ end
46
+
47
+ # You can override this method if you store each game
48
+ # in a database or something like that.
49
+ # This method will be called when a result is known.
50
+ def save
51
+ end
52
+
53
+ # Calculates the K-factor for the player.
54
+ # Elo allows you specify custom Rules (see Elo::Configuration).
55
+ #
56
+ # You can set it manually, if you wish:
57
+ #
58
+ # Elo::Player.new(:k_factor => 10)
59
+ #
60
+ # This stops this player from using the K-factor rules.
61
+ def k_factor
62
+ return @k_factor if @k_factor
63
+ Elo.config.applied_k_factors.each do |rule|
64
+ return rule[:factor] if instance_eval(&rule[:rule])
65
+ end
66
+ Elo.config.default_k_factor
67
+ end
68
+
69
+ # Start a game with another player. At this point, no
70
+ # result is known and nothing really happens.
71
+ def versus(other_player)
72
+ Game.new(:one => self, :two => other_player)
73
+ end
74
+
75
+ # Start a game with another player and set the score
76
+ # immediately.
77
+ def wins_from(other_player)
78
+ versus(other_player).win
79
+ end
80
+
81
+ # Start a game with another player and set the score
82
+ # immediately.
83
+ def plays_draw(other_player)
84
+ versus(other_player).draw
85
+ end
86
+
87
+ # Start a game with another player and set the score
88
+ # immediately.
89
+ def loses_from(other_player)
90
+ versus(other_player).lose
91
+ end
92
+
93
+ private
94
+
95
+ # A Game tells the players informed to update their
96
+ # scores, after it knows the result (so it can calculate the rating).
97
+ #
98
+ # This method is private, because it is called automatically.
99
+ # Therefore it is not part of the public API of Elo.
100
+ def played(game)
101
+ @games_played = games_played + 1
102
+ games << game
103
+ @rating = game.ratings[self].new_rating
104
+ @pro = true if pro_rating?
105
+ save
106
+ end
107
+
108
+ end
109
+
110
+ end
@@ -0,0 +1,59 @@
1
+ module Elo
2
+
3
+ # This class calculates the rating between two players,
4
+ # but only from one persons perspective. You need two Rating-instances
5
+ # to calculate ratings for both players. Luckily, Elo::Game handles
6
+ # this for you automatically.
7
+ class Rating
8
+
9
+ include Helper
10
+
11
+ # The rating of the player you DON"T wish to calculate.
12
+ attr_reader :other_rating
13
+
14
+ # The rating of the player you wish to calculate.
15
+ attr_reader :old_rating
16
+
17
+ # The k-factor you wish to use for this calculation.
18
+ attr_reader :k_factor
19
+
20
+ # The new rating is... wait for it... the new rating!
21
+ def new_rating
22
+ (old_rating.to_f + change).to_i
23
+ end
24
+
25
+ private
26
+
27
+ # The result of the match. 1 means that the player won, 0 means that the
28
+ # player lost and 0.5 means that it was a draw.
29
+ def result
30
+ raise "Invalid result: #{@result.inspect}" unless valid_result?
31
+ @result.to_f
32
+ end
33
+
34
+ # Only values between 0 and 1 are considered to be valid scores.
35
+ def valid_result?
36
+ (0..1).include? @result
37
+ end
38
+
39
+ # The expected score is the probably outcome of the match, depending
40
+ # on the difference in rating between the two players.
41
+ #
42
+ # For more information visit
43
+ # {Wikipedia}[http://en.wikipedia.org/wiki/Elo_rating_system#Mathematical_details]
44
+ def expected
45
+ 1.0 / ( 1.0 + ( 10.0 ** ((other_rating.to_f - old_rating.to_f) / 400.0) ) )
46
+ end
47
+
48
+ # The change is the points you earn or lose.
49
+ #
50
+ # For more information visit
51
+ # {Wikipedia}[http://en.wikipedia.org/wiki/Elo_rating_system#Mathematical_details]
52
+ def change
53
+ k_factor.to_f * ( result.to_f - expected )
54
+ end
55
+
56
+
57
+ end
58
+
59
+ end
@@ -1,7 +1,148 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "Elo" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
4
+
5
+ after do
6
+ Elo.instance_eval { @config = nil }
7
+ end
8
+
9
+ it "should work as advertised" do
10
+
11
+ bob = Elo::Player.new
12
+ jane = Elo::Player.new(:rating => 1500)
13
+
14
+ game1 = bob.wins_from(jane)
15
+ game2 = bob.loses_from(jane)
16
+ game3 = bob.plays_draw(jane)
17
+
18
+ game4 = bob.versus(jane)
19
+ game4.winner = jane
20
+
21
+ game5 = bob.versus(jane)
22
+ game5.loser = jane
23
+
24
+ game6 = bob.versus(jane)
25
+ game6.draw
26
+
27
+ game7 = bob.versus(jane)
28
+ game7.result = 1
29
+
30
+ bob.rating.should == 1084
31
+ jane.rating.should == 1409
32
+ bob.should_not be_pro
33
+ bob.should be_starter
34
+ bob.games_played.should == 7
35
+ bob.games.should == [ game1, game2, game3, game4, game5, game6, game7 ]
36
+
37
+ Elo::Player.all.should == [ bob, jane ]
38
+ Elo::Game.all.should == [ game1, game2, game3, game4, game5, game6, game7 ]
39
+
40
+ end
41
+
42
+ describe "Configuration" do
43
+
44
+ it "default_rating" do
45
+ Elo.config.default_rating.should == 1000
46
+ Elo::Player.new.rating.should == 1000
47
+
48
+ Elo.config.default_rating = 1337
49
+
50
+ Elo.config.default_rating.should == 1337
51
+ Elo::Player.new.rating.should == 1337
52
+ end
53
+
54
+ it "starter_boundry" do
55
+ Elo.config.starter_boundry.should == 30
56
+ Elo::Player.new(:games_played => 20).should be_starter
57
+
58
+ Elo.config.starter_boundry = 15
59
+
60
+ Elo.config.starter_boundry.should == 15
61
+ Elo::Player.new(:games_played => 20).should_not be_starter
62
+ end
63
+
64
+ it "default_k_factor and FIDE settings" do
65
+ Elo.config.use_FIDE_settings.should == true
66
+ Elo.config.default_k_factor.should == 15
67
+
68
+ Elo.config.default_k_factor = 20
69
+ Elo.config.use_FIDE_settings = false
70
+
71
+ Elo.config.default_k_factor.should == 20
72
+ Elo.config.use_FIDE_settings.should == false
73
+ Elo::Player.new.k_factor.should == 20
74
+ end
75
+
76
+ it "pro_rating_boundry" do
77
+ Elo.config.pro_rating_boundry.should == 2400
78
+
79
+ Elo.config.pro_rating_boundry = 1337
80
+
81
+ Elo.config.pro_rating_boundry.should == 1337
82
+ Elo::Player.new(:rating => 1337).should be_pro_rating
83
+ end
84
+
6
85
  end
86
+
87
+ describe "according to FIDE" do
88
+
89
+ it "starter" do
90
+ player = Elo::Player.new
91
+ player.k_factor.should == 25
92
+ player.should be_starter
93
+ player.should_not be_pro
94
+ player.should_not be_pro_rating
95
+ end
96
+
97
+ it "normal" do
98
+ player = Elo::Player.new(:rating => 2399, :games_played => 30)
99
+ player.k_factor.should == 15
100
+ player.should_not be_starter
101
+ player.should_not be_pro
102
+ player.should_not be_pro_rating
103
+ end
104
+
105
+ it "pro rating" do
106
+ player = Elo::Player.new(:rating => 2400)
107
+ player.k_factor.should == 10
108
+ player.should be_starter
109
+ player.should be_pro_rating
110
+ player.should_not be_pro
111
+ end
112
+
113
+ it "historically a pro" do
114
+ player = Elo::Player.new(:rating => 2399, :pro => true)
115
+ player.k_factor.should == 10
116
+ player.should be_starter
117
+ player.should_not be_pro_rating
118
+ player.should be_pro
119
+ end
120
+ end
121
+
122
+ describe "examples for calculating rating correctly" do
123
+
124
+ # examples from http://chesselo.com/
125
+
126
+ before do
127
+ @a = Elo::Player.new(:rating => 2000, :k_factor => 10)
128
+ @b = Elo::Player.new(:rating => 1900, :k_factor => 10)
129
+ end
130
+
131
+ it "winning" do
132
+ @a.wins_from(@b)
133
+ @a.rating.should == 2003
134
+ end
135
+
136
+ it "losing" do
137
+ @a.loses_from(@b)
138
+ @a.rating.should == 1993
139
+ end
140
+
141
+ it "draw" do
142
+ @a.plays_draw(@b)
143
+ @a.rating.should == 1998
144
+ end
145
+
146
+ end
147
+
7
148
  end
metadata CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 2
8
+ - 3
9
9
  - alpha
10
- version: 0.0.2.alpha
10
+ version: 0.0.3.alpha
11
11
  platform: ruby
12
12
  authors:
13
13
  - Iain Hecker
@@ -61,7 +61,13 @@ files:
61
61
  - doc/fr_method_index.html
62
62
  - doc/index.html
63
63
  - doc/rdoc-style.css
64
+ - elo.gemspec
64
65
  - lib/elo.rb
66
+ - lib/elo/configuration.rb
67
+ - lib/elo/game.rb
68
+ - lib/elo/helper.rb
69
+ - lib/elo/player.rb
70
+ - lib/elo/rating.rb
65
71
  - spec/elo_spec.rb
66
72
  - spec/spec.opts
67
73
  - spec/spec_helper.rb