Created
March 28, 2013 21:36
-
-
Save Berlioz/5267024 to your computer and use it in GitHub Desktop.
Cockatrice support for magic card generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby | |
# usage: transformer.rb 20 >> cards.xml, where the integer is the number of pages to hit | |
# dependency: nokogiri, any version written after Hitler was dead | |
require 'open-uri' | |
require 'nokogiri' | |
GENERATOR_URI = "http://www.toothycat.net/wiki/bnf.pl?page=AlexChurchill/MagicCardGenerator" | |
class Card | |
COLORMAP = {'red' => 'R', 'black' => 'B', 'blue' => 'U', 'white' => 'W', 'green' => 'G'} | |
# given a list of lines from the generator website all corresponding to the | |
# same card, instantiate a Card object with all the data stored in the right | |
# places | |
def initialize(lines) | |
# artifacts and lands | |
if lines.first[:type] == :short_typebar | |
@type = lines.first[:text].strip | |
@colors = [] | |
else | |
# split the first line into card type and other metadata | |
text = lines.first[:text] | |
typebar, metadata = text.scan(/(.*?)\s+\((.*?)\)/).first | |
# for whatever reason the generator splits creature types with two spaces | |
@type = typebar.gsub(" ", " ").strip | |
# split metadata into power/toughness and colors | |
@pt = metadata.scan(/\d+\/\d+/).first | |
@colors = metadata.split(",").last.strip.split(" and ") | |
end | |
text_lines = [] | |
lines[1..-1].each do |line| | |
text = line[:text] | |
if line[:type] == :flavor_text | |
@flavor = text | |
elsif line[:type] == :cost | |
@cost = text.scan(/\d+/) | |
else | |
# it's rules text of some sort. Probably. | |
text_lines << pretty(text) | |
end | |
end | |
@text = text_lines.join("\n") | |
end | |
# Handle wrapping the tap and mana cost symbols in {}s | |
# Handle uppercasing the first letter of the line | |
# TODO: fix the case of a mana cost containing only colorless mana | |
# (currently disabled because of confusion with stuff like deal 3 damage) | |
def pretty(text) | |
text[0] = text[0].capitalize | |
words = text.split(" ") | |
new_words = [] | |
words.each do |word| | |
if /\A\d{0,2}(W|U|R|B|G|T)*:?,?.?\z/.match(word) | |
numeric = Integer(word) rescue nil | |
if numeric | |
new_words << word | |
else | |
new_words << word.gsub(/(\d+|W|U|R|B|G|T)/,'{\1}') | |
end | |
else | |
new_words << word | |
end | |
end | |
new_words.join(" ") | |
end | |
# convert this card to an xml node formatted like cockatrice's cards.xml | |
def to_xmlnode(doc, id) | |
node = doc.create_element("card") | |
# create subnodes | |
node.add_child(doc.create_element("name", "GEN#{id}")) | |
node.add_child(doc.create_element("set", "GEN")) | |
@colors.each do |color| | |
node.add_child(doc.create_element("color", COLORMAP[color])) | |
end | |
node.add_child(doc.create_element("manacost", @cost)) | |
node.add_child(doc.create_element("type", @type)) | |
node.add_child(doc.create_element("pt", @pt)) if @pt | |
node.add_child(doc.create_element("tablerow", 0)) | |
node.add_child(doc.create_element("text", @text)) | |
node | |
end | |
end | |
# returns a file object containing one pass of the generator (3 cards) | |
def get_blob | |
open(GENERATOR_URI) | |
end | |
def extract_content(element) | |
if element.is_a? Nokogiri::XML::Text | |
element.text | |
else | |
child_texts = element.children.map{|c| extract_content(c)}.compact | |
child_texts.length == 0 ? nil : child_texts.join("\n") | |
end | |
end | |
def guess_linetype(element, text) | |
if element.name == "i" | |
if text[0..1] == "--" | |
:flavor_text | |
elsif /Converted mana cost: \d+/.match(text) | |
:cost | |
elsif (text[0] == "(" && text[-1] == ")") | |
:reminder_text | |
elsif /(.*?)\s+\((.*?)\)/.match(text) | |
:long_typebar | |
elsif ["Land", "Artifact", "Artifact - Equipment"].include?(text.strip) | |
:short_typebar | |
elsif | |
:keyword_or_unknown | |
end | |
elsif text.include?("- - - - -") | |
:flip_delimiter | |
else | |
:rules_text | |
end | |
end | |
def lex(blob) | |
page = Nokogiri::HTML(blob) | |
tokens = [] | |
# try to pair each line up with our best guess for its type | |
page.xpath("//body").children.each do |element| | |
text = extract_content(element) | |
next if (text.nil? || text == "\n" || text.include?("AlexChurchill")) | |
tokens << {:type => guess_linetype(element, text), :text => text} | |
end | |
# do another walk to resolve planeswalker names and flip cards | |
tokens.each_with_index do |token, i| | |
if token[:type] == :full_typebar && token[:text].include?("Planeswalker") | |
tokens[i - 1][:type] = :planeswalker_name | |
elsif token[:type] == :flip_delimiter | |
tokens[i + 1][:type] = :rules_text | |
end | |
end | |
end | |
def parse(tokens) | |
current_card = [] | |
created_cards = [] | |
tokens.each do |token| | |
next if token[:type] == :planeswalker_name | |
if [:long_typebar, :short_typebar].include?(token[:type]) | |
unless current_card.empty? | |
created_cards << Card.new(current_card) | |
current_card = [] | |
end | |
end | |
current_card << token | |
end | |
created_cards << Card.new(current_card) | |
created_cards | |
end | |
def create_xml_doc(all_cards) | |
doc = Nokogiri::XML::Document.new | |
doc.root = doc.create_element("cockatrice_carddatabase", :version => "2") | |
# init set data | |
sets = doc.create_element("sets") | |
set = doc.create_element("set") | |
setname = doc.create_element("name", "GEN") | |
setlongname = doc.create_element("longname", "BNF Generated Cards") | |
set.add_child(setname) | |
set.add_child(setlongname) | |
sets.add_child(set) | |
doc.root.add_child(sets) | |
# init container for cards | |
cards = doc.create_element("cards") | |
# fill the container | |
id = 0 | |
all_cards.each do |card| | |
new_node = card.to_xmlnode(doc, id) | |
id += 1 | |
cards.add_child(new_node) | |
end | |
doc.root.add_child(cards) | |
doc | |
end | |
def download_set(pages) | |
set = [] | |
pages.times do | |
set << parse(lex(get_blob)) | |
sleep(1) | |
end | |
set.flatten | |
end | |
pages = ARGV[0] ? ARGV[0].to_i : 90 | |
set = download_set(pages) | |
doc = create_xml_doc(set) | |
puts doc.to_s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment