Add popularity script
[milivella] / milivelo.rb
1 #!/usr/bin/ruby
2
3 # Author: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
4 # License: GPLv2
5
6 # Quick & dirty script to calculate ELO rankings. See later of input format.
7
8 class Rating
9         # customizable
10         BASE_RATING=1500
11
12         # K factor, can be either fixed, or staircased, or some other function
13         def fixedK(score)
14                 # customizable
15                 16
16         end
17
18         def rangedK(score)
19                 # customizable
20                 case score
21                 when 0..2100
22                         32
23                 when 2100..2400
24                         24
25                 else
26                         # > 2400
27                         16
28                 end
29         end
30
31         def funcK(score)
32                 # customizable
33                 32 - Math.log(2400)
34         end
35
36         # choose the one we want here
37         alias :factor :fixedK
38
39         attr_reader :rating
40         def initialize
41                 # the player list is a hash player => rating that defaults to the base rating
42                 @rating = Hash.new { |h, k| h[k] = BASE_RATING }
43         end
44
45         def [](k)
46                 @rating[k.to_sym]
47         end
48
49
50         def to_s
51                 rating.to_a.sort { |a, b| b.last <=> a.last }.map { |a| a.join(': ') }.join("\n")
52         end
53
54         def update(winner, loser, draw=false)
55                 diff = rating[loser] - rating[winner]
56                 winner_wins = 1.0/(Math.exp(diff/174)+1)
57                 loser_wins = 1.0 - winner_wins
58                 score = 1.0
59                 score -= 0.5 if draw
60                 rating[winner] += factor(rating[winner])*(score - winner_wins)
61                 rating[loser] += factor(rating[loser])*(1.0 - score - loser_wins)
62         end
63 end
64
65 # read lines from standard input, skipping lines that start with a has sign (#)
66 # each match line is supposed to be in the form:
67 # player1 <=> player2
68 # where <=> is < if player2 won, > if player1 won, = if the match was a draw.
69 # 2, 1, x are also accepted instead of <, > and = respectively.
70 # Lines in the form:
71 # player1,player2,<=>
72 # are accepted too.
73
74 # The player names should have no spaces in them, and anything after the name
75 # of the second player is ignored
76
77 rating = Rating.new
78 $stdin.each_line do |line|
79         # strip comments
80         line.sub!(/#.*/,'')
81                 # strip whitespace
82                 line.strip!
83         next if line.empty?
84         player1, sign, player2, junk = line.split($;,4)
85         unless sign
86                 player1, player2, sign = line.split(/\s*,\s*/,3)
87         end
88         case sign
89         when '2', '<'
90                 rating.update(player2.intern, player1.intern)
91         when 'x', 'X', '='
92                 rating.update(player2.intern, player1.intern, true)
93         when '1', '>'
94                 rating.update(player1.intern, player2.intern)
95         else
96                 raise "wrong match sign #{sign.inspect}"
97         end
98         puts "%s,%s,%s,%f,%f" % [player1, player2, sign, rating[player1], rating[player2]]
99 end
100
101 puts
102 puts rating.to_s