Skip to content

Instantly share code, notes, and snippets.

@vwall
Forked from leemeichin/README.md
Created August 14, 2016 01:39
Show Gist options
  • Save vwall/47b5a9487684f3190b863f35be9fa868 to your computer and use it in GitHub Desktop.
Save vwall/47b5a9487684f3190b863f35be9fa868 to your computer and use it in GitHub Desktop.
Apply Standard Competition Rankings to your leaderboard

Standard Competition Rankings

This class makes it easier to generate a leaderboard that complies with the Standard Competition Ranking system. That is, it takes joint/tied positions into account, and adjusts the positions accordingly.

Take the following list of users and points for example:

User    | Points
1         35
2         35
3         50
4         10
5         35

You might be contented with a leaderboard like this:

User    | Points    | Position
3         50          1
2         35          2
1         35          3
5         35          4
4         10          5

Users 2, 1 and 5 all have the same points, so what makes user 2 come second, and user 5 come fourth? That can't be right!

What you really need is this:

User    | Points    | Position
3         50          1
2         35          2
1         35          2
5         35          2
4         10          5

Which you can do with the code in this gist!

ranking_data = {...} # hash representation of above user/points table

leaderboard = StandardCompetitionRankings.new(ranking_data, :rank_by => :points, :sort_direction => :desc)
better_leaderboard = leaderboard.calculate

If you don't want any sorting applied because you want to roll your own or whatever, set the :sort_direction parameter to nil or false.

After all that, it will append a field called position to your dataset, which contains your now standardised rankings, and return the whole thing. :)

class StandardCompetitionRankings
def initialize(data, options = {})
@data = data
@options = options.reverse_merge(:rank_by => :points, :sort_direction => :desc)
end
def sort_data!
return if @options[:sort_direction].nil? or @options[:sort_direction] == false
case @options[:sort_direction]
when :desc then @data.sort! {|a, b| b[@options[:rank_by]] <=> a[@options[:rank_by]] }
when :asc then @data.sort! {|a, b| a[@options[:rank_by]] <=> b[@options[:rank_by]] }
else raise ArgumentError, "Sort direction can only be :asc or :desc"
end
end
def calculate
return @rankings if @rankings.present?
@rankings = []
sort_data!
@data.each_with_index do |data, i|
if i == 0
data[:position] = 1
elsif data[@options[:rank_by]] == @data[i-1][@options[:rank_by]]
data[:position] = @rankings[i-1][:position]
else
data[:position] = i + 1
end
@rankings[i] = data
end
@rankings
end
end
require 'spec_helper'
describe StandardCompetitionRankings do
let!(:sample_data) do
%w(1000 2000 3000 3000 4000 5000 6000 7000 7000 9000).map {|points| {:points => points} }
end
describe '#calculate' do
it "should produce rankings with correctly calculated tied positions, sorted ascending" do
scr = Pokerfed::StandardCompetitionRankings.new(sample_data, :rank_by => :points, :sort_direction => :asc)
rankings = scr.calculate.map {|r| r[:position] }
rankings.should == [1, 2, 3, 3, 5, 6, 7, 8, 8, 10]
end
it "should produce rankings with correctly calculated tied positions, sorted descending" do
scr = Pokerfed::StandardCompetitionRankings.new(sample_data, :rank_by => :points, :sort_direction => :desc)
rankings = scr.calculate.map {|r| r[:position] }
rankings.should == [1, 2, 2, 4, 5, 6, 7, 7, 9, 10]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment