elo_rating 0.2.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +157 -0
- data/lib/elo_rating/match.rb +118 -0
- data/lib/elo_rating.rb +73 -0
- data/spec/elo_rating_spec.rb +84 -0
- data/spec/match_spec.rb +95 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9b56895aa2ec414462c5b2480ed2ebe93fa9f642
|
4
|
+
data.tar.gz: 50b2f10121650d74f5344d28eb440663d3b3a672
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7bd15401b85f7c9b8a0b5414ed45d44755afbb70b15b3308ca5cede8f41775f108b00763a3521ce6b2a9856673ea39e3e5b94b3ed41cd79eb6a493d3c19d8d1a
|
7
|
+
data.tar.gz: 63b7eb3a19af7eccca15b9c07c9c93ee0d77b01b142d25dbcb17fc5575d8ddecaa3460f94f65cd292df50aa7dd3ca1327153886f2f63e0c13e4334c8b8e5faa6
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Maxwell Holder
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
# elo_rating
|
2
|
+
|
3
|
+
`elo_rating` helps you calculate [Elo ratings](https://en.wikipedia.org/wiki/Elo_rating_system), a rating system used primary for Chess.
|
4
|
+
|
5
|
+
It can handle multiple players in one game and allows for custom K-factor functions.
|
6
|
+
|
7
|
+
- [API Documentation]()
|
8
|
+
|
9
|
+
## Getting started
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem install elo_rating
|
13
|
+
```
|
14
|
+
|
15
|
+
or add it to your Gemfile and run `bundle`:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'elo_rating'
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Say you have two players, both have ratings of 2000. The second player wins. To
|
24
|
+
determine both player's updated ratings:
|
25
|
+
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
match = EloRating::Match.new
|
29
|
+
match.add_player(rating: 2000)
|
30
|
+
match.add_player(rating: 2000, winner: true)
|
31
|
+
match.updated_ratings # => [1988, 2012]
|
32
|
+
```
|
33
|
+
|
34
|
+
The first player's rating goes down 12 points and the second player's goes up 12 points.
|
35
|
+
|
36
|
+
### >2 players
|
37
|
+
|
38
|
+
Most Elo rating calculators only allow for matches of just 2 players, but the
|
39
|
+
formula can be extended games with more players by calculating rating adjustments
|
40
|
+
for each player against each of their opponents.
|
41
|
+
|
42
|
+
Say you have three players, rated 1900, 2000, and 2000. They are playing a game
|
43
|
+
like [Monopoly](https://en.wikipedia.org/wiki/Monopoly_(game)) where there is
|
44
|
+
only one winner. The third player wins.
|
45
|
+
To determine their new scores:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
match = EloRating::Match.new
|
49
|
+
match.add_player(rating: 1900, winner: true)
|
50
|
+
match.add_player(rating: 2000)
|
51
|
+
match.add_player(rating: 2000)
|
52
|
+
match.updated_ratings # => [1931, 1985, 1985]
|
53
|
+
```
|
54
|
+
|
55
|
+
### Ranked games
|
56
|
+
|
57
|
+
Some games like [Mario Kart](https://en.wikipedia.org/wiki/Mario_Kart) have
|
58
|
+
multiple, ranked winners. Let's say you have three players like before, rated
|
59
|
+
1900, 2000, and 2000. Instead of indicating the winner, you can specify the
|
60
|
+
ranks:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
match = EloRating::Match.new
|
64
|
+
match.add_player(rating: 1900, rank: 1)
|
65
|
+
match.add_player(rating: 2000, rank: 2)
|
66
|
+
match.add_player(rating: 2000, rank: 3)
|
67
|
+
match.updated_ratings # => [1973, 1997, 1931]
|
68
|
+
```
|
69
|
+
|
70
|
+
## Elo rating functions
|
71
|
+
|
72
|
+
The functions used in the above calculations are available for use directly:
|
73
|
+
|
74
|
+
### Expected score
|
75
|
+
|
76
|
+
Say you have 2 players, rated 1900 and 2000.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
EloRating.expected_score(1900, 2000) # => 0.360
|
80
|
+
```
|
81
|
+
|
82
|
+
The player rated 1900 has a 36% chance of winning.
|
83
|
+
|
84
|
+
### Rating adjustment
|
85
|
+
|
86
|
+
You can use the expected score and the results of an actual match to determine
|
87
|
+
how an Elo rating should change.
|
88
|
+
|
89
|
+
Let's say we have the expected rating from above of 0.36. If the first player rated 1900 won the match, their actual score would be 1.
|
90
|
+
|
91
|
+
We can use this to determine how much their rating should change:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
EloRating.rating_adjustment(0.36, 1) # => 15.36
|
95
|
+
```
|
96
|
+
|
97
|
+
Their rating should go up about 15 points if they win.
|
98
|
+
|
99
|
+
## K-factor
|
100
|
+
|
101
|
+
The K-factor is used in calculating the rating adjustment and determines how much impact the most recent game has on a player's rating.
|
102
|
+
|
103
|
+
It defaults to 24:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
EloRating::k_factor # => 24
|
107
|
+
```
|
108
|
+
|
109
|
+
You can change this to any number. With a lower K-factor, ratings are less volatile and change slower:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
EloRating::k_factor = 10
|
113
|
+
match = EloRating::Match.new
|
114
|
+
match.add_player(rating: 2000, winner: true)
|
115
|
+
match.add_player(rating: 2000)
|
116
|
+
match.updated_ratings # => [1995, 2005]
|
117
|
+
```
|
118
|
+
|
119
|
+
You can also pass a block to provide a custom function to calculate the K-factor
|
120
|
+
based on the player's rating:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
EloRating::set_k_factor do |rating|
|
124
|
+
rating ||= 2000
|
125
|
+
if rating < 2100
|
126
|
+
32
|
127
|
+
elsif 2100 <= rating && rating <= 2400
|
128
|
+
24
|
129
|
+
else
|
130
|
+
16
|
131
|
+
end
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
You can provide a rating to `EloRating.rating_adjustment` that will be used in
|
136
|
+
your custom K-factor function:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
EloRating.rating_adjustment(0.75, 0) # => -24.0
|
140
|
+
EloRating.rating_adjustment(0.75, 0, rating: 2200) # => -18.0
|
141
|
+
EloRating.rating_adjustment(0.75, 0, rating: 2500) # => -12.0
|
142
|
+
```
|
143
|
+
|
144
|
+
You can also specify a K-factor for a single rating adjustment:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
EloRating.rating_adjustment(0.75, 0, k_factor: 24) # => -18.0
|
148
|
+
```
|
149
|
+
|
150
|
+
Note: custom K-factor functions must not raise any exceptions when the rating is nil:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
EloRating::set_k_factor do |rating|
|
154
|
+
rating / 100
|
155
|
+
end
|
156
|
+
# => ArgumentError: Error encountered in K-factor block when passed nil rating: undefined method `/' for nil:NilClass
|
157
|
+
```
|
@@ -0,0 +1,118 @@
|
|
1
|
+
##
|
2
|
+
# This class represents a single game between a number of players.
|
3
|
+
class EloRating::Match
|
4
|
+
|
5
|
+
attr_reader :players
|
6
|
+
|
7
|
+
# Creates a new match
|
8
|
+
def initialize
|
9
|
+
@players = []
|
10
|
+
end
|
11
|
+
|
12
|
+
# Adds a player to the match
|
13
|
+
#
|
14
|
+
# ==== Attributes
|
15
|
+
# * +rating+: the Elo rating of the player
|
16
|
+
# * +winner+ (optional): boolean, whether this player is the winner of the match
|
17
|
+
# * +place+ (optional): a number representing the rank of the player within the match; the lower the number, the higher they placed
|
18
|
+
#
|
19
|
+
# Raises an +ArgumentError+ if the rating or place is not numeric, or if
|
20
|
+
# both winner and place is specified.
|
21
|
+
def add_player(player_attributes)
|
22
|
+
players << Player.new(player_attributes.merge(match: self))
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
# Calculates the updated ratings for each of the players in the match.
|
27
|
+
#
|
28
|
+
# Raises an +ArgumentError+ if more than one player is marked as the winner or
|
29
|
+
# if some but not all players have +place+ specified.
|
30
|
+
def updated_ratings
|
31
|
+
validate_players!
|
32
|
+
players.map(&:updated_rating)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def validate_players!
|
38
|
+
raise ArgumentError, 'Only one player can be the winner' if multiple_winners?
|
39
|
+
raise ArgumentError, 'All players must have places if any do' if inconsistent_places?
|
40
|
+
end
|
41
|
+
|
42
|
+
def multiple_winners?
|
43
|
+
players.select { |player| player.winner? }.size > 1
|
44
|
+
end
|
45
|
+
|
46
|
+
def inconsistent_places?
|
47
|
+
players.select { |player| player.place }.any? &&
|
48
|
+
players.select { |player| !player.place }.any?
|
49
|
+
end
|
50
|
+
|
51
|
+
class Player
|
52
|
+
# :nodoc:
|
53
|
+
attr_reader :rating, :place, :match
|
54
|
+
def initialize(match:, rating:, place: nil, winner: nil)
|
55
|
+
validate_attributes!(rating: rating, place: place, winner: winner)
|
56
|
+
@match = match
|
57
|
+
@rating = rating
|
58
|
+
@place = place
|
59
|
+
@winner = winner
|
60
|
+
end
|
61
|
+
|
62
|
+
def winner?
|
63
|
+
@winner
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_attributes!(rating:, place:, winner:)
|
67
|
+
raise ArgumentError, 'Rating must be numeric' unless rating.is_a? Numeric
|
68
|
+
raise ArgumentError, 'Winner and place cannot both be specified' if place && winner
|
69
|
+
raise ArgumentError, 'Place must be numeric' unless place.nil? || place.is_a?(Numeric)
|
70
|
+
end
|
71
|
+
|
72
|
+
def opponents
|
73
|
+
match.players - [self]
|
74
|
+
end
|
75
|
+
|
76
|
+
def updated_rating
|
77
|
+
(rating + total_rating_adjustments).round
|
78
|
+
end
|
79
|
+
|
80
|
+
def total_rating_adjustments
|
81
|
+
opponents.map do |opponent|
|
82
|
+
rating_adjustment_against(opponent)
|
83
|
+
end.reduce(0, :+)
|
84
|
+
end
|
85
|
+
|
86
|
+
def rating_adjustment_against(opponent)
|
87
|
+
EloRating.rating_adjustment(
|
88
|
+
expected_score_against(opponent),
|
89
|
+
actual_score_against(opponent)
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
def expected_score_against(opponent)
|
94
|
+
EloRating.expected_score(rating, opponent.rating)
|
95
|
+
end
|
96
|
+
|
97
|
+
def actual_score_against(opponent)
|
98
|
+
if won_against?(opponent)
|
99
|
+
1
|
100
|
+
elsif opponent.won_against?(self)
|
101
|
+
0
|
102
|
+
else # draw
|
103
|
+
0.5
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def won_against?(opponent)
|
108
|
+
winner? || placed_ahead_of?(opponent)
|
109
|
+
end
|
110
|
+
|
111
|
+
def placed_ahead_of?(opponent)
|
112
|
+
if place && opponent.place
|
113
|
+
place < opponent.place
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
data/lib/elo_rating.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# :main: README.md
|
2
|
+
|
3
|
+
##
|
4
|
+
# EloRating helps you calculate Elo ratings.
|
5
|
+
#
|
6
|
+
# See the README for an introduction.
|
7
|
+
module EloRating
|
8
|
+
|
9
|
+
# Default K-factor.
|
10
|
+
@k_factor = Proc.new do |rating|
|
11
|
+
24
|
12
|
+
end
|
13
|
+
|
14
|
+
# Calls the K-factor function with the provided rating.
|
15
|
+
def self.k_factor(rating = nil)
|
16
|
+
@k_factor.call(rating)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Sets the K-factor by providing a block that optionally takes a +rating+
|
20
|
+
# argument:
|
21
|
+
#
|
22
|
+
# EloRating::set_k_factor do |rating|
|
23
|
+
# if rating && rating > 2500
|
24
|
+
# 24
|
25
|
+
# else
|
26
|
+
# 16
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Raises an ArgumentError if an exception is encountered when calling the provided block with nil as the argument
|
31
|
+
def self.set_k_factor(&k_factor)
|
32
|
+
k_factor.call(nil)
|
33
|
+
@k_factor = k_factor
|
34
|
+
rescue => e
|
35
|
+
raise ArgumentError, "Error encountered in K-factor block when passed nil rating: #{e}"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets the K-factor to a number.
|
39
|
+
def self.k_factor=(k_factor)
|
40
|
+
@k_factor = Proc.new do
|
41
|
+
k_factor
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the expected score of a player given their rating (+player_rating+)
|
46
|
+
# and their opponent's rating (+opponent_rating+).
|
47
|
+
#
|
48
|
+
# Returns a float between 0 and 1 where 0.999 represents high certainty of the
|
49
|
+
# first player winning.
|
50
|
+
def self.expected_score(player_rating, opponent_rating)
|
51
|
+
1.0/(1 + (10 ** ((opponent_rating - player_rating)/400.0)))
|
52
|
+
end
|
53
|
+
|
54
|
+
# Calculates the amount a player's rating should change.
|
55
|
+
#
|
56
|
+
# ==== Arguments
|
57
|
+
# * +expected_score+: a float between 0 and 1, representing the probability of
|
58
|
+
# the player winning
|
59
|
+
# * +actual_score+: 0, 0.5, or 1, whether the outcome was a loss, draw, or win
|
60
|
+
# (respectively)
|
61
|
+
# * +rating+ (optional): the rating of the player, used by the K-factor function
|
62
|
+
# * +k_factor+ (optional): the K-factor to use for this calculation to be used
|
63
|
+
# instead of the normal K-factor or K-factor function
|
64
|
+
#
|
65
|
+
# Returns a positive or negative float representing the amount the player's
|
66
|
+
# rating should change.
|
67
|
+
def self.rating_adjustment(expected_score, actual_score, rating: nil, k_factor: nil)
|
68
|
+
k_factor ||= k_factor(rating)
|
69
|
+
k_factor * (actual_score - expected_score)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
require 'elo_rating/match'
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require_relative '../lib/elo_rating.rb'
|
2
|
+
|
3
|
+
describe EloRating do
|
4
|
+
after(:each) do
|
5
|
+
EloRating::k_factor = 24
|
6
|
+
end
|
7
|
+
describe '::k_factor' do
|
8
|
+
it 'defaults to 24' do
|
9
|
+
expect(EloRating::k_factor).to eql(24)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
describe '::k_factor=' do
|
13
|
+
it 'sets the K-factor to an integer' do
|
14
|
+
EloRating::k_factor = 10
|
15
|
+
expect(EloRating::k_factor).to eql(10)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
describe '::set_k_factor' do
|
19
|
+
it 'takes a block to determine the K-factor based on a player\'s rating' do
|
20
|
+
EloRating::set_k_factor do |rating|
|
21
|
+
if rating && rating > 1000
|
22
|
+
15
|
23
|
+
else
|
24
|
+
24
|
25
|
+
end
|
26
|
+
end
|
27
|
+
expect(EloRating::k_factor(1001)).to eql 15
|
28
|
+
end
|
29
|
+
context 'given a block that doesn\'t handle nil ratings' do
|
30
|
+
it 'raises an error' do
|
31
|
+
expect do
|
32
|
+
EloRating::set_k_factor do |rating|
|
33
|
+
rating * 10
|
34
|
+
end
|
35
|
+
end.to raise_error ArgumentError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
describe '.expected_score' do
|
40
|
+
it 'returns the odds of a player winning given their rating and their opponent\'s rating' do
|
41
|
+
expect(EloRating.expected_score(1200, 1000)).to be_within(0.0001).of(0.7597)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
describe '.rating_adjustment' do
|
45
|
+
it 'returns the amount a rating should change given an expected score and an actual score' do
|
46
|
+
expect(EloRating.rating_adjustment(0.75, 0)).to be_within(0.0001).of(-18.0)
|
47
|
+
end
|
48
|
+
it 'uses the K-factor' do
|
49
|
+
expect(EloRating).to receive(:k_factor).and_return(24)
|
50
|
+
|
51
|
+
EloRating.rating_adjustment(0.75, 0)
|
52
|
+
end
|
53
|
+
context 'custom numeric k-factor' do
|
54
|
+
it 'uses the custom k-factor' do
|
55
|
+
EloRating::k_factor = 10
|
56
|
+
|
57
|
+
expect(EloRating.rating_adjustment(0.75, 0)).to be_within(0.0001).of(-7.5)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
context 'custom k-factor function' do
|
61
|
+
it 'calls the function with the provided rating to determine the k-factor' do
|
62
|
+
EloRating::set_k_factor do |rating|
|
63
|
+
rating ||= 2000
|
64
|
+
if rating < 2100
|
65
|
+
32
|
66
|
+
elsif 2100 <= rating && rating <= 2400
|
67
|
+
24
|
68
|
+
else
|
69
|
+
16
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
expect(EloRating.rating_adjustment(0.75, 0)).to be_within(0.0001).of(-24.0)
|
74
|
+
expect(EloRating.rating_adjustment(0.75, 0, rating: 2200)).to be_within(0.0001).of(-18.0)
|
75
|
+
expect(EloRating.rating_adjustment(0.75, 0, rating: 2500)).to be_within(0.0001).of(-12.0)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
context 'provided with a nonce numeric k-factor' do
|
79
|
+
it 'uses the provided k-factor' do
|
80
|
+
expect(EloRating.rating_adjustment(0.75, 0, k_factor: 24)).to be_within(0.0001).of(-18.0)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/spec/match_spec.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative '../lib/elo_rating.rb'
|
2
|
+
|
3
|
+
describe EloRating::Match do
|
4
|
+
describe '#updated_ratings' do
|
5
|
+
context 'simple match with two players and one winner' do
|
6
|
+
it 'returns the updated ratings of all the players' do
|
7
|
+
match = EloRating::Match.new
|
8
|
+
match.add_player(rating: 2000)
|
9
|
+
match.add_player(rating: 2000, winner: true)
|
10
|
+
expect(match.updated_ratings).to eql [1988, 2012]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
context 'match with 3 players and one winner' do
|
14
|
+
it 'returns the updated ratings of all the players' do
|
15
|
+
match = EloRating::Match.new
|
16
|
+
match.add_player(rating: 1900, winner: true)
|
17
|
+
match.add_player(rating: 2000)
|
18
|
+
match.add_player(rating: 2000)
|
19
|
+
expect(match.updated_ratings).to eql [1931, 1985, 1985]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
context 'ranked game with 3 players' do
|
23
|
+
it 'returns the updated ratings of all the players' do
|
24
|
+
match = EloRating::Match.new
|
25
|
+
match.add_player(rating: 1900, place: 1)
|
26
|
+
match.add_player(rating: 2000, place: 2)
|
27
|
+
match.add_player(rating: 2000, place: 3)
|
28
|
+
expect(match.updated_ratings).to eql [1931, 1997, 1973]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
context 'multiple winners specified' do
|
32
|
+
it 'raises an error' do
|
33
|
+
match = EloRating::Match.new
|
34
|
+
match.add_player(rating: 1000, winner: true)
|
35
|
+
match.add_player(rating: 1000, winner: true)
|
36
|
+
expect { match.updated_ratings }.to raise_error ArgumentError
|
37
|
+
end
|
38
|
+
end
|
39
|
+
context 'place specified for one player but not all' do
|
40
|
+
it 'raises an error' do
|
41
|
+
match = EloRating::Match.new
|
42
|
+
match.add_player(rating: 1000)
|
43
|
+
match.add_player(rating: 1000, place: 2)
|
44
|
+
expect { match.updated_ratings }.to raise_error ArgumentError
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#add_player' do
|
50
|
+
context 'adding a player with a rating' do
|
51
|
+
it 'creates a new player with the specified rating' do
|
52
|
+
match = EloRating::Match.new
|
53
|
+
expect(EloRating::Match::Player).to receive(:new).with(rating: 2000, match: match)
|
54
|
+
|
55
|
+
match.add_player(rating: 2000)
|
56
|
+
end
|
57
|
+
it 'appends the new player to the match\'s player' do
|
58
|
+
match = EloRating::Match.new
|
59
|
+
match.add_player(rating: 2000)
|
60
|
+
|
61
|
+
expect(match.players.size).to eql 1
|
62
|
+
end
|
63
|
+
it 'returns the match itself so multiple calls can be chained' do
|
64
|
+
match = EloRating::Match.new
|
65
|
+
match.add_player(rating: 1000).add_player(rating: 2000)
|
66
|
+
|
67
|
+
expect(match.players.size). to eql 2
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'adding a player with a non-numeric rating' do
|
72
|
+
it 'raises an error' do
|
73
|
+
match = EloRating::Match.new
|
74
|
+
|
75
|
+
expect { match.add_player(rating: :foo) }.to raise_error(ArgumentError)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'adding a player with a non-numeric place' do
|
80
|
+
it 'raises an error' do
|
81
|
+
match = EloRating::Match.new
|
82
|
+
|
83
|
+
expect { match.add_player(rating: 1000, place: :foo) }.to raise_error(ArgumentError)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'adding a player with both winner and place specified' do
|
88
|
+
it 'raises an error' do
|
89
|
+
match = EloRating::Match.new
|
90
|
+
|
91
|
+
expect { match.add_player(rating: 1000, place: 2, winner: true) }.to raise_error ArgumentError
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: elo_rating
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Max Holder
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-28 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Tiny library for calculating Elo ratings. Handles multiplayer matches
|
14
|
+
and allows for custom k-factor functions.
|
15
|
+
email: mxhold@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- lib/elo_rating.rb
|
23
|
+
- lib/elo_rating/match.rb
|
24
|
+
- spec/elo_rating_spec.rb
|
25
|
+
- spec/match_spec.rb
|
26
|
+
homepage: http://github.com/mxhold/elo_rating
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.2.0
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Tiny library for calculating Elo ratings
|
50
|
+
test_files:
|
51
|
+
- spec/elo_rating_spec.rb
|
52
|
+
- spec/match_spec.rb
|