X-Git-Url: http://git.sourceforge.jp/view?p=shogi-server%2Fshogi-server.git;a=blobdiff_plain;f=mk_rate;h=d072ae13eecf24b26cbb489929b837624b313cf8;hp=919a7c5fef50e90626af34973898cdb607e7165f;hb=9551a1c96db3c222850967c0d4738fe5b1ca5b1f;hpb=0b30a645a7f898b6f8808cb8586e8fb75b426dda diff --git a/mk_rate b/mk_rate index 919a7c5..d072ae1 100755 --- a/mk_rate +++ b/mk_rate @@ -1,7 +1,7 @@ #!/usr/bin/ruby ## $Id$ -## Copyright (C) 2006 Daigo Moriwaki +## Copyright (C) 2006-2008 Daigo Moriwaki ## ## 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 @@ -23,6 +23,7 @@ # # 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. @@ -50,6 +51,7 @@ require 'yaml' require 'time' +require 'getoptlong' require 'gsl' require 'rubygems' require 'rgl/adjacency' @@ -210,9 +212,9 @@ class Rating 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 = @@ -281,7 +283,8 @@ class Rating $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 @@ -334,6 +337,13 @@ class Rating end ## + # Translate by value + # + def translate!(value) + @rate += value + end + + ## # Make the values of @rate integer. # def integer! @@ -368,7 +378,7 @@ class WinLossMatrix keys = players.keys.sort size = keys.size matrix = - Matrix[* + GSL::Matrix[* ((0...size).collect do |k| p1 = keys[k] p1_hash = players[p1] @@ -424,11 +434,15 @@ class WinLossMatrix 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) @@ -446,7 +460,8 @@ class WinLossMatrix 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 @@ -497,7 +512,7 @@ class WinLossMatrix 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| @@ -505,7 +520,7 @@ class WinLossMatrix 0 else p2 = @keys.index(keys[j]) - @matrix[p1][p2] + @matrix[p1,p2] end end) end)] @@ -531,12 +546,11 @@ end # Half-life effect # After NHAFE_LIFE days value will get half. # 0.693 is constant, where exp(0.693) ~ 0.5 -NHALF_LIFE=60 def half_life(days) - if days < 7 + if days < $options["half-life-ignore"] return 1.0 else - Math::exp(-0.693/NHALF_LIFE*(days-7)) + Math::exp(-0.693/$options["half-life"]*(days-$options["half-life-ignore"])) end end @@ -614,17 +628,76 @@ USAGE: #{$0} dir [...] 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 usage(io) + io.puts < 0 obj = WinLossMatrix::mk_win_loss_matrix($players) - rating_group = 0 obj.connected_subsets.each do |win_loss_matrix| yaml["players"][rating_group] = {} @@ -633,6 +706,23 @@ def main rating.average!(Rating::AVERAGE_RATE) rating.integer! + if $options["fixed-rate-player"] + # first, try exact match + index = win_loss_matrix.keys.index($options["fixed-rate-player"]) + # second, try regular match + unless index + win_loss_matrix.keys.each_with_index do |p, i| + if %r!#{$options["fixed-rate-player"]}! =~ p + index = i + end + end + end + if index + the_rate = rating.rate[index] + rating.translate!($options["fixed-rate"] - the_rate) + end + end + 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 @@ -648,6 +738,35 @@ def main 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 + 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