diff --git a/rb/glicko2_test.rb b/rb/glicko2_test.rb index 46d1012..e104ca6 100644 --- a/rb/glicko2_test.rb +++ b/rb/glicko2_test.rb @@ -22,6 +22,12 @@ class TestGlicko2 < Test::Unit::TestCase assert_in_delta(1464.05,a.rating,0.01) assert_in_delta(151.516,a.deviation,0.01) + + a.update() + + assert_in_delta(1464.05,a.rating,0.01) + assert_in_delta(151.516,a.deviation,0.01) + end end diff --git a/rb/xmlrate.rb b/rb/xmlrate.rb index 87b709c..bd5e383 100644 --- a/rb/xmlrate.rb +++ b/rb/xmlrate.rb @@ -1,68 +1,107 @@ +#!/usr/bin/env ruby require "rexml/document" require "glicko2" -file = File.new( "../py/data/hockey-2005.xml" ) - -doc = REXML::Document.new(file) - -# each rating doc -doc.elements.each("ratingdoc") do |root| - - players = Hash.new - - # each player - root.elements.each("player") do |player| - - name = player.elements["name"].text - rating = 1500.0 - deviation = 350.0 - variance = 0.06 - - # TODO: find better "Ruby-esqe" way of doing these - if player.elements["rating"] - rating = player.elements["rating"].text - end - if player.elements["deviation"] - deviation = player.elements["deviation"].text - end - if player.elements["variance"] - variance = player.elements["variance"].text - end - - players[name] = Glicko2.new(rating.to_f,deviation.to_f,variance.to_f) - puts name,rating,deviation,variance - puts - - end - - # each rating period - root.elements.each("period") do |period| - - # each regular game - period.elements.each("game") do |game| - - home = game.elements["home"].text - away = game.elements["away"].text - result = game.elements["result"].text - - players[home].add_result( players[away], result.to_f ) - players[away].add_result( players[home], 1.0 - result.to_f ) - end - - # each race - period.elements.each("race") do |race| - race.elements.each("competitor") do |c| - puts c.text - end - puts - end - - # update all players - players.each_value { |p| p.update } - players.each { |p,r| puts p,r.rating } - - end - +# sort & output the player array +def output_players(playerhash,title="") + puts title + sorted_players = playerhash.sort { |a,b| a[1] <=> b[1] } + i = 0 + sorted_players.reverse.each { |k,v| puts sprintf("%3d%30s: %6.1f +/- %5.1f %0.05f\n",i += 1,k,v.rating,v.deviation,v.volatility) } + puts end +# handle one file +def parse_file(filename) + + # load & parse XML + file = File.new(filename) + doc = REXML::Document.new(file) + + # each rating doc + doc.elements.each("ratingdoc") do |root| + + # new/empty player hash + players = Hash.new + + # each player + root.elements.each("player") do |player| + + # setup defaults + name = player.elements["name"].text + rating = 1500.0 + deviation = 350.0 + variance = 0.06 + + # TODO: find better "Ruby-esqe" way of doing these + if player.elements["rating"] + rating = player.elements["rating"].text + end + if player.elements["deviation"] + deviation = player.elements["deviation"].text + end + if player.elements["variance"] + variance = player.elements["variance"].text + end + + # create player + glicko2 object + players[name] = Glicko2.new(rating.to_f,deviation.to_f,variance.to_f) + + end + + # dump initial player ratings + output_players(players,"initial ratings") + + # each rating period + period_id = 0 + root.elements.each("period") do |period| + + # each regular game + period.elements.each("game") do |game| + + home = game.elements["home"].text + away = game.elements["away"].text + result = game.elements["result"].text + + result_val = Glicko2::WIN if result == "win" + result_val = Glicko2::LOSS if result == "loss" + result_val = Glicko2::DRAW if result == "draw" + + players[home].add_result( players[away], result_val ) + players[away].add_result( players[home], 1.0 - result_val ) + end + + # each race + racelist = [] + period.elements.each("race") do |race| + race.elements.each("competitor") do |c| + + # make this competitor lose to everyone already in our list + racelist.each do |otherc| + players[otherc].add_win(players[c.text]) + players[c.text].add_loss(players[otherc]) + end + + # add this competitor to our list + racelist.push(c.text) + + end + end + + # update all players + players.each_value { |p| p.update } + + # output players + output_players(players,sprintf("after rating period %d",period_id += 1)) + + end + + end +end + + +# main program +ARGV.each { |a| parse_file(a) } + +