elo_demo 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: 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: []