added html visual spoilers for nearest cards / names (creativity metrics)
This commit is contained in:
parent
2ebb15cb28
commit
2cd8b03249
8 changed files with 68 additions and 22 deletions
|
@ -4,7 +4,7 @@ Utilities to assist in the process of generating Magic the Gathering cards with
|
|||
|
||||
http://www.mtgsalvation.com/forums/creativity/custom-card-creation/612057-generating-magic-cards-using-deep-recurrent-neural
|
||||
|
||||
The purpose of this code is mostly to wrangle text between various human and machine readable formats. The original input comes from [mtgjson](http://mtgjson.com); this is filtered and reduced to one of several input formats intended for neural network training, such as the standard encoded format used in [data/output.txt](https://github.com/billzorn/mtgencode/blob/master/data/output.txt). Any json or encoded data, including output from appropriately trained neural nets, can then be interpreted as cards and decoded to a human readable format, such as a text spoiler or [Magic Set Editor 2](http://magicseteditor.sourceforge.net) set file.
|
||||
The purpose of this code is mostly to wrangle text between various human and machine readable formats. The original input comes from [mtgjson](http://mtgjson.com); this is filtered and reduced to one of several input formats intended for neural network training, such as the standard encoded format used in [data/output.txt](https://github.com/billzorn/mtgencode/blob/master/data/output.txt). Any json or encoded data, including output from appropriately trained neural nets, can then be interpreted as cards and decoded to a human readable format, such as a text spoiler, [Magic Set Editor 2](http://magicseteditor.sourceforge.net) set file, or a pretty, portable html file that can be viewed in any browser.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
@ -57,7 +57,7 @@ custom | Blank format slot, inteded to help users add their own formats to t
|
|||
|
||||
```
|
||||
usage: decode.py [-h] [-e {std,named,noname,rfields,old,norarity,vec,custom}]
|
||||
[-g] [-f] [-c] [-d] [-v] [-mse]
|
||||
[-g] [-f] [-c] [-d] [-v] [-mse] [-html]
|
||||
infile [outfile]
|
||||
|
||||
positional arguments:
|
||||
|
@ -75,13 +75,14 @@ optional arguments:
|
|||
-v, --verbose verbose output
|
||||
-mse, --mse use Magic Set Editor 2 encoding; will output as .mse-
|
||||
set file
|
||||
-html, --html create a .html file with pretty forum formatting
|
||||
```
|
||||
|
||||
The default output is a text spoiler which modifies the output of the neural net as little as possible while making it human readable. Specifying the -g option will produce a prettier, Gatherer-inspired text spoiler with heavier-weight transformations applied to the text, such as capitalization. The -f option encodes mana symbols in the format used by the mtgsalvation forum; this is useful if you want to cut and paste your spoiler into a post to share it.
|
||||
|
||||
Passing the -mse option will cause decode.py to produce both the hilarious internal MSE text format as well as an actual mse set file, which is really just a renamed zip archive. The -f and -g flags will be respected in the text that is dumped to each card's notes field.
|
||||
|
||||
Finally, the -c and -d options will print out additional data about the quality of the cards. Running with -c is extremely slow due to the massive amount of computation involved; -d is probably a good idea to use in general unless you're trying to produce pretty output to show off.
|
||||
Finally, the -c and -d options will print out additional data about the quality of the cards. Running with -c is extremely slow due to the massive amount of computation involved, though at least we can do it in parallel over all of your processor cores; -d is probably a good idea to use in general unless you're trying to produce pretty output to show off. Using html mode is especially useful with -c as we can link to visual spoilers from magiccards.info.
|
||||
|
||||
### Examples
|
||||
|
||||
|
|
60
decode.py
60
decode.py
|
@ -12,9 +12,6 @@ import cardlib
|
|||
from cbow import CBOW
|
||||
from namediff import Namediff
|
||||
|
||||
def exclude_sets(cardset):
|
||||
return cardset == 'Unglued' or cardset == 'Unhinged' or cardset == 'Celebration'
|
||||
|
||||
def main(fname, oname = None, verbose = True, encoding = 'std',
|
||||
gatherer = False, for_forum = False, for_mse = False,
|
||||
creativity = False, vdump = False, for_html = False):
|
||||
|
@ -52,8 +49,36 @@ def main(fname, oname = None, verbose = True, encoding = 'std',
|
|||
cards = jdecode.mtg_open_file(fname, verbose=verbose, fmt_ordered=fmt_ordered)
|
||||
|
||||
if creativity:
|
||||
cbow = CBOW()
|
||||
namediff = Namediff()
|
||||
cbow = CBOW()
|
||||
if verbose:
|
||||
print 'Computing nearest names...'
|
||||
nearest_names = namediff.nearest_par(map(lambda c: c.name, cards), n=3)
|
||||
if verbose:
|
||||
print 'Computing nearest cards...'
|
||||
nearest_cards = cbow.nearest_par(cards)
|
||||
for i in range(0, len(cards)):
|
||||
cards[i].nearest_names = nearest_names[i]
|
||||
cards[i].nearest_cards = nearest_cards[i]
|
||||
if verbose:
|
||||
print '...Done.'
|
||||
|
||||
def hoverimg(cardname, dist, nd):
|
||||
truename = nd.names[cardname]
|
||||
code = nd.codes[cardname]
|
||||
namestr = ''
|
||||
if for_html:
|
||||
if code:
|
||||
namestr = ('<div class="hover_img"><a href="#">' + truename
|
||||
+ '<span><img src="http://magiccards.info/scans/en/' + code
|
||||
+ '" alt="image"/></span></a>' + ': ' + str(dist) + '</div>')
|
||||
else:
|
||||
namestr = '<div>' + truename + ': ' + str(dist) + '</div>'
|
||||
elif for_forum:
|
||||
namestr = '[card]' + truename + '[/card]' + ': ' + str(dist) + '\n'
|
||||
else:
|
||||
namestr = truename + ': ' + str(dist) + '\n'
|
||||
return namestr
|
||||
|
||||
def writecards(writer):
|
||||
if for_mse:
|
||||
|
@ -78,27 +103,26 @@ def main(fname, oname = None, verbose = True, encoding = 'std',
|
|||
fstring = fstring.replace('<', '(').replace('>', ')')
|
||||
writer.write(('\n' + fstring[:-1]).replace('\n', '\n\t\t'))
|
||||
else:
|
||||
writer.write((card.format(gatherer = gatherer, for_forum = for_forum,
|
||||
vdump = vdump, for_html = for_html) + '\n').encode('utf-8'))
|
||||
fstring = card.format(gatherer = gatherer, for_forum = for_forum,
|
||||
vdump = vdump, for_html = for_html)
|
||||
if creativity and for_html:
|
||||
fstring = fstring[:-6] # chop off the closing </div> to stick stuff in
|
||||
writer.write((fstring + '\n').encode('utf-8'))
|
||||
|
||||
if creativity:
|
||||
cstring = '~~ closest cards ~~\n'
|
||||
nearest = cbow.nearest(card)
|
||||
nearest = card.nearest_cards
|
||||
for dist, cardname in nearest:
|
||||
cardname = namediff.names[cardname]
|
||||
if for_forum:
|
||||
cardname = '[card]' + cardname + '[/card]'
|
||||
cstring += cardname + ': ' + str(dist) + '\n'
|
||||
cstring += hoverimg(cardname, dist, namediff)
|
||||
cstring += '~~ closest names ~~\n'
|
||||
nearest = namediff.nearest(card.name)
|
||||
nearest = card.nearest_names
|
||||
for dist, cardname in nearest:
|
||||
cardname = namediff.names[cardname]
|
||||
if for_forum:
|
||||
cardname = '[card]' + cardname + '[/card]'
|
||||
cstring += cardname + ': ' + str(dist) + '\n'
|
||||
if for_mse:
|
||||
cstring = cstring.replace('<', '(').replace('>', ')')
|
||||
cstring += hoverimg(cardname, dist, namediff)
|
||||
if for_html:
|
||||
cstring = '<hr><div>' + cstring.replace('\n', '<br>\n') + '</div>\n</div>'
|
||||
elif for_mse:
|
||||
cstring = ('\n\n' + cstring[:-1]).replace('\n', '\n\t\t')
|
||||
|
||||
writer.write(cstring.encode('utf-8'))
|
||||
|
||||
writer.write('\n'.encode('utf-8'))
|
||||
|
|
|
@ -399,6 +399,9 @@ class Card:
|
|||
# flags
|
||||
self.parsed = True
|
||||
self.valid = True # doesn't record that much
|
||||
# placeholders to fill in with expensive distance metrics
|
||||
self.nearest_names = []
|
||||
self.nearest_cards = []
|
||||
# default values for all fields
|
||||
self.__dict__[field_name] = ''
|
||||
self.__dict__[field_rarity] = ''
|
||||
|
|
|
@ -61,3 +61,4 @@ field_label_text = '9'
|
|||
# additional fields we add to the json cards
|
||||
json_field_bside = 'bside'
|
||||
json_field_set_name = 'setName'
|
||||
json_field_info_code = 'magicCardsInfoCode'
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,9 +15,14 @@ def mtg_open_json(fname, verbose = False):
|
|||
for k_set in jobj:
|
||||
set = jobj[k_set]
|
||||
setname = set['name']
|
||||
if 'magicCardsInfoCode' in set:
|
||||
codename = set['magicCardsInfoCode']
|
||||
else:
|
||||
codename = ''
|
||||
|
||||
for card in set['cards']:
|
||||
card[utils.json_field_set_name] = setname
|
||||
card[utils.json_field_info_code] = codename
|
||||
|
||||
cardnumber = None
|
||||
if 'number' in card:
|
||||
|
|
|
@ -6,6 +6,7 @@ import difflib
|
|||
import os
|
||||
import multiprocessing
|
||||
|
||||
import utils
|
||||
import jdecode
|
||||
import cardlib
|
||||
|
||||
|
@ -54,6 +55,7 @@ class Namediff:
|
|||
json_fname = os.path.join(datadir, 'AllSets.json')):
|
||||
self.verbose = verbose
|
||||
self.names = {}
|
||||
self.codes = {}
|
||||
|
||||
if self.verbose:
|
||||
print 'Setting up namediff...'
|
||||
|
@ -71,11 +73,20 @@ class Namediff:
|
|||
card = cardlib.Card(jcards[idx])
|
||||
name = card.name
|
||||
jname = jcards[idx]['name']
|
||||
jcode = jcards[idx][utils.json_field_info_code]
|
||||
if 'number' in jcards[idx]:
|
||||
jnum = jcards[idx]['number']
|
||||
else:
|
||||
jnum = ''
|
||||
|
||||
if name in self.names:
|
||||
print ' Duplicate name ' + name + ', ignoring.'
|
||||
else:
|
||||
self.names[name] = jname
|
||||
if jcode and jnum:
|
||||
self.codes[name] = jcode + '/' + jnum + '.jpg'
|
||||
else:
|
||||
self.codes[name] = ''
|
||||
namecount += 1
|
||||
|
||||
print ' Read ' + str(namecount) + ' unique cardnames'
|
||||
|
|
|
@ -78,6 +78,7 @@ field_label_text = config.field_label_text
|
|||
# additional fields we add to the json cards
|
||||
json_field_bside = config.json_field_bside
|
||||
json_field_set_name = config.json_field_set_name
|
||||
json_field_info_code = config.json_field_info_code
|
||||
|
||||
# unicode / ascii conversion
|
||||
unicode_trans = {
|
||||
|
|
Loading…
Reference in a new issue