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.
@@ -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
@@ -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
@@ -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
@@ -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,10 @@
1
+ module Elo
2
+ module Model
3
+ # Poor man's activemodel
4
+ def initialize(attributes = {})
5
+ attributes.each do |key, value|
6
+ public_send("#{key}=", value)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Elo
2
+ VERSION = "0.1.0"
3
+ end
@@ -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: []