elo 0.0.2.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
2
+ <html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
3
+ <head>
4
+ <title>elo</title>
5
+ <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'>
6
+ </head>
7
+ <frameset border='1' bordercolor='gray' cols='20%, *' frameborder='1'>
8
+ <frameset rows='15%, 35%, 50%'>
9
+ <frame name='Files' src='fr_file_index.html' title='Files'></frame>
10
+ <frame name='Classes' src='fr_class_index.html'></frame>
11
+ <frame name='Methods' src='fr_method_index.html'></frame>
12
+ </frameset>
13
+ <frame name='docwin' src='files/README_rdoc.html'></frame>
14
+ </frameset>
15
+ </html>
@@ -0,0 +1,328 @@
1
+ html, body {
2
+ height: 100%; }
3
+
4
+ body {
5
+ font-family: Lucida Grande, Verdana, Arial, Helvetica, sans-serif;
6
+ font-size: 90%;
7
+ margin: 0;
8
+ padding: 0;
9
+ background: white;
10
+ color: black; }
11
+
12
+ #wrapper {
13
+ min-height: 100%;
14
+ height: auto !important;
15
+ height: 100%;
16
+ margin: 0 auto -43px; }
17
+
18
+ #footer-push {
19
+ height: 43px; }
20
+
21
+ div.header, #footer {
22
+ background: #eee; }
23
+
24
+ #footer {
25
+ border-top: 1px solid silver;
26
+ margin-top: 12px;
27
+ padding: 0 2em;
28
+ line-height: 30px;
29
+ text-align: center;
30
+ font-variant: small-caps;
31
+ font-size: 95%; }
32
+
33
+ .clearing:after {
34
+ content: ".";
35
+ visibility: hidden;
36
+ height: 0;
37
+ display: block;
38
+ clear: both; }
39
+ * html .clearing {
40
+ height: 1px; }
41
+ .clearing *:first-child + html {
42
+ overflow: hidden; }
43
+
44
+ h1, h2, h3, h4, h5, h6 {
45
+ margin: 0;
46
+ font-weight: normal; }
47
+
48
+ a {
49
+ color: #0b3e71; }
50
+ a:hover {
51
+ background: #336699;
52
+ text-decoration: none;
53
+ color: #eef; }
54
+
55
+ #diagram img {
56
+ border: 0; }
57
+
58
+ #description a, .method .description a, .header a {
59
+ color: #336699; }
60
+ #description a:hover, .method .description a:hover, .header a:hover {
61
+ color: #eee; }
62
+ #description h1 a, #description h2 a, #description h3 a, #description h4 a, #description h5 a, #description h6 a, .method .description h1 a, .method .description h2 a, .method .description h3 a, .method .description h4 a, .method .description h5 a, .method .description h6 a, .header h1 a, .header h2 a, .header h3 a, .header h4 a, .header h5 a, .header h6 a {
63
+ color: #0b3e71; }
64
+
65
+ ol {
66
+ margin: 0;
67
+ padding: 0;
68
+ list-style: none; }
69
+ ol li {
70
+ margin-left: 0;
71
+ white-space: nowrap; }
72
+ ol li.other {
73
+ display: none; }
74
+
75
+ ol.expanded li.other {
76
+ display: list-item; }
77
+
78
+ table {
79
+ margin-bottom: 1em;
80
+ font-size: 1em;
81
+ border-collapse: collapse; }
82
+ table td, table th {
83
+ padding: .4em .8em; }
84
+ table thead {
85
+ background-color: #e8e8e8; }
86
+ table thead th {
87
+ font-variant: small-caps;
88
+ color: #666666; }
89
+ table tr {
90
+ border-bottom: 1px solid silver; }
91
+
92
+ #index a.show, div.header a.show {
93
+ text-decoration: underline;
94
+ font-style: italic;
95
+ color: #666666; }
96
+ #index a.show:after, div.header a.show:after {
97
+ content: " ..."; }
98
+ #index a.show:hover, div.header a.show:hover {
99
+ color: black;
100
+ background: #ffe; }
101
+
102
+ #index {
103
+ font: 85%/1.2 Arial, Helvetica, sans-serif; }
104
+ #index a {
105
+ text-decoration: none; }
106
+ #index h1 {
107
+ padding: .2em .5em .1em;
108
+ background: #ccc;
109
+ font: small-caps 1.2em Georgia, serif;
110
+ color: #333;
111
+ border-bottom: 1px solid gray; }
112
+ #index form {
113
+ margin: 0;
114
+ padding: 0; }
115
+ #index form input {
116
+ margin: .4em 3px 0 .4em;
117
+ width: 80%; }
118
+ #index form #search.untouched {
119
+ color: #777777; }
120
+ #index form .clear_button {
121
+ -moz-border-radius: 7px;
122
+ -webkit-border-radius: 7px;
123
+ background: #AAA;
124
+ color: white;
125
+ padding: 0 5px 1px 5px;
126
+ cursor: pointer; }
127
+ #index ol {
128
+ padding: .4em .5em; }
129
+ #index ol li {
130
+ white-space: nowrap; }
131
+ #index #index-entries li a {
132
+ padding: 1px 2px; }
133
+ #index #index-entries.classes {
134
+ font-size: 1.1em; }
135
+ #index #index-entries.classes ol {
136
+ padding: 0; }
137
+ #index #index-entries.classes span.nodoc {
138
+ display: none; }
139
+ #index #index-entries.classes span.nodoc, #index #index-entries.classes a {
140
+ font-weight: bold; }
141
+ #index #index-entries.classes .parent {
142
+ font-weight: normal; }
143
+ #index #index-entries.methods li, #index #search-results.methods li {
144
+ margin-bottom: 0.2em; }
145
+ #index #index-entries.methods li a .method_name, #index #search-results.methods li a .method_name {
146
+ margin-right: 0.25em; }
147
+ #index #index-entries.methods li a .module_name, #index #search-results.methods li a .module_name {
148
+ color: #666666; }
149
+ #index #index-entries.methods li a:hover .module_name, #index #search-results.methods li a:hover .module_name {
150
+ color: #ddd; }
151
+
152
+ #attribute-list .context-item-name {
153
+ font-weight: bold; }
154
+
155
+ div.header {
156
+ font-size: 80%;
157
+ padding: .5em 2%;
158
+ font-family: Arial, Helvetica, sans-serif;
159
+ border-bottom: 1px solid silver; }
160
+ div.header .name {
161
+ font-size: 1.6em;
162
+ font-family: Georgia, serif; }
163
+ div.header .name .type {
164
+ color: #666666;
165
+ font-size: 80%;
166
+ font-variant: small-caps; }
167
+ div.header h1.name {
168
+ font-size: 2.2em; }
169
+ div.header .paths, div.header .last-update, div.header .parent {
170
+ color: #666666; }
171
+ div.header .last-update .datetime {
172
+ color: #484848; }
173
+ div.header .parent {
174
+ margin-top: .3em; }
175
+ div.header .parent strong {
176
+ font-weight: normal;
177
+ color: #484848; }
178
+
179
+ #content {
180
+ padding: 12px 2%; }
181
+ div.class #content {
182
+ position: relative;
183
+ width: 72%; }
184
+ #content pre, #content .method .synopsis {
185
+ font: 14px Monaco, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace; }
186
+ #content pre {
187
+ color: black;
188
+ background: #eee;
189
+ border: 1px solid silver;
190
+ padding: .5em .8em;
191
+ overflow: auto; }
192
+ #content p code, #content p tt, #content li code, #content li tt, #content dl code, #content dl tt {
193
+ font: 14px Monaco, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
194
+ background: #ffffe3;
195
+ padding: 2px 3px;
196
+ line-height: 1.4; }
197
+ #content h1 code, #content h1 tt, #content h2 code, #content h2 tt, #content h3 code, #content h3 tt, #content h4 code, #content h4 tt, #content h5 code, #content h5 tt, #content h6 code, #content h6 tt {
198
+ font-size: 1.1em; }
199
+ #content #text {
200
+ position: relative; }
201
+ #content #description p {
202
+ margin-top: .5em; }
203
+ #content #description h1, #content #description h2, #content #description h3, #content #description h4, #content #description h5, #content #description h6 {
204
+ font-family: Georgia, serif; }
205
+ #content #description h1 {
206
+ font-size: 2.2em;
207
+ margin-bottom: .2em;
208
+ border-bottom: 3px double #d8d8d8;
209
+ padding-bottom: .1em; }
210
+ #content #description h2 {
211
+ font-size: 1.8em;
212
+ color: #0e3062;
213
+ margin: .8em 0 .3em 0; }
214
+ #content #description h3 {
215
+ font-size: 1.6em;
216
+ margin: .8em 0 .3em 0;
217
+ color: #666666; }
218
+ #content #description h4 {
219
+ font-size: 1.4em;
220
+ margin: .8em 0 .3em 0; }
221
+ #content #description h5 {
222
+ font-size: 1.2em;
223
+ margin: .8em 0 .3em 0;
224
+ color: #0e3062; }
225
+ #content #description h6 {
226
+ font-size: 1.0em;
227
+ margin: .8em 0 .3em 0;
228
+ color: #666666; }
229
+ #content #description ul, #content #description ol, #content .method .description ul, #content .method .description ol {
230
+ margin: .8em 0;
231
+ padding-left: 1.5em; }
232
+ #content #description ol, #content .method .description ol {
233
+ list-style: decimal; }
234
+ #content #description ol li, #content .method .description ol li {
235
+ white-space: normal; }
236
+
237
+ #method-list {
238
+ position: absolute;
239
+ top: 0px;
240
+ right: -33%;
241
+ width: 28%;
242
+ background: #eee;
243
+ border: 1px solid silver;
244
+ padding: .4em 1%;
245
+ overflow: hidden; }
246
+ #method-list h2 {
247
+ font-size: 1.3em; }
248
+ #method-list h3 {
249
+ font-variant: small-caps;
250
+ text-transform: capitalize;
251
+ font-family: Georgia, serif;
252
+ color: #666;
253
+ font-size: 1.1em; }
254
+ #method-list ol {
255
+ padding: 0 0 .5em .5em; }
256
+
257
+ #context {
258
+ border-top: 1px dashed silver;
259
+ margin-top: 1em;
260
+ margin-bottom: 1em; }
261
+
262
+ #context h2, #section h2 {
263
+ font: small-caps 1.2em Georgia, serif;
264
+ color: #444;
265
+ margin: 1em 0 .2em 0; }
266
+
267
+ #methods .method {
268
+ border: 1px solid silver;
269
+ margin-top: .5em;
270
+ background: #eee; }
271
+ #methods .method .synopsis {
272
+ color: black;
273
+ background: silver;
274
+ padding: .2em 1em; }
275
+ #methods .method .synopsis .name {
276
+ font-weight: bold; }
277
+ #methods .method .synopsis a {
278
+ text-decoration: none; }
279
+ #methods .method .description {
280
+ padding: 0 1em; }
281
+ #methods .method .description pre {
282
+ background: #f8f8f8; }
283
+ #methods .method .source {
284
+ margin: .5em 0; }
285
+ #methods .method .source-toggle {
286
+ font-size: 85%;
287
+ margin-left: 1em; }
288
+ #methods .public-class {
289
+ background: #ffffe4; }
290
+ #methods .public-instance .synopsis {
291
+ color: #eee;
292
+ background: #336699; }
293
+
294
+ #content .method .source pre {
295
+ background: #262626;
296
+ color: #ffdead;
297
+ margin: 1em;
298
+ padding: 0.5em;
299
+ border: 1px dashed #999;
300
+ overflow: auto; }
301
+ #content .method .source pre .ruby-constant {
302
+ color: #7fffd4;
303
+ background: transparent; }
304
+ #content .method .source pre .ruby-keyword {
305
+ color: #00ffff;
306
+ background: transparent; }
307
+ #content .method .source pre .ruby-ivar {
308
+ color: #eedd82;
309
+ background: transparent; }
310
+ #content .method .source pre .ruby-operator {
311
+ color: #00ffee;
312
+ background: transparent; }
313
+ #content .method .source pre .ruby-identifier {
314
+ color: #ffdead;
315
+ background: transparent; }
316
+ #content .method .source pre .ruby-node {
317
+ color: #ffa07a;
318
+ background: transparent; }
319
+ #content .method .source pre .ruby-comment {
320
+ color: #b22222;
321
+ font-weight: bold;
322
+ background: transparent; }
323
+ #content .method .source pre .ruby-regexp {
324
+ color: #ffa07a;
325
+ background: transparent; }
326
+ #content .method .source pre .ruby-value {
327
+ color: #7fffd4;
328
+ background: transparent; }
@@ -0,0 +1,298 @@
1
+ # The configuration of Elo is done here.
2
+ #
3
+ # See README.rdoc for general information about Elo.
4
+ module Elo
5
+
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
+
91
+ end
92
+
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
+
296
+ end
297
+
298
+ end