Skip to content

Instantly share code, notes, and snippets.

@chron
Created June 2, 2011 05:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chron/1003992 to your computer and use it in GitHub Desktop.
Save chron/1003992 to your computer and use it in GitHub Desktop.
WoW Armory average ilvl and boss kill comparison
#
# compare-kills-new-armory.rb
# Paul Prestidge
# 2011-05-20
#
require 'fileutils'
module Armory
class ArmoryCacher
CACHE_DIR = 'na'
class << self
attr_accessor :path
end
def initialize fn, url
location = File.join(CACHE_DIR, self.class.path)
FileUtils.mkdir_p(location)
fn = File.join(location, "/#{fn}.html")
@armorydata = if File.exist?(fn)
File.read(fn)
else
x = %x(curl --silent http://us.battle.net/#{url})
File.open(fn, 'w') { |f| f.puts x }
x
end
end
def self.set_path p
self.path = p
end
end
class Character < ArmoryCacher
set_path 'character'
def initialize character_name, realm
@name, @realm = character_name, realm
super "#{realm.downcase}-#{character_name.downcase}", "wow/en/character/#{realm}/#{character_name}/advanced"
@armorydata.gsub!(/bwl: \{.+?\},/m,'') # HACK: remove bwd because nef is in two places
end
def name
@armorydata.scan(/<title>(.+?) @/).first.first
end
def boss_kills
@armorydata.scan(/name: "([^"]+)"(?:, nKills: (-?\d+))?(?:, nHeroicKills: (-?\d+))?/).map do |b,k,hk|
[b, k.to_i, hk.to_i]
end
end
def items include_vanity = false
@armorydata.scan(/\/wow\/en\/item\/(\d+)" class="item"/).uniq.map { |n| Item.new(n.first) }.select { |i| include_vanity || !i.vanity? }
end
def achievements category
CharacterAchievements.new(@name, @realm, category)
end
def <=> o
self.name <=> o.name
end
end
class CharacterAchievements < ArmoryCacher
set_path 'character'
CATEGORIES = {
:dungeon_and_raid => '168'
}
def initialize character_name, realm, category
cat = CATEGORIES[category]
raise 'Unknown category' unless cat
super "#{realm.downcase}-#{character_name.downcase}-ach-#{cat.tr(':','c')}", "/wow/en/character/#{realm}/#{character_name}/achievement/#{cat}"
end
def gotcr
data = @armorydata[/Glory of the Cataclysm Raider(.+)<\/div>/m, 1].scan(/<li class="(.+?)">.+?<\/span>(.+?)<\/a>/m).map do |a,b|
[b.strip.gsub(/&#39;/, "'"), !!(a =~ /unlocked/)]
end
Hash[*data.flatten]
#data.map { |a,b| a }
end
end
class Item < ArmoryCacher
set_path 'item'
attr_reader :itemid
def initialize id
@itemid = id
super "item-#{id}", "wow/en/item/#{id}/tooltip"
end
def vanity?
@armorydata =~ /Shirt|Tabard/ # hmm
end
def shirt?
@armorydata =~ /\bShirt\b/
end
def tabard?
@armorydata =~ /\bTabard\b/
end
def set_item?
@armorydata =~ /\(\d+\) Set:/
end
def name
(@armorydata[/<h3 class="color-q\d+">(.+?)<\/h3>/, 1] || '?').gsub(/&#39;/, "'")
end
def ilvl
@armorydata[/Item Level (\d+)/, 1].to_i
end
def icon
@armorydata[/background-image: url\("([^"]+)"/, 1]
end
end
def self.report_gear_quality characters
r = characters.map do |c|
inv = c.items
#puts c.name; c.items.each { |i| puts ' -> %40s %6i %3i' % [i.name, i.itemid, i.ilvl] }
ntier, htier = [359, 372].map { |v| inv.select { |i| i.ilvl == v && i.set_item? }.size }
avgilvl = inv.inject(0.0) { |s,i| s + i.ilvl } / inv.size
[c.name, c.items.inject(Hash.new(0)) { |h,i| h[i.ilvl] += 1; h }, avgilvl, ntier, htier]
end
categories = r.map { |v| v[1].keys }.flatten.uniq.sort
puts ('%14s' + (' %3i' * categories.size) + ' avg set') % ['', *categories]
r.sort_by { |cn,h,avgilvl,nt,ht| [-avgilvl, cn] }.each do |cn,h,*r|
hp = categories.map { |cat| h[cat] || 0 } + r
puts ('%14s' + (' %3i' * categories.size) + ' %3.2f %1i/%1i') % [cn, *hp]
end
end
def self.report_boss_kills characters, heroic_only = false
puts (' '*15) + BOSSES.map { |n| s = n[0,3]; ABBREVS[s] || s }.join(' ')
characters.map do |c|
bk = BOSSES.map { |b| bn,k,hk = c.boss_kills.assoc(b); heroic_only ? [hk,0].max : [hk,0].max+[k,0].max }
[bk.inject{|a,b|a+b}, ('%14s' + (' %3i' * BOSSES.size)) % [c.name, *bk]]
end.sort_by { |sum,line| -sum }.each do |sum,line|
puts "#{line} => #{sum}"
end
end
def self.report_glory_progress characters, include_totals = false
template = '%14s' + (' %5s' * GLORY.size) + (include_totals ? ' %5s' : '')
puts template % ['', GLORY.map { |achi,abbrev| abbrev }, (include_totals ? 'total' : nil)].compact.flatten
ca = characters.sort.map { |c| [c, c.achievements(:dungeon_and_raid).gotcr] }
ca.each do |c,a|
puts template % [c.name, GLORY.map { |achi,abbrev| a[achi] ? 'X' : '-' }, (include_totals ? a.select{|k,v| v}.size : nil)].flatten
end
if include_totals
totals = GLORY.map { |achi,abbrev| ca.inject(0) { |i,j| i + (j.last[achi] ? 1 : 0) }}
puts template % ['Total', totals, totals.inject { |s,c| s + c }].flatten
end
# GLORY.each { |achi,abbr| puts '%10s => %s' % [abbr, achi] }
end
BOSSES = %w(
Argaloth
Magmaw Toxitron Maloriak Atramedes Chimaeron Nefarian
Halfus\ Wyrmbreaker Valiona Elementium\ Monstrosity Cho\\'gall Sinestra
Conclave\ of\ Wind Al\\'Akir
)
ABBREVS = { 'Tox' => 'OmC', 'Val' => 'V&T', 'Ele' => 'AsC', 'Al\\' => 'A\'A' }
GLORY = [
['Heroic: Magmaw', 'h-mag'],
['Heroic: Omnotron Defense System', 'h-omn'],
['Heroic: Maloriak', 'h-mal'],
['Heroic: Atramedes', 'h-atr'],
['Heroic: Chimaeron', 'h-chi'],
['Heroic: Nefarian', 'h-nef'],
['Heroic: Halfus Wyrmbreaker', 'h-hal'],
['Heroic: Valiona and Theralion', 'h-v&t'],
['Heroic: Ascendant Council', 'h-asc'],
['Heroic: Cho\'gall', 'h-cho'],
['Heroic: Conclave of Wind', 'h-con'],
['Heroic: Al\'Akir', 'h-a\'a'],
['Parasite Evening', 'para'],
['Achieve-a-tron', 'achi'],
['Silence is Golden', 'silen'],
['Full of Sound and Fury', 'fos&f'],
['Aberrant Behavior', 'aberr'],
['Keeping it in the Family', 'kiitf'],
['The Only Escape', 'escap'],
['Double Dragon', 'dd'],
['Elementary', 'elem'],
['The Abyss Will Gaze Back Into You', 'abyss'],
['Stay Chill', 'chill'],
['Four Play', 'four']
]
end
if $0 == __FILE__
characters = %w(freshest rockmellon azuba zuzum soul slyran shebull leafi sovereigna talvirr wendy snokey).map { |c| Armory::Character.new(c, 'Saurfang') }
#Armory.report_boss_kills(characters, true)
#Armory.report_gear_quality(characters)
#Armory.report_glory_progress(characters, true)
#characters.sort.each do |c|
# inv = c.items(true)
# shirt, tabard = inv.find { |i| i.shirt? }, inv.find { |i| i.tabard? }
# puts '%14s %30s %30s' % [c.name, shirt ? shirt.name : '', tabard ? tabard.name : '']
#end
File.open('iconfreq.txt', 'w') do |f|
characters.each do |c|
c.items.each do |item|
url = item.icon
fn = url[/\w+.\w+$/]
fp = File.join('na', 'icons', fn)
f.puts fn
%x(curl --silent -o #{fp} #{url}) unless File.exist?(fp)
end
end
end
require 'RMagick'
s = 56
icons = File.readlines('iconfreq.txt').map { |l| File.join('na', 'icons', l.chomp) } #Dir['icons/*.jpg']
w = h = nil
puts ' => %i icons' % icons.size
Math.sqrt(icons.size).ceil.downto(1) do |i|
if (icons.size/i.to_f) % 1 == 0
w, h = icons.size/i, i
break;
end
end
i = Magick::Image.new(s * w, s * h) {
self.background_color = 'black'
}
icons.sort_by { rand }.each_with_index do |icon,idx|
image = Magick::Image.read(icon).first
i.composite!(image, idx%w * s, idx/w * s, Magick::CopyCompositeOp)
end
i.write('icons.jpeg')
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment