elo 0.0.2.alpha → 0.0.3.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -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