elo_rating 0.3.0 → 0.4.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -2
- data/README.md +63 -26
- data/Rakefile +4 -0
- data/lib/elo_rating/match.rb +5 -3
- data/lib/elo_rating/version.rb +1 -1
- data/lib/elo_rating.rb +1 -1
- data/spec/match_spec.rb +14 -0
- metadata +1 -3
- data/Gemfile.lock +0 -63
- data/Guardfile +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72c73870b99ccb9fa0eb8d5ada07c4104af74f33
|
|
4
|
+
data.tar.gz: f60fde89bad3ca68a36baf083dca5fa112da4a57
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 059f1564f4fbbc9064660f6ae6be567faed7e3c0787f3340a62718923b4b9f224e69cb6bc8f5281d0834c7559490aa16370f0d45c2119d347fb37bca8eb893a3
|
|
7
|
+
data.tar.gz: f2c8fea968c72d13c49f00e77fb7a3934c3afe2f3c05d714ace015f7fbf5c3c113447772ef67cff9c3f92330d53d3cac4ce4dd172d0404349991c7f7a794595b
|
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# elo_rating
|
|
2
2
|
|
|
3
|
-
`elo_rating` helps you calculate [Elo ratings](https://en.wikipedia.org/wiki/Elo_rating_system), a rating system used primary for Chess.
|
|
3
|
+
`elo_rating` helps you calculate [Elo ratings](https://en.wikipedia.org/wiki/Elo_rating_system), a rating system used primary for Chess, but can be used anywhere you want to determine an absolute ordering of things by doing many comparisons of a small number of things.
|
|
4
4
|
|
|
5
5
|
It can handle multiple players in one game and allows for custom K-factor functions.
|
|
6
6
|
|
|
@@ -20,9 +20,11 @@ gem 'elo_rating'
|
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
23
|
-
Say you have two players, both
|
|
24
|
-
determine both player's updated ratings:
|
|
23
|
+
Say you have two players playing against each other in a match, both with initial ratings of 2000.
|
|
25
24
|
|
|
25
|
+
The second player wins.
|
|
26
|
+
|
|
27
|
+
To determine both player's updated ratings:
|
|
26
28
|
|
|
27
29
|
```ruby
|
|
28
30
|
match = EloRating::Match.new
|
|
@@ -31,13 +33,19 @@ match.add_player(rating: 2000, winner: true)
|
|
|
31
33
|
match.updated_ratings # => [1988, 2012]
|
|
32
34
|
```
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
This tells us that the first player's rating should go down 12 points and the second player's should go up 12 points.
|
|
37
|
+
|
|
38
|
+
You can chain the same function calls to achieve the same result:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
EloRating::Match.new.add_player(rating: 2000).add_player(rating: 2000, winner: true).updated_ratings # => [1988, 2012]
|
|
42
|
+
```
|
|
35
43
|
|
|
36
44
|
### >2 players
|
|
37
45
|
|
|
38
|
-
Most Elo rating calculators only allow for matches of just 2 players, but the
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
Most Elo rating calculators only allow for matches of just 2 players, but the formula can be extended to games of any number of players.
|
|
47
|
+
|
|
48
|
+
We can do this by combining the rating adjustments for each pairing of players into one big adjustment.
|
|
41
49
|
|
|
42
50
|
Say you have three players, rated 1900, 2000, and 2000. They are playing a game
|
|
43
51
|
like [Monopoly](https://en.wikipedia.org/wiki/Monopoly_(game)) where there is
|
|
@@ -52,21 +60,26 @@ match.add_player(rating: 2000)
|
|
|
52
60
|
match.updated_ratings # => [1931, 1985, 1985]
|
|
53
61
|
```
|
|
54
62
|
|
|
63
|
+
This is calculated as if the first player beat both of the other players and the other two players tied.
|
|
64
|
+
|
|
55
65
|
### Ranked games
|
|
56
66
|
|
|
57
|
-
Some games like [Mario Kart](https://en.wikipedia.org/wiki/Mario_Kart) have
|
|
58
|
-
|
|
59
|
-
1900, 2000, and 2000
|
|
60
|
-
|
|
67
|
+
Some games like [Mario Kart](https://en.wikipedia.org/wiki/Mario_Kart) have multiple, ranked winners.
|
|
68
|
+
|
|
69
|
+
Let's say you have three players like before, rated 1900, 2000, and 2000, who came in first place, second place, and third place respectively.
|
|
70
|
+
|
|
71
|
+
Instead of indicating the winner, you can specify their places:
|
|
61
72
|
|
|
62
73
|
```ruby
|
|
63
74
|
match = EloRating::Match.new
|
|
64
|
-
match.add_player(rating: 1900,
|
|
65
|
-
match.add_player(rating: 2000,
|
|
66
|
-
match.add_player(rating: 2000,
|
|
75
|
+
match.add_player(rating: 1900, place: 1)
|
|
76
|
+
match.add_player(rating: 2000, place: 2)
|
|
77
|
+
match.add_player(rating: 2000, place: 3)
|
|
67
78
|
match.updated_ratings # => [1973, 1997, 1931]
|
|
68
79
|
```
|
|
69
80
|
|
|
81
|
+
This is calculated as if the first player beat both of the other players and the second player beat the third.
|
|
82
|
+
|
|
70
83
|
## Elo rating functions
|
|
71
84
|
|
|
72
85
|
The functions used in the above calculations are available for use directly:
|
|
@@ -83,10 +96,11 @@ The player rated 1900 has a 36% chance of winning.
|
|
|
83
96
|
|
|
84
97
|
### Rating adjustment
|
|
85
98
|
|
|
86
|
-
You can use the expected score and the results of an actual match to determine
|
|
87
|
-
|
|
99
|
+
You can use the expected score and the results of an actual match to determine how an Elo rating should change.
|
|
100
|
+
|
|
101
|
+
The `EloRating.rating_adjustment` function takes an expected score and an actual score and returns how much a rating should go up or down.
|
|
88
102
|
|
|
89
|
-
Let's say we have the expected rating from above of 0.36
|
|
103
|
+
Let's say we have the expected rating from above of 0.36 and the first player rated 1900 won the match, making their actual score 1.
|
|
90
104
|
|
|
91
105
|
We can use this to determine how much their rating should change:
|
|
92
106
|
|
|
@@ -94,7 +108,7 @@ We can use this to determine how much their rating should change:
|
|
|
94
108
|
EloRating.rating_adjustment(0.36, 1) # => 15.36
|
|
95
109
|
```
|
|
96
110
|
|
|
97
|
-
|
|
111
|
+
This means their rating should now be 1915.
|
|
98
112
|
|
|
99
113
|
## K-factor
|
|
100
114
|
|
|
@@ -106,18 +120,27 @@ It defaults to 24:
|
|
|
106
120
|
EloRating::k_factor # => 24
|
|
107
121
|
```
|
|
108
122
|
|
|
109
|
-
You can change this to any number. With a lower K-factor, ratings are less volatile and change slower:
|
|
123
|
+
You can change this to any number. With a lower K-factor, ratings are less volatile and change slower. Compare:
|
|
110
124
|
|
|
111
125
|
```ruby
|
|
112
126
|
EloRating::k_factor = 10
|
|
113
127
|
match = EloRating::Match.new
|
|
114
128
|
match.add_player(rating: 2000, winner: true)
|
|
115
129
|
match.add_player(rating: 2000)
|
|
116
|
-
match.updated_ratings # => [
|
|
130
|
+
match.updated_ratings # => [2005, 1995]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
to:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
EloRating::k_factor = 20
|
|
137
|
+
match = EloRating::Match.new
|
|
138
|
+
match.add_player(rating: 2000, winner: true)
|
|
139
|
+
match.add_player(rating: 2000)
|
|
140
|
+
match.updated_ratings # => [2010, 1990]
|
|
117
141
|
```
|
|
118
142
|
|
|
119
|
-
You can also pass a block to provide a custom function to calculate the K-factor
|
|
120
|
-
based on the player's rating:
|
|
143
|
+
You can also pass a block to provide a custom function to calculate the K-factor based on the player's rating:
|
|
121
144
|
|
|
122
145
|
```ruby
|
|
123
146
|
EloRating::set_k_factor do |rating|
|
|
@@ -132,8 +155,7 @@ EloRating::set_k_factor do |rating|
|
|
|
132
155
|
end
|
|
133
156
|
```
|
|
134
157
|
|
|
135
|
-
|
|
136
|
-
your custom K-factor function:
|
|
158
|
+
Then you can provide a rating to `EloRating.rating_adjustment` that will be used in your custom K-factor function:
|
|
137
159
|
|
|
138
160
|
```ruby
|
|
139
161
|
EloRating.rating_adjustment(0.75, 0) # => -24.0
|
|
@@ -141,13 +163,13 @@ EloRating.rating_adjustment(0.75, 0, rating: 2200) # => -18.0
|
|
|
141
163
|
EloRating.rating_adjustment(0.75, 0, rating: 2500) # => -12.0
|
|
142
164
|
```
|
|
143
165
|
|
|
144
|
-
You can also specify a K-factor for a single rating adjustment:
|
|
166
|
+
You can also just specify a K-factor directly for a single rating adjustment:
|
|
145
167
|
|
|
146
168
|
```ruby
|
|
147
169
|
EloRating.rating_adjustment(0.75, 0, k_factor: 24) # => -18.0
|
|
148
170
|
```
|
|
149
171
|
|
|
150
|
-
Note
|
|
172
|
+
*Note*: custom K-factor functions must not raise any exceptions when the rating is nil:
|
|
151
173
|
|
|
152
174
|
```ruby
|
|
153
175
|
EloRating::set_k_factor do |rating|
|
|
@@ -155,3 +177,18 @@ EloRating::set_k_factor do |rating|
|
|
|
155
177
|
end
|
|
156
178
|
# => ArgumentError: Error encountered in K-factor block when passed nil rating: undefined method `/' for nil:NilClass
|
|
157
179
|
```
|
|
180
|
+
|
|
181
|
+
## Thanks
|
|
182
|
+
|
|
183
|
+
Thanks to:
|
|
184
|
+
|
|
185
|
+
* Divergent Informatics for their [multiplayer Elo
|
|
186
|
+
calculator](http://elo.divergentinformatics.com/) used to verify calculations used in the development of this gem
|
|
187
|
+
* [Ian Hecker](https://github.com/iain) for the original [Elo](https://github.com/iain/elo).
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
Copyright © 2014 Maxwell Holder.
|
|
192
|
+
|
|
193
|
+
It is free software, and may be redistributed under the terms specified in the
|
|
194
|
+
LICENSE file.
|
data/Rakefile
CHANGED
data/lib/elo_rating/match.rb
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
# This class represents a single game between a number of players.
|
|
3
3
|
class EloRating::Match
|
|
4
4
|
|
|
5
|
+
# All the players of the match.
|
|
5
6
|
attr_reader :players
|
|
6
7
|
|
|
7
|
-
# Creates a new match
|
|
8
|
+
# Creates a new match with no players.
|
|
8
9
|
def initialize
|
|
9
10
|
@players = []
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
# Adds a player to the match
|
|
13
|
-
#
|
|
14
|
+
#
|
|
14
15
|
# ==== Attributes
|
|
15
16
|
# * +rating+: the Elo rating of the player
|
|
16
17
|
# * +winner+ (optional): boolean, whether this player is the winner of the match
|
|
@@ -86,7 +87,8 @@ class EloRating::Match
|
|
|
86
87
|
def rating_adjustment_against(opponent)
|
|
87
88
|
EloRating.rating_adjustment(
|
|
88
89
|
expected_score_against(opponent),
|
|
89
|
-
actual_score_against(opponent)
|
|
90
|
+
actual_score_against(opponent),
|
|
91
|
+
rating: rating
|
|
90
92
|
)
|
|
91
93
|
end
|
|
92
94
|
|
data/lib/elo_rating/version.rb
CHANGED
data/lib/elo_rating.rb
CHANGED
|
@@ -29,7 +29,7 @@ module EloRating
|
|
|
29
29
|
# end
|
|
30
30
|
# end
|
|
31
31
|
#
|
|
32
|
-
# Raises an ArgumentError if an exception is encountered when calling the provided block with nil as the argument
|
|
32
|
+
# Raises an +ArgumentError+ if an exception is encountered when calling the provided block with nil as the argument
|
|
33
33
|
def self.set_k_factor(&k_factor)
|
|
34
34
|
k_factor.call(nil)
|
|
35
35
|
@k_factor = k_factor
|
data/spec/match_spec.rb
CHANGED
|
@@ -28,6 +28,20 @@ describe EloRating::Match do
|
|
|
28
28
|
expect(match.updated_ratings).to eql [1931, 1997, 1973]
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
|
+
context 'custom K-factor function' do
|
|
32
|
+
it 'uses the custom K-factor function' do
|
|
33
|
+
EloRating::set_k_factor do |rating|
|
|
34
|
+
rating || 0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
match = EloRating::Match.new
|
|
38
|
+
match.add_player(rating: 2000)
|
|
39
|
+
match.add_player(rating: 2000, winner: true)
|
|
40
|
+
expect(match.updated_ratings).not_to eql [2000, 2000]
|
|
41
|
+
|
|
42
|
+
EloRating::k_factor = 24
|
|
43
|
+
end
|
|
44
|
+
end
|
|
31
45
|
context 'multiple winners specified' do
|
|
32
46
|
it 'raises an error' do
|
|
33
47
|
match = EloRating::Match.new
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: elo_rating
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Max Holder
|
|
@@ -90,8 +90,6 @@ extra_rdoc_files: []
|
|
|
90
90
|
files:
|
|
91
91
|
- ".gitignore"
|
|
92
92
|
- Gemfile
|
|
93
|
-
- Gemfile.lock
|
|
94
|
-
- Guardfile
|
|
95
93
|
- LICENSE
|
|
96
94
|
- README.md
|
|
97
95
|
- Rakefile
|
data/Gemfile.lock
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: .
|
|
3
|
-
specs:
|
|
4
|
-
elo_rating (0.3.0)
|
|
5
|
-
|
|
6
|
-
GEM
|
|
7
|
-
remote: https://rubygems.org/
|
|
8
|
-
specs:
|
|
9
|
-
celluloid (0.15.2)
|
|
10
|
-
timers (~> 1.1.0)
|
|
11
|
-
coderay (1.1.0)
|
|
12
|
-
diff-lcs (1.2.5)
|
|
13
|
-
ffi (1.9.3)
|
|
14
|
-
formatador (0.2.5)
|
|
15
|
-
guard (2.6.1)
|
|
16
|
-
formatador (>= 0.2.4)
|
|
17
|
-
listen (~> 2.7)
|
|
18
|
-
lumberjack (~> 1.0)
|
|
19
|
-
pry (>= 0.9.12)
|
|
20
|
-
thor (>= 0.18.1)
|
|
21
|
-
guard-rspec (4.2.10)
|
|
22
|
-
guard (~> 2.1)
|
|
23
|
-
rspec (>= 2.14, < 4.0)
|
|
24
|
-
listen (2.7.9)
|
|
25
|
-
celluloid (>= 0.15.2)
|
|
26
|
-
rb-fsevent (>= 0.9.3)
|
|
27
|
-
rb-inotify (>= 0.9)
|
|
28
|
-
lumberjack (1.0.7)
|
|
29
|
-
method_source (0.8.2)
|
|
30
|
-
pry (0.10.0)
|
|
31
|
-
coderay (~> 1.1.0)
|
|
32
|
-
method_source (~> 0.8.1)
|
|
33
|
-
slop (~> 3.4)
|
|
34
|
-
rake (10.3.1)
|
|
35
|
-
rb-fsevent (0.9.4)
|
|
36
|
-
rb-inotify (0.9.5)
|
|
37
|
-
ffi (>= 0.5.0)
|
|
38
|
-
rspec (3.0.0)
|
|
39
|
-
rspec-core (~> 3.0.0)
|
|
40
|
-
rspec-expectations (~> 3.0.0)
|
|
41
|
-
rspec-mocks (~> 3.0.0)
|
|
42
|
-
rspec-core (3.0.2)
|
|
43
|
-
rspec-support (~> 3.0.0)
|
|
44
|
-
rspec-expectations (3.0.2)
|
|
45
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
|
46
|
-
rspec-support (~> 3.0.0)
|
|
47
|
-
rspec-mocks (3.0.2)
|
|
48
|
-
rspec-support (~> 3.0.0)
|
|
49
|
-
rspec-support (3.0.2)
|
|
50
|
-
slop (3.5.0)
|
|
51
|
-
thor (0.19.1)
|
|
52
|
-
timers (1.1.0)
|
|
53
|
-
|
|
54
|
-
PLATFORMS
|
|
55
|
-
ruby
|
|
56
|
-
|
|
57
|
-
DEPENDENCIES
|
|
58
|
-
bundler (~> 1.6)
|
|
59
|
-
elo_rating!
|
|
60
|
-
guard
|
|
61
|
-
guard-rspec
|
|
62
|
-
rake
|
|
63
|
-
rspec
|
data/Guardfile
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# A sample Guardfile
|
|
2
|
-
# More info at https://github.com/guard/guard#readme
|
|
3
|
-
|
|
4
|
-
guard :rspec do
|
|
5
|
-
watch(%r{^spec/.+_spec\.rb$})
|
|
6
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
|
7
|
-
watch('spec/spec_helper.rb') { "spec" }
|
|
8
|
-
|
|
9
|
-
# Rails example
|
|
10
|
-
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
|
11
|
-
watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
|
12
|
-
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
|
13
|
-
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
|
14
|
-
watch('config/routes.rb') { "spec/routing" }
|
|
15
|
-
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
|
16
|
-
|
|
17
|
-
# Capybara features specs
|
|
18
|
-
watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
|
19
|
-
|
|
20
|
-
# Turnip features and steps
|
|
21
|
-
watch(%r{^spec/acceptance/(.+)\.feature$})
|
|
22
|
-
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
|
23
|
-
end
|
|
24
|
-
|