acts_as_elo 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.
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rvmrc
6
+ *.tmproj
7
+
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rake"
4
+ gem "riot"
5
+ gemspec
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ ## ActsAsElo
2
+
3
+ ### What is it?
4
+
5
+ Provides sophisticated yet easy to understand ranking system with minimum changes to the system.
6
+ Elo ranking system is heavily used in tournaments, player rankings in chess, sports, leader boards, multiplayer games, ranking of test questions, tasks etc.
7
+
8
+ Read more about it on [wiki](http://en.wikipedia.org/wiki/Elo_rating_system)
9
+
10
+ ### Usage
11
+
12
+ `acts_as_elo` is very easy to use
13
+
14
+ 1. Install: `gem install acts_as_elo`
15
+ 2. Add `include Acts::Elo` and `acts_as_elo` to your model/record
16
+
17
+ ### Example
18
+
19
+ ```ruby
20
+
21
+ class Player
22
+ include Acts::Elo
23
+ acts_as_elo
24
+ end
25
+
26
+ bob, jack = Player.new, Player.new
27
+ bob.elo_rank # => 1200
28
+ jack.elo_rank # => 1200
29
+
30
+ bob.elo_win!(jack)
31
+ bob.elo_rank # => 1205
32
+ jack.elo_rank # => 1995
33
+ ```
34
+
35
+ That's it. No additional classes are involved.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ #require 'rake'
5
+ require 'rake/testtask'
6
+
7
+ # Run the test with 'rake' or 'rake test'
8
+ desc 'Default: run acts_as_elo unit tests.'
9
+ task :default => :test
10
+
11
+ desc 'Test the acts_as_list plugin.'
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'lib' << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = true
16
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ Gem::Specification.new do |s|
4
+ s.name = 'acts_as_elo'
5
+ s.version = '0.1.0'
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ['tjbladez']
8
+ s.email = ['nick@tjbladez.net']
9
+ s.homepage = 'http://github.com/tjbladez/acts_as_elo'
10
+ s.summary = %q{Elo ranking made easy}
11
+ s.description = %q{Provides sophisticated yet easy to understand ranking system with minimum changes to the system}
12
+
13
+ # Load Paths...
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.require_paths = ['lib']
17
+
18
+ # Dependencies (installed via 'bundle install')...
19
+ s.add_development_dependency("bundler", ["~> 1.0.0"])
20
+ s.add_development_dependency("riot")
21
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ $:.unshift "#{File.dirname(__FILE__)}/lib"
2
+ require 'acts_as_elo'
@@ -0,0 +1,73 @@
1
+ module Acts
2
+ module Elo
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def acts_as_elo(opts = {})
9
+ default_rank = opts[:default_rank] || 1200
10
+
11
+ unless opts.empty?
12
+ class << self
13
+ attr_accessor :acts_as_elo_options
14
+ end
15
+ @acts_as_elo_options = opts
16
+ end
17
+
18
+ class_eval do
19
+ attr_writer :elo_rank
20
+
21
+ define_method(:elo_rank) do
22
+ @elo_rank ||= default_rank
23
+ end
24
+ end
25
+
26
+ include Acts::Elo::InstanceMethods unless self.included_modules.include?(Acts::Elo::InstanceMethods)
27
+ end
28
+ end
29
+
30
+ module InstanceMethods
31
+ def elo_win!(opponent, opts={})
32
+ elo_update(opponent, opts.merge!(:result => :win))
33
+ end
34
+
35
+ def elo_lose!(opponent, opts={})
36
+ elo_update(opponent, opts.merge!(:result => :lose))
37
+ end
38
+
39
+ def elo_draw!(opponent, opts={})
40
+ elo_update(opponent, opts.merge!(:result => :draw))
41
+ end
42
+
43
+ def elo_update(opponent, opts={})
44
+ begin
45
+ if self.class.respond_to?(:acts_as_elo_options) && self.class.acts_as_elo_options[:one_way]
46
+ one_way = true
47
+ end
48
+ one_way ||= opts[:one_way]
49
+ diff = (opponent.elo_rank.to_f - elo_rank.to_f).abs
50
+ expected = 1 / (1 + 10 ** (diff / 400))
51
+ send_opts = {one_way: true}
52
+
53
+ if opts[:result] == :win
54
+ points = 1
55
+ send_opts.merge!(result: :lose)
56
+ elsif opts[:result] == :lose
57
+ points = 0
58
+ send_opts.merge!(result: :win)
59
+ elsif opts[:result] == :draw
60
+ points = 0.5
61
+ send_opts.merge!(result: :draw)
62
+ end
63
+
64
+ opponent.elo_update(self, send_opts) unless one_way
65
+
66
+ @elo_rank = (elo_rank + 10*(points-expected)).round
67
+ rescue Exception => e
68
+ puts "Exception: #{e.message}"
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1 @@
1
+ require 'acts_as_elo/acts/elo'
@@ -0,0 +1,46 @@
1
+ require 'pathname'
2
+ require Pathname.new('.') + 'helper'
3
+
4
+ # When user answers the question based on the on current rank of the user
5
+ # and current rank of the question both user and question ranks will be updated
6
+ class User
7
+ include Acts::Elo
8
+
9
+ attr_accessor :questions
10
+
11
+ acts_as_elo :one_way => true
12
+ end
13
+
14
+ class Question
15
+ include Acts::Elo
16
+
17
+ attr_accessor :user
18
+ acts_as_elo
19
+ end
20
+
21
+
22
+ context "User with many questions" do
23
+ helper(:question) { |user| Question.new.tap {|q| q.user = user} }
24
+
25
+ setup { User.new }
26
+ hookup { topic.questions = [question(topic), question(topic)] }
27
+
28
+ asserts(:elo_rank).equals(1200)
29
+ asserts("questions have default ranks"){ topic.questions.map(&:elo_rank)}.equals([1200, 1200])
30
+
31
+ context "when question is answered correctly" do
32
+ hookup { topic.questions.each {|q| q.elo_rank = 1200 }}
33
+ hookup { topic.questions.first.elo_lose!(topic)}
34
+
35
+ asserts(:elo_rank).equals(1205)
36
+ asserts("questions have their ranks updated"){ topic.questions.map(&:elo_rank)}.equals([1195, 1200])
37
+ end
38
+
39
+ context "when question is answered incorrectly" do
40
+ hookup { topic.questions.each {|q| q.elo_rank = 1200 }}
41
+ hookup { topic.questions.first.elo_win!(topic) }
42
+
43
+ asserts(:elo_rank).equals(1195)
44
+ asserts("questions have their ranks updated"){ topic.questions.map(&:elo_rank)}.equals([1205, 1200])
45
+ end
46
+ end
@@ -0,0 +1,51 @@
1
+ require 'pathname'
2
+ require Pathname.new('.') + 'helper'
3
+
4
+ class Player
5
+ include Acts::Elo
6
+
7
+ acts_as_elo
8
+ end
9
+
10
+
11
+ context "Two players are opponents of each other" do
12
+ setup { [Player.new, Player.new] }
13
+
14
+ asserts("default ranks are 1200") {
15
+ topic.map(&:elo_rank)
16
+ }.equals([1200, 1200])
17
+
18
+ context "when first one wins" do
19
+ hookup {topic.each {|pl| pl.elo_rank = 1200 }}
20
+ hookup {topic.first.elo_win!(topic.last)}
21
+
22
+ asserts("ranks are updated") {topic.map(&:elo_rank)}.equals([1205, 1195])
23
+ end
24
+
25
+ context "when first one loses" do
26
+ hookup {topic.each {|pl| pl.elo_rank = 1200 }}
27
+ hookup {topic.first.elo_lose!(topic.last)}
28
+
29
+ asserts("ranks are updated") {topic.map(&:elo_rank)}.equals([1195, 1205])
30
+ end
31
+
32
+ context "when they draw" do
33
+ hookup {topic.each {|pl| pl.elo_rank = 1200 }}
34
+ hookup {topic.first.elo_draw!(topic.last)}
35
+
36
+ asserts("ranks are updated") {topic.map(&:elo_rank)}.equals([1200, 1200])
37
+ end
38
+
39
+ end
40
+
41
+ class PlayerWithDefaultRank
42
+ include Acts::Elo
43
+ acts_as_elo :default_rank => 1300
44
+ end
45
+
46
+ context "Testing default for elo rank" do
47
+ setup { PlayerWithDefaultRank.new }
48
+ asserts(:elo_rank).equals(1300)
49
+ end
50
+
51
+
data/test/helper.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
11
+ require 'riot'
12
+ require "#{File.dirname(__FILE__)}/../init"
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_elo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - tjbladez
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-11 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: &70308114821680 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70308114821680
25
+ - !ruby/object:Gem::Dependency
26
+ name: riot
27
+ requirement: &70308114821280 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70308114821280
36
+ description: Provides sophisticated yet easy to understand ranking system with minimum
37
+ changes to the system
38
+ email:
39
+ - nick@tjbladez.net
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - README.md
47
+ - Rakefile
48
+ - acts_as_elo.gemspec
49
+ - init.rb
50
+ - lib/acts_as_elo.rb
51
+ - lib/acts_as_elo/acts/elo.rb
52
+ - test/advance_test.rb
53
+ - test/basic_test.rb
54
+ - test/helper.rb
55
+ homepage: http://github.com/tjbladez/acts_as_elo
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.10
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Elo ranking made easy
79
+ test_files:
80
+ - test/advance_test.rb
81
+ - test/basic_test.rb
82
+ - test/helper.rb