#!/usr/bin/ruby
## $Id$
-## Copyright (C) 2006 Daigo Moriwaki <daigo at debian dot org>
+## Copyright (C) 2006-2008 Daigo Moriwaki <daigo at debian dot org>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
#
# Sample:
# $ ./mk_rate . > players.yaml
+# $ ./mk_rate . && ./mk_rate . > players.yaml
#
# The conditions that games and players are rated as following:
# * Rated games, which were played by both rated players.
# * Rated players, who logged in the server with a name followed by a trip:
# "name,trip".
-# * (Rated) players, who played more than $GAMES_LIMIT [ten] (rated) games.
+# * (Rated) players, who played more than $GAMES_LIMIT [15] (rated) games.
#
#
# PREREQUIRE
#
# Count out players who play less games than $GAMES_LIMIT
-$GAMES_LIMIT = $DEBUG ? 0 : 10
+$GAMES_LIMIT = $DEBUG ? 0 : 15
WIN_MARK = "win"
LOSS_MARK = "lose"
DRAW_MARK = "draw"
end
##
- # The initial value of the rate, which is of very importance for Newton method.
- # This is based on my huristics; the higher the win probablity of a player is,
- # the greater points he takes.
+ # The initial value of the rate, which is of very importance for Newton
+ # method. This is based on my huristics; the higher the win probablity of
+ # a player is, the greater points he takes.
#
def initial_rate
possibility =
$stderr.puts "f: %s -> %f" % [f.to_a.inspect, f.nrm2] if $DEBUG
# GSL::Linalg::LU.solve or GSL::Linalg::HH.solve would be available instead.
- a = GSL::Linalg::SV.solve(j, f)
+ #a = GSL::Linalg::HH.solve(j, f)
+ a, = GSL::MultiFit::linear(j, f)
a = self.class.average(a)
# $stderr.puts "a: %s -> %f" % [a.to_a.inspect, a.nrm2] if $DEBUG
keys = players.keys.sort
size = keys.size
matrix =
- Matrix[*
+ GSL::Matrix[*
((0...size).collect do |k|
p1 = keys[k]
p1_hash = players[p1]
copied_cols = []
(0...size).each do |i|
next if i == delete_index
- row = @matrix.get_row(i) # get_row returns a copy of the row
+ row = @matrix.row(i).clone
row.delete_at(delete_index)
copied_cols << row
end
- new_matrix = Matrix[*copied_cols]
+ if copied_cols.size == 0
+ new_matrix = GSL::Matrix.new
+ else
+ new_matrix = GSL::Matrix[*copied_cols]
+ end
new_keys = @keys.clone
new_keys.delete_at(delete_index)
return WinLossMatrix.new(new_keys, new_matrix)
end
##
- # Removes players who do not pass a criteria to be rated, and returns a new object.
+ # Removes players who do not pass a criteria to be rated, and returns a
+ # new object.
#
def filter
$stderr.puts @keys.inspect if $DEBUG
result = subsets.collect do |keys|
matrix =
- Matrix[*
+ GSL::Matrix[*
((0...keys.size).collect do |k|
p1 = @keys.index(keys[k])
((0...keys.size).collect do |j|
0
else
p2 = @keys.index(keys[j])
- @matrix[p1][p2]
+ @matrix[p1,p2]
end
end)
end)]
exit 1
end
+def validate(yaml)
+ yaml["players"].each do |group_key, group|
+ group.each do |player_key, player|
+ rate = player['rate']
+ next unless rate
+ if rate > 10000 || rate < -10000
+ return false
+ end
+ end
+ end
+ return true
+end
+
def main
usage if ARGV.empty?
while dir = ARGV.shift do
Dir.glob( File.join(dir, "**", "*.csa") ) {|f| grep(f)}
end
- obj = WinLossMatrix::mk_win_loss_matrix($players)
yaml = {}
yaml["players"] = {}
rating_group = 0
- obj.connected_subsets.each do |win_loss_matrix|
- yaml["players"][rating_group] = {}
-
- rating = Rating.new(win_loss_matrix.matrix)
- rating.rating
- rating.average!(Rating::AVERAGE_RATE)
- rating.integer!
-
- win_loss_matrix.keys.each_with_index do |p, i| # player_id, index#
- win = win_loss_matrix.matrix.row(i).sum
- loss = win_loss_matrix.matrix.col(i).sum
-
- yaml["players"][rating_group][p] =
- { 'name' => p.split("+")[0],
- 'rating_group' => rating_group,
- 'rate' => rating.rate[i],
- 'last_modified' => $players_time[p].dup,
- 'win' => win,
- 'loss' => loss}
+ if $players.size > 0
+ obj = WinLossMatrix::mk_win_loss_matrix($players)
+ obj.connected_subsets.each do |win_loss_matrix|
+ yaml["players"][rating_group] = {}
+
+ rating = Rating.new(win_loss_matrix.matrix)
+ rating.rating
+ rating.average!(Rating::AVERAGE_RATE)
+ rating.integer!
+
+ win_loss_matrix.keys.each_with_index do |p, i| # player_id, index#
+ win = win_loss_matrix.matrix.row(i).sum
+ loss = win_loss_matrix.matrix.col(i).sum
+
+ yaml["players"][rating_group][p] =
+ { 'name' => p.split("+")[0],
+ 'rating_group' => rating_group,
+ 'rate' => rating.rate[i],
+ 'last_modified' => $players_time[p].dup,
+ 'win' => win,
+ 'loss' => loss}
+ end
+ rating_group += 1
+ end
+ end
+ rating_group -= 1
+ non_rated_group = 999 # large enough
+ yaml["players"][non_rated_group] = {}
+ $players.each_key do |id|
+ # skip players who have already been rated
+ found = false
+ (0..rating_group).each do |i|
+ found = true if yaml["players"][i][id]
+ break if found
end
- rating_group += 1
+ next if found
+
+ v = GSL::Vector[0, 0]
+ $players[id].each_value {|value| v += value}
+ next if v[0] < 1 && v[1] < 1
+
+ yaml["players"][non_rated_group][id] =
+ { 'name' => id.split("+")[0],
+ 'rating_group' => non_rated_group,
+ 'rate' => 0,
+ 'last_modified' => $players_time[id].dup,
+ 'win' => v[0],
+ 'loss' => v[1]}
+ end
+ unless validate(yaml)
+ $stderr.puts "Aborted. It did not result in valid ratings."
+ $stderr.puts yaml.to_yaml if $DEBUG
+ exit 10
end
puts yaml.to_yaml
end