Skip to content

Instantly share code, notes, and snippets.

@mortenjohs
Created February 14, 2014 11:05
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save mortenjohs/8999316 to your computer and use it in GitHub Desktop.
A small analyser of board game geek user stats.
require 'open-uri'
require 'nokogiri'
require 'pp'
require 'json'
def load_game id, refresh = false
filename = "./cache/#{id}.xml"
unless File.exist?(filename)
url = "http://www.boardgamegeek.com/xmlapi/boardgame/#{id}?stats=1"
file = File.open(filename,"w") do |f|
puts url
f.write(open(url).read)
end
end
Nokogiri::XML(File.open(filename)) do |config|
config.strict.noblanks
end
end
usernames = {
mortenjohs: 'mortenjohs',
ole: 'ebbe',
frode: 'frodefrank',
kai: 'abhoth_'
}
username = usernames[:mortenjohs]
reload_user = false
refresh_games = false
rootpath_v1 = "http://www.boardgamegeek.com/xmlapi/"
rootpath_v2 = ""
# http://www.boardgamegeek.com/xmlapi/boardgame/121408?stats=1
if reload_user || !File.exist?("#{username}.xml")
file = File.open("#{username}.xml","w") do |f|
url = rootpath_v1+"collection/"+username+"?stats=1"
puts url
f.write(open(url).read)
end
end
doc = Nokogiri::XML(File.open("#{username}.xml")) do |config|
config.strict.noblanks
end
mechanics_map = Hash.new { |hash1, key1| hash1[key1] = Hash.new { |hash2, key2| hash2[key2] = 0}}
playing_times = Hash.new
mechanics_times = Hash.new(0)
mechanics_combos = Hash.new(0)
mechanics_combo_times = Hash.new(0)
mechanics_ratings = Hash.new { |hash1, key1| hash1[key1] = {total_score: 0, weight: 0, games: 0}}
i = 0
doc.xpath("//item").each do |item|
id = item["objectid"]
name = item.xpath("name").text
time_per_game = item.xpath("stats").first["playingtime"].to_i
numplays = item.xpath("numplays").text.to_i
playing_times[name] = time_per_game * numplays
rating = item.xpath("stats/rating").first["value"]
game = load_game id, refresh_games
mechanics = game.xpath("//boardgame/boardgamemechanic").children.to_a.map{|e| e.text}
mechanics.each do |m1|
mechanics_times[m1] += (time_per_game * numplays)/mechanics.length
unless rating == "N/A"
mechanics_ratings[m1][:total_score] += rating.to_f/mechanics.length.to_f
mechanics_ratings[m1][:weight] += 1/mechanics.length.to_f
mechanics_ratings[m1][:games] += 1
end
mechanics.each do |m2|
mechanics_map[m1][m2]+=1 unless m1 == m2
end
end
mechanics_combos[mechanics.sort.join(" & ")] += 1 unless numplays == 0
mechanics_combo_times[mechanics.sort.join(" & ")] += time_per_game * numplays
end
puts "\n\nGames"
pp playing_times.sort_by{|k,v| -v}.delete_if{|e| e[1] == 0}
puts "\n\nMechanics"
pp mechanics_times.sort_by{|k,v| -v}.delete_if{|e| e[1] == 0}
puts "\n\nMechanics Map"
pp mechanics_map.each {|k,v| mechanics_map[k] = v.sort_by{ |j,c| -c }}.sort_by{|e| -e[1][0][1]}
puts "\n\nMechanics Combos"
pp mechanics_combos.sort_by{|k,v| -v}.delete_if{|e| e[1] == 0}
puts "\n\nMechanics Combos Times"
pp mechanics_combo_times.sort_by{|k,v| -v}.delete_if{|e| e[1] == 0}
puts "\n\nMechanics Ratings"
pp mechanics_ratings.map{|k,v| [k, (v[:total_score]/v[:weight]).round(2), "# of games: #{v[:games]} (Weight: #{v[:weight].round(2)})"]}.sort_by{|e| -e[1]}
## force directed graph
nodes = {}
mechanics_map.keys.each_with_index do |k, i|
nodes[k] = {
"name"=> k,
"group"=>1,
"index" => i,
"rating" => mechanics_ratings[k][:weight]>0 ? (mechanics_ratings[k][:total_score]/mechanics_ratings[k][:weight]).round(2) : 0,
"weight" => mechanics_ratings[k][:weight],
"games" => mechanics_ratings[k][:games]
}
end
links = []
mechanics_map.each do |source, targets|
targets.each do |target|
s = nodes[source]["index"]
t = nodes[target[0]]["index"]
links << { "source"=> s, "target"=> t, "value" => target[1], "source_name" => source, "target_name" => target[0] } unless t < s
end
[mechanics_map.keys - targets].each do |target|
s = nodes[source]["index"]
t = nodes[target[0]]["index"]
links << { "source"=> s, "target"=> t, "value" => -1, "source_name" => source, "target_name" => target[0] } unless t < s
end
end
# puts JSON.pretty_generate(links)
File.open("#{username}.json", "w") do |file|
data = {
"nodes" => nodes.values,
"links" => links
}
file.write(JSON.pretty_generate(data))
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment