From 40b0cd07cb046e687ec92151acbe0aea1c1ef4d4 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 11 Dec 2008 17:01:02 +0100 Subject: [PATCH] Add script for ELO ranking computation --- milivelo.rb | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100755 milivelo.rb diff --git a/milivelo.rb b/milivelo.rb new file mode 100755 index 0000000..f6ef074 --- /dev/null +++ b/milivelo.rb @@ -0,0 +1,102 @@ +#!/usr/bin/ruby + +# Author: Giuseppe "Oblomov" Bilotta +# License: GPLv2 + +# Quick & dirty script to calculate ELO rankings. See later of input format. + +class Rating + # customizable + BASE_RATING=1500 + + # K factor, can be either fixed, or staircased, or some other function + def fixedK(score) + # customizable + 16 + end + + def rangedK(score) + # customizable + case score + when 0..2100 + 32 + when 2100..2400 + 24 + else + # > 2400 + 16 + end + end + + def funcK(score) + # customizable + 32 - Math.log(2400) + end + + # choose the one we want here + alias :factor :fixedK + + attr_reader :rating + def initialize + # the player list is a hash player => rating that defaults to the base rating + @rating = Hash.new { |h, k| h[k] = BASE_RATING } + end + + def [](k) + @rating[k.to_sym] + end + + + def to_s + rating.to_a.sort { |a, b| b.last <=> a.last }.map { |a| a.join(': ') }.join("\n") + end + + def update(winner, loser, draw=false) + diff = rating[loser] - rating[winner] + winner_wins = 1.0/(Math.exp(diff/174)+1) + loser_wins = 1.0 - winner_wins + score = 1.0 + score -= 0.5 if draw + rating[winner] += factor(rating[winner])*(score - winner_wins) + rating[loser] += factor(rating[loser])*(1.0 - score - loser_wins) + end +end + +# read lines from standard input, skipping lines that start with a has sign (#) +# each match line is supposed to be in the form: +# player1 <=> player2 +# where <=> is < if player2 won, > if player1 won, = if the match was a draw. +# 2, 1, x are also accepted instead of <, > and = respectively. +# Lines in the form: +# player1,player2,<=> +# are accepted too. + +# The player names should have no spaces in them, and anything after the name +# of the second player is ignored + +rating = Rating.new +$stdin.each_line do |line| + # strip comments + line.sub!(/#.*/,'') + # strip whitespace + line.strip! + next if line.empty? + player1, sign, player2, junk = line.split($;,4) + unless sign + player1, player2, sign = line.split(/\s*,\s*/,3) + end + case sign + when '2', '<' + rating.update(player2.intern, player1.intern) + when 'x', 'X', '=' + rating.update(player2.intern, player1.intern, true) + when '1', '>' + rating.update(player1.intern, player2.intern) + else + raise "wrong match sign #{sign.inspect}" + end + puts "%s,%s,%s,%f,%f" % [player1, player2, sign, rating[player1], rating[player2]] +end + +puts +puts rating.to_s -- 2.32.0.93.g670b81a890