elo_demo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e726a1cf9b570c320a2db70e33cdb574fa3e6a49
4
+ data.tar.gz: e6feab4d422bd8db9f8a915406e55875184c120c
5
+ SHA512:
6
+ metadata.gz: f422db78f8176d2ef7801552fcf460e749f73b1cdd435681dfaa733a80a091a206d582c342b59afaabde167b2ac114ad03f475b239d5022293cd39b253606644
7
+ data.tar.gz: 4539decaa860a62c0dd6192a7c2ef47b69d042de16f7ab053439d7dcdd80e0e933a942b4608aac8b21c766e91569a9beac2aebeef849109b4211a8b057980f9e
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.13.1
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ ruby '2.3.1'
4
+
5
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 TODO: Write your name
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.
@@ -0,0 +1,162 @@
1
+ # Elo Rating Demonstration
2
+
3
+ Most people do ranking wrong. In this small demonstration I have 10 players battling each other through 1,000 random matches.
4
+
5
+ The most naive sorting would be to create a simple score (such as number of wins subtracted by number of loses). In this case, this is the simple ranking:
6
+
7
+ ```
8
+ Ranking sorted by Points (wins - loses)
9
+ Name Games Wins Loses Points Elo Rating
10
+ 1 Kong 217 117 100 17 802
11
+ 2 Samus 211 110 101 9 842
12
+ 3 Wario 197 102 95 7 824
13
+ 4 Luigi 186 95 91 4 841
14
+ 5 Zelda 160 81 79 2 847
15
+ 6 Pikachu 209 105 104 1 851
16
+ 7 Yoshi 223 112 111 1 803
17
+ 8 Mario 203 101 102 -1 820
18
+ 9 Fox 208 95 113 -18 754
19
+ 10 Bowser 186 82 104 -22 785
20
+ ```
21
+
22
+ Now we make Samus (2nd) wins from Wario (3rd) 10 times in a row, without no one else battling this time.
23
+
24
+ ```
25
+ Ranking sorted by Points (wins - loses)
26
+ Name Games Wins Loses Points Elo Rating
27
+ 1 Samus 221 120 101 19 896
28
+ 2 Kong 217 117 100 17 802
29
+ 3 Luigi 186 95 91 4 841
30
+ 4 Zelda 160 81 79 2 847
31
+ 5 Pikachu 209 105 104 1 851
32
+ 6 Yoshi 223 112 111 1 803
33
+ 7 Mario 203 101 102 -1 820
34
+ 8 Wario 207 102 105 -3 760
35
+ 9 Fox 208 95 113 -18 754
36
+ 10 Bowser 186 82 104 -22 785
37
+ ```
38
+
39
+ You can see that Samus went to the top of the ranking while poor Wario drastically dropped from 3rd to 8th place.
40
+
41
+ This is very unfair as Wario was already weaker than then Samus so the odds of him winning were not high to begin with, and the penalty of trying against a stronger opponent made him drop a lot in the ranking.
42
+
43
+ Let's try to make Kong (2nd) losing to Bowser (10th) 10 times.
44
+
45
+ ```
46
+ Ranking sorted by Points (wins - loses)
47
+ Name Games Wins Loses Points Elo Rating
48
+ 1 Samus 221 120 101 19 896
49
+ 2 Kong 227 117 110 7 732
50
+ 3 Luigi 186 95 91 4 841
51
+ 4 Zelda 160 81 79 2 847
52
+ 5 Pikachu 209 105 104 1 851
53
+ 6 Yoshi 223 112 111 1 803
54
+ 7 Mario 203 101 102 -1 820
55
+ 8 Wario 207 102 105 -3 760
56
+ 9 Bowser 196 92 104 -12 845
57
+ 10 Fox 208 95 113 -18 754
58
+ ```
59
+
60
+ Now even though Kong was supposed to have better odds for being stronger, he lost 10 times in a row but he's still at 2nd place.
61
+
62
+ And Bowser, who performed an impressive winning streak of 10 wins agains the 2nd most stronger, still only jumped up one position in the ranking, to 9th place.
63
+
64
+ Again, this is very unfair. And this is why simple counting methods such as absolute ammout of wins and loses are not used in real rankings.
65
+
66
+
67
+ ## Elo Rating Run
68
+
69
+ Starting over with the exact 1,000 matches in the exact same order and win x lose scenario, we already arrive to a very different ranking than the first one:
70
+
71
+ In the naive system, the current top player Pikachu was only in 6th, and Kong, who is at 8th here was considered the top player.
72
+
73
+ ```
74
+ Ranking sorted by Elo Rating
75
+ Name Games Wins Loses Points Elo Rating
76
+ 1 Pikachu 209 105 104 1 851
77
+ 2 Zelda 160 81 79 2 847
78
+ 3 Samus 211 110 101 9 842
79
+ 4 Luigi 186 95 91 4 841
80
+ 5 Wario 197 102 95 7 824
81
+ 6 Mario 203 101 102 -1 820
82
+ 7 Yoshi 223 112 111 1 803
83
+ 8 Kong 217 117 100 17 802
84
+ 9 Bowser 186 82 104 -22 785
85
+ 10 Fox 208 95 113 -18 754
86
+ ```
87
+
88
+ Now let's make Zelda (2nd) win from Samus (3rd) 10 times.
89
+
90
+ They are almost evenly matched (almost same level in the ranking), so Zelda jumps to 1st place and Samus drops from 3rd to 9th.
91
+
92
+ ```
93
+ Ranking sorted by Elo Rating
94
+ Name Games Wins Loses Points Elo Rating
95
+ 1 Zelda 170 91 79 12 904
96
+ 2 Pikachu 209 105 104 1 851
97
+ 3 Luigi 186 95 91 4 841
98
+ 4 Wario 197 102 95 7 824
99
+ 5 Mario 203 101 102 -1 820
100
+ 6 Yoshi 223 112 111 1 803
101
+ 7 Kong 217 117 100 17 802
102
+ 8 Bowser 186 82 104 -22 785
103
+ 9 Samus 221 110 111 -1 775
104
+ 10 Fox 208 95 113 -18 754
105
+ ```
106
+
107
+ Now let's try the improbable scenario of Pikachu (2nd) losing to Fox (10th) 10 times.
108
+
109
+ ```
110
+ Ranking sorted by Elo Rating
111
+ Name Games Wins Loses Points Elo Rating
112
+ 1 Zelda 170 91 79 12 904
113
+ 2 Luigi 186 95 91 4 841
114
+ 3 Fox 218 105 113 -8 829
115
+ 4 Wario 197 102 95 7 824
116
+ 5 Mario 203 101 102 -1 820
117
+ 6 Yoshi 223 112 111 1 803
118
+ 7 Kong 217 117 100 17 802
119
+ 8 Bowser 186 82 104 -22 785
120
+ 9 Samus 221 110 111 -1 775
121
+ 10 Pikachu 219 105 114 -9 766
122
+ ```
123
+
124
+ They almost swapped positions because by the skill reflected in the ranking, Pikachu had a higher probability of winning against a 'weak' opponent such as Fox.
125
+
126
+ But instead, Fox did an impressive winning streak, so he deserved jumping up from 10th to 3rd, and Pikachu suffered a severe penalty of dropping from 3rd to 10th for losing so many times.
127
+
128
+ This is a much fair system where we use the probability of wins and loses.
129
+
130
+ If a strong player does a match against a weaker player, he shouldn't jump up too much in the ranking while the weaker shouldn't drop down so much from the ranking, as it's all expected outcomes.
131
+
132
+ Now, in the case of improbable matches where a stronger player loses against a weaker player, the stronger should drop down a lot more and the weaker should jump up a lot more as reward.
133
+
134
+ This motivates the stronger player to play their best to stay up and the weaker to risk against stronger opponents to wield better rewards.
135
+
136
+ That's what makes a more competitive environment for players.
137
+
138
+ ## Installation
139
+
140
+ And then execute:
141
+
142
+ $ bundle
143
+
144
+ Or install it yourself as:
145
+
146
+ $ gem install elo_demo
147
+
148
+ ## Usage
149
+
150
+ Just execute the scenario runner:
151
+
152
+ $ bin/run_demo
153
+
154
+ ## Contributing
155
+
156
+ Bug reports and pull requests are welcome on GitHub at https://github.com/akitaonrails/elo_demo.
157
+
158
+
159
+ ## License
160
+
161
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
162
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "elo_demo"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "elo_demo"
5
+
6
+ puts "##"
7
+ puts "## NAIVE RUN"
8
+
9
+ t = EloDemo::Tournment.new
10
+ t.run!
11
+ t.print_naive_ranking
12
+
13
+ attempts = 10
14
+
15
+ second_player = t.sorted_by_naive[1]
16
+ third_player = t.sorted_by_naive[2]
17
+
18
+ puts "#{second_player.name} (2nd) wins from #{third_player.name} (3rd) #{attempts} times"
19
+ attempts.times do
20
+ t.mark_win(second_player, third_player)
21
+ end
22
+
23
+ t.print_naive_ranking
24
+
25
+ second_player = t.sorted_by_naive[1]
26
+ worst_player = t.sorted_by_naive.last
27
+
28
+ puts "#{second_player.name} (2nd) loses to #{worst_player.name} (10th) #{attempts} times"
29
+ attempts.times do
30
+ t.mark_win(worst_player, second_player)
31
+ end
32
+
33
+ t.print_naive_ranking
34
+
35
+ fourth_player = t.sorted_by_naive[3]
36
+ fifth_player = t.sorted_by_naive[4]
37
+
38
+ puts "#{fourth_player.name} (4th) wins to #{fifth_player.name} (5th) #{attempts} times"
39
+ attempts.times do
40
+ t.mark_win(fourth_player, fifth_player)
41
+ end
42
+
43
+ t.print_naive_ranking
44
+
45
+
46
+
47
+
48
+ puts "##"
49
+ puts "## ELO RUN"
50
+
51
+ t = EloDemo::Tournment.new
52
+ t.run!
53
+ t.print_elo_ranking
54
+
55
+ attempts = 10
56
+
57
+ second_player = t.sorted_by_elo[1]
58
+ third_player = t.sorted_by_elo[2]
59
+
60
+ puts "#{second_player.name} (2nd) wins from #{third_player.name} (3rd) #{attempts} times"
61
+ attempts.times do
62
+ t.mark_win(second_player, third_player)
63
+ end
64
+
65
+ t.print_elo_ranking
66
+
67
+ second_player = t.sorted_by_elo[1]
68
+ worst_player = t.sorted_by_elo.last
69
+
70
+ puts "#{second_player.name} (2nd) loses to #{worst_player.name} (10th) #{attempts} times"
71
+ attempts.times do
72
+ t.mark_win(worst_player, second_player)
73
+ end
74
+
75
+ t.print_elo_ranking
76
+
77
+ fourth_player = t.sorted_by_elo[3]
78
+ fifth_player = t.sorted_by_elo[4]
79
+
80
+ puts "#{fourth_player.name} (4th) wins to #{fifth_player.name} (5th) #{attempts} times"
81
+ attempts.times do
82
+ t.mark_win(fourth_player, fifth_player)
83
+ end
84
+
85
+ t.print_elo_ranking
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'elo_demo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "elo_demo"
8
+ spec.version = EloDemo::VERSION
9
+ spec.authors = ["Fabio Akita"]
10
+ spec.email = ["boss@akitaonrails.com"]
11
+
12
+ spec.summary = %q{This is a short demonstration of one of the most miunderstood topic in programming: ranking}
13
+ spec.description = %q{This short demo will show what most people think of ranking and what the beginnings of a good ranking system actually look like}
14
+ spec.homepage = "https://github.com/akitaonrails/elo_demo"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_runtime_dependency "elo", "~> 0.1.0"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.13"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ end
@@ -0,0 +1,87 @@
1
+ require "elo_demo/version"
2
+ require "elo"
3
+
4
+ module EloDemo
5
+ class Player
6
+ attr_accessor :name, :games_played, :wins, :loses, :elo_player
7
+ def initialize(options = {})
8
+ self.name = options[:name]
9
+ self.games_played = options[:games_played] || 0
10
+ self.wins = options[:wins] || 0
11
+ self.loses = options[:loses] || 0
12
+ self.elo_player = Elo::Player.new
13
+ end
14
+ end
15
+
16
+ class Tournment
17
+ attr_reader :players
18
+ NAMES = %w[Mario Luigi Zelda Bowser Yoshi Wario Fox Pikachu Kong Samus Kirby]
19
+
20
+ def initialize
21
+ srand(666)
22
+
23
+ @players = []
24
+ 10.times do |i|
25
+ @players << Player.new(name: NAMES[i])
26
+ end
27
+ end
28
+
29
+ def run!
30
+ 1000.times do
31
+ player_0 = @players[rand(1..players.size) - 1]
32
+ player_1 = @players[rand(1..players.size) - 1]
33
+ game = [player_0, player_1]
34
+ winner = game[rand(0..1)]
35
+
36
+ if player_0.name == winner.name
37
+ mark_win(player_0, player_1)
38
+ else
39
+ mark_win(player_1, player_0)
40
+ end
41
+ end
42
+ end
43
+
44
+ def mark_win(p1, p2)
45
+ p1.games_played += 1
46
+ p2.games_played += 1
47
+ p1.wins += 1
48
+ p2.loses += 1
49
+ p1.elo_player.wins_from(p2.elo_player)
50
+ end
51
+
52
+ def print_ranking
53
+ print_naive_ranking
54
+ print_elo_ranking
55
+ end
56
+
57
+ def print_naive_ranking
58
+ puts "Ranking sorted by Points (wins - loses)"
59
+ puts " #{"Name".ljust(10)} Games Wins Loses Points Elo Rating"
60
+ sorted_by_naive.each_with_index do |p, index|
61
+ print_line p, index + 1
62
+ end
63
+ puts ""
64
+ end
65
+
66
+ def print_elo_ranking
67
+ puts "Ranking sorted by Elo Rating"
68
+ puts " #{"Name".ljust(10)} Games Wins Loses Points Elo Rating"
69
+ sorted_by_elo.each_with_index do |p, index|
70
+ print_line p, index + 1
71
+ end
72
+ puts ""
73
+ end
74
+
75
+ def sorted_by_naive
76
+ players.sort_by { |p| p.wins - p.loses }.reverse
77
+ end
78
+
79
+ def sorted_by_elo
80
+ players.sort_by { |p| p.elo_player.rating }.reverse
81
+ end
82
+
83
+ def print_line(p, index)
84
+ puts "#{index.to_s.rjust(3)} #{p.name.ljust(10)} #{p.games_played.to_s.rjust(5)} #{p.wins.to_s.rjust(4)} #{p.loses.to_s.rjust(5)} #{(p.wins - p.loses).to_s.rjust(5)} #{p.elo_player.rating.to_s.rjust(10)}"
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,3 @@
1
+ module EloDemo
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elo_demo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Fabio Akita
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-11-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: elo
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: This short demo will show what most people think of ranking and what
70
+ the beginnings of a good ranking system actually look like
71
+ email:
72
+ - boss@akitaonrails.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin/console
85
+ - bin/run_demo
86
+ - bin/setup
87
+ - elo_demo.gemspec
88
+ - lib/elo_demo.rb
89
+ - lib/elo_demo/version.rb
90
+ homepage: https://github.com/akitaonrails/elo_demo
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.5.1
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: 'This is a short demonstration of one of the most miunderstood topic in programming:
114
+ ranking'
115
+ test_files: []