elo2 0.1.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/lib/elo.rb +27 -0
- data/lib/elo/configuration.rb +83 -0
- data/lib/elo/game.rb +102 -0
- data/lib/elo/helper.rb +29 -0
- data/lib/elo/model.rb +10 -0
- data/lib/elo/player.rb +114 -0
- data/lib/elo/rating.rb +59 -0
- data/lib/elo/version.rb +3 -0
- data/lib/elo2.rb +1 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 249ef1b338e702a1f19cf4ee6e4472c5ca3c22d9
|
4
|
+
data.tar.gz: 121bea75bb56e1cac834ad81559ecdfa60160c9c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7a0c882c33002e9d5e52900587ad771934e4d8726f7e807c4ca931ee257fd889e7af23f8d8fd8f2b061d307a75134fc941c29d9ea59b4532574369c9b5ed5f9a
|
7
|
+
data.tar.gz: 929fde0b1092d16eb469067fe0dbc7ee3278199a3b1e807b88d8bb78c6c10052d57c3013d0b7963cbaf7aa698df4af01cb34c6bddfcf85cf0113b035f4d972ba
|
data/lib/elo.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'elo/helper'
|
2
|
+
require 'elo/configuration'
|
3
|
+
require 'elo/game'
|
4
|
+
require 'elo/player'
|
5
|
+
require 'elo/rating'
|
6
|
+
require 'elo/version'
|
7
|
+
|
8
|
+
# See README.rdoc for general information about Elo.
|
9
|
+
module Elo
|
10
|
+
|
11
|
+
# Accessor to the configuration object, which,
|
12
|
+
# should be instantiated only once (and automatically).
|
13
|
+
def self.config
|
14
|
+
@config ||= Configuration.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Configure Elo in a block style.
|
18
|
+
# See Elo::Configuration for more details.
|
19
|
+
#
|
20
|
+
# Elo.configure do |config|
|
21
|
+
# config.attribute = :value
|
22
|
+
# end
|
23
|
+
def self.configure(&block)
|
24
|
+
yield(config)
|
25
|
+
end
|
26
|
+
|
27
|
+
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
|
data/lib/elo/game.rb
ADDED
@@ -0,0 +1,102 @@
|
|
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
|
+
calculate
|
27
|
+
end
|
28
|
+
alias result= process_result
|
29
|
+
|
30
|
+
def calculate
|
31
|
+
if result
|
32
|
+
one.send(:played, self)
|
33
|
+
two.send(:played, self)
|
34
|
+
save
|
35
|
+
end
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# Player +:one+ has won!
|
40
|
+
# This is a shortcut method for setting the score to 1
|
41
|
+
def win
|
42
|
+
process_result 1.0
|
43
|
+
end
|
44
|
+
|
45
|
+
# Player +:one+ has lost!
|
46
|
+
# This is a shortcut method for setting the score to 0
|
47
|
+
def lose
|
48
|
+
process_result 0.0
|
49
|
+
end
|
50
|
+
|
51
|
+
# It was a draw.
|
52
|
+
# This is a shortcut method for setting the score to 0.5
|
53
|
+
def draw
|
54
|
+
process_result 0.5
|
55
|
+
end
|
56
|
+
|
57
|
+
# You can override this method if you store each game
|
58
|
+
# in a database or something like that.
|
59
|
+
# This method will be called when a result is known.
|
60
|
+
def save
|
61
|
+
end
|
62
|
+
|
63
|
+
# Set the winner. Provide it with a Elo::Player.
|
64
|
+
def winner=(player)
|
65
|
+
process_result(player == one ? 1.0 : 0.0)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Set the loser. Provide it with a Elo::Player.
|
69
|
+
def loser=(player)
|
70
|
+
process_result(player == one ? 0.0 : 1.0)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Access the Elo::Rating objects for both players.
|
74
|
+
def ratings
|
75
|
+
@ratings ||= { one => rating_one, two => rating_two }
|
76
|
+
end
|
77
|
+
|
78
|
+
def inspect
|
79
|
+
"game"
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# Create an Elo::Rating object for player one
|
85
|
+
def rating_one
|
86
|
+
Rating.new(:result => result,
|
87
|
+
:old_rating => one.rating,
|
88
|
+
:other_rating => two.rating,
|
89
|
+
:k_factor => one.k_factor)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Create an Elo::Rating object for player two
|
93
|
+
def rating_two
|
94
|
+
Rating.new(:result => (1.0 - result),
|
95
|
+
:old_rating => two.rating,
|
96
|
+
:other_rating => one.rating,
|
97
|
+
:k_factor => two.k_factor)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
data/lib/elo/helper.rb
ADDED
@@ -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
|
data/lib/elo/model.rb
ADDED
data/lib/elo/player.rb
ADDED
@@ -0,0 +1,114 @@
|
|
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, options = {})
|
72
|
+
Game.new(options.merge(:one => self, :two => other_player)).calculate
|
73
|
+
end
|
74
|
+
|
75
|
+
# Start a game with another player and set the score
|
76
|
+
# immediately.
|
77
|
+
def wins_from(other_player, options = {})
|
78
|
+
versus(other_player, options).win
|
79
|
+
end
|
80
|
+
|
81
|
+
# Start a game with another player and set the score
|
82
|
+
# immediately.
|
83
|
+
def plays_draw(other_player, options = {})
|
84
|
+
versus(other_player, options).draw
|
85
|
+
end
|
86
|
+
|
87
|
+
# Start a game with another player and set the score
|
88
|
+
# immediately.
|
89
|
+
def loses_from(other_player, options = {})
|
90
|
+
versus(other_player, options).lose
|
91
|
+
end
|
92
|
+
|
93
|
+
def inspect
|
94
|
+
"player"
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# A Game tells the players informed to update their
|
100
|
+
# scores, after it knows the result (so it can calculate the rating).
|
101
|
+
#
|
102
|
+
# This method is private, because it is called automatically.
|
103
|
+
# Therefore it is not part of the public API of Elo.
|
104
|
+
def played(game)
|
105
|
+
@games_played = games_played + 1
|
106
|
+
games << game
|
107
|
+
@rating = game.ratings[self].new_rating
|
108
|
+
@pro = true if pro_rating?
|
109
|
+
save
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
data/lib/elo/rating.rb
ADDED
@@ -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 + change.round
|
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
|
data/lib/elo/version.rb
ADDED
data/lib/elo2.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'elo'
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: elo2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Hawthorn
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
description: The Elo rating system is a method for calculating the relative skill
|
28
|
+
levels of players in two-player games such as cess and Go.
|
29
|
+
email:
|
30
|
+
- john.hawthorn@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/elo.rb
|
36
|
+
- lib/elo/configuration.rb
|
37
|
+
- lib/elo/game.rb
|
38
|
+
- lib/elo/helper.rb
|
39
|
+
- lib/elo/model.rb
|
40
|
+
- lib/elo/player.rb
|
41
|
+
- lib/elo/rating.rb
|
42
|
+
- lib/elo/version.rb
|
43
|
+
- lib/elo2.rb
|
44
|
+
homepage: http://github.com/iain/elo
|
45
|
+
licenses: []
|
46
|
+
metadata: {}
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 2.4.5
|
64
|
+
signing_key:
|
65
|
+
specification_version: 4
|
66
|
+
summary: The Elo rating system is a method for calculating the relative skill levels
|
67
|
+
of players in two-player games such as cess and Go.
|
68
|
+
test_files: []
|