various improvements, added cbow
This commit is contained in:
parent
08dc3944f8
commit
49e386ac4f
9 changed files with 29818 additions and 34 deletions
BIN
data/cbow.bin
Normal file
BIN
data/cbow.bin
Normal file
Binary file not shown.
29488
data/cbow.txt
Normal file
29488
data/cbow.txt
Normal file
File diff suppressed because it is too large
Load diff
|
@ -6529,7 +6529,7 @@
|
||||||
|
|
||||||
|sigil of the new dawn||enchantment||||{^^^WW}|whenever a creature is put into your graveyard from the battlefield, you may pay {^WW}. if you do, return that card to your hand.|
|
|sigil of the new dawn||enchantment||||{^^^WW}|whenever a creature is put into your graveyard from the battlefield, you may pay {^WW}. if you do, return that card to your hand.|
|
||||||
|
|
||||||
|benalish commander||creature||human soldier|*/*|{^^^WW}|countertype % time\@'s power and toughness are each equal to the number of soldiers you control.\suspend x~{XXWWWW}. X can't be &. \whenever a % counter is removed from @ while it's exiled, put a &^/&^ white soldier creature token onto the battlefield.|
|
|benalish commander||creature||human soldier|*/*|{^^^WW}|countertype % time\@'s power and toughness are each equal to the number of soldiers you control.\suspend X~{XXWWWW}. X can't be &. \whenever a % counter is removed from @ while it's exiled, put a &^/&^ white soldier creature token onto the battlefield.|
|
||||||
|
|
||||||
|deluge||instant||||{^^UU}|tap all creatures without flying.|
|
|deluge||instant||||{^^UU}|tap all creatures without flying.|
|
||||||
|
|
||||||
|
@ -8148,7 +8148,7 @@
|
||||||
|
|
||||||
|ventifact bottle||artifact||||{^^^}|countertype % charge\{XX^}, T: put X % counters on @. activate this ability only any time you could cast a sorcery.\at the beginning of your precombat main phase, if @ has a % counter on it, tap it and remove all % counters from it. add {^} to your mana pool for each % counter removed this way.|
|
|ventifact bottle||artifact||||{^^^}|countertype % charge\{XX^}, T: put X % counters on @. activate this ability only any time you could cast a sorcery.\at the beginning of your precombat main phase, if @ has a % counter on it, tap it and remove all % counters from it. add {^} to your mana pool for each % counter removed this way.|
|
||||||
|
|
||||||
|aeon chronicler||creature||avatar|*/*|{^^^UUUU}|countertype % time\@'s power and toughness are each equal to the number of cards in your hand.\suspend x~{XX^^^UU}. X can't be &. \whenever a % counter is removed from @ while it's exiled, draw a card.|
|
|aeon chronicler||creature||avatar|*/*|{^^^UUUU}|countertype % time\@'s power and toughness are each equal to the number of cards in your hand.\suspend X~{XX^^^UU}. X can't be &. \whenever a % counter is removed from @ while it's exiled, draw a card.|
|
||||||
|
|
||||||
|brine seer||creature||human wizard|&^/&^|{^^^UU}|{^^UU}, T: reveal any number of blue cards in your hand. uncast target spell unless its controller pays {^} for each card revealed this way.|
|
|brine seer||creature||human wizard|&^/&^|{^^^UU}|{^^UU}, T: reveal any number of blue cards in your hand. uncast target spell unless its controller pays {^} for each card revealed this way.|
|
||||||
|
|
||||||
|
@ -9290,7 +9290,7 @@
|
||||||
|
|
||||||
|gaea's liege||creature||avatar|*/*|{^^^GGGGGG}|as long as @ isn't attacking, its power and toughness are each equal to the number of forests you control. as long as @ is attacking, its power and toughness are each equal to the number of forests defending player controls.\T: target land becomes a forest until @ leaves the battlefield.|
|
|gaea's liege||creature||avatar|*/*|{^^^GGGGGG}|as long as @ isn't attacking, its power and toughness are each equal to the number of forests you control. as long as @ is attacking, its power and toughness are each equal to the number of forests defending player controls.\T: target land becomes a forest until @ leaves the battlefield.|
|
||||||
|
|
||||||
|swell of courage||instant||||{^^^WWWW}|creatures you control get +&^^/+&^^ until end of turn.\reinforce x~{XXWWWW}|
|
|swell of courage||instant||||{^^^WWWW}|creatures you control get +&^^/+&^^ until end of turn.\reinforce X~{XXWWWW}|
|
||||||
|
|
||||||
|battlegrace angel||creature||angel|&^^^^/&^^^^|{^^^WWWW}|flying\exalted \whenever a creature you control attacks alone, it gains lifelink until end of turn.|
|
|battlegrace angel||creature||angel|&^^^^/&^^^^|{^^^WWWW}|flying\exalted \whenever a creature you control attacks alone, it gains lifelink until end of turn.|
|
||||||
|
|
||||||
|
@ -9919,7 +9919,7 @@
|
||||||
|
|
||||||
|bellowing saddlebrute||creature||orc warrior|&^^^^/&^^^^^|{^^^BB}|raid ~ when @ enters the battlefield, you lose &^^^^ life unless you attacked with a creature this turn.|
|
|bellowing saddlebrute||creature||orc warrior|&^^^^/&^^^^^|{^^^BB}|raid ~ when @ enters the battlefield, you lose &^^^^ life unless you attacked with a creature this turn.|
|
||||||
|
|
||||||
|detritivore||creature||lhurgoyf|*/*|{^^RRRR}|countertype % time\@'s power and toughness are each equal to the number of nonbasic land cards in your opponents' graveyards.\suspend x~{XX^^^RR}. X can't be &. \whenever a % counter is removed from @ while it's exiled, destroy target nonbasic land.|
|
|detritivore||creature||lhurgoyf|*/*|{^^RRRR}|countertype % time\@'s power and toughness are each equal to the number of nonbasic land cards in your opponents' graveyards.\suspend X~{XX^^^RR}. X can't be &. \whenever a % counter is removed from @ while it's exiled, destroy target nonbasic land.|
|
||||||
|
|
||||||
|gruul signet||artifact||||{^^}|{^}, T: add {RRGG} to your mana pool.|
|
|gruul signet||artifact||||{^^}|{^}, T: add {RRGG} to your mana pool.|
|
||||||
|
|
||||||
|
@ -17008,7 +17008,7 @@
|
||||||
|
|
||||||
|viashino grappler||creature||viashino|&^^^/&^|{^^RR}|{GG}: @ gains trample until end of turn.|
|
|viashino grappler||creature||viashino|&^^^/&^|{^^RR}|{GG}: @ gains trample until end of turn.|
|
||||||
|
|
||||||
|fungal behemoth||creature||fungus|*/*|{^^^GG}|countertype % time\@'s power and toughness are each equal to the number of +&^/+&^ counters on creatures you control.\suspend x~{XXGGGG}. X can't be &. \whenever a % counter is removed from @ while it's exiled, you may put a +&^/+&^ counter on target creature.|
|
|fungal behemoth||creature||fungus|*/*|{^^^GG}|countertype % time\@'s power and toughness are each equal to the number of +&^/+&^ counters on creatures you control.\suspend X~{XXGGGG}. X can't be &. \whenever a % counter is removed from @ while it's exiled, you may put a +&^/+&^ counter on target creature.|
|
||||||
|
|
||||||
|defang||enchantment||aura||{^WW}|enchant creature\prevent all damage that would be dealt by enchanted creature.|
|
|defang||enchantment||aura||{^WW}|enchant creature\prevent all damage that would be dealt by enchanted creature.|
|
||||||
|
|
||||||
|
@ -18757,7 +18757,7 @@
|
||||||
|
|
||||||
|arcane lighthouse||land|||||T: add {^} to your mana pool.\{^}, T: until end of turn, creatures your opponents control lose hexproof and shroud and can't have hexproof or shroud.|
|
|arcane lighthouse||land|||||T: add {^} to your mana pool.\{^}, T: until end of turn, creatures your opponents control lose hexproof and shroud and can't have hexproof or shroud.|
|
||||||
|
|
||||||
|roiling horror||creature||horror|*/*|{^^^BBBB}|countertype % time\@'s power and toughness are each equal to your life total minus the life total of an opponent with the most life.\suspend x~{XXBBBBBB}. X can't be &. \whenever a % counter is removed from @ while it's exiled, target player loses &^ life and you gain &^ life.|
|
|roiling horror||creature||horror|*/*|{^^^BBBB}|countertype % time\@'s power and toughness are each equal to your life total minus the life total of an opponent with the most life.\suspend X~{XXBBBBBB}. X can't be &. \whenever a % counter is removed from @ while it's exiled, target player loses &^ life and you gain &^ life.|
|
||||||
|
|
||||||
|illusionary terrain||enchantment||||{UUUU}|cumulative upkeep {^^} \as @ enters the battlefield, choose two basic land types.\basic lands of the first chosen type are the second chosen type.|
|
|illusionary terrain||enchantment||||{UUUU}|cumulative upkeep {^^} \as @ enters the battlefield, choose two basic land types.\basic lands of the first chosen type are the second chosen type.|
|
||||||
|
|
||||||
|
|
31
decode.py
31
decode.py
|
@ -7,8 +7,10 @@ sys.path.append(libdir)
|
||||||
import utils
|
import utils
|
||||||
import jdecode
|
import jdecode
|
||||||
import cardlib
|
import cardlib
|
||||||
|
from cbow import CBOW
|
||||||
|
|
||||||
def main(fname, oname = None, verbose = True, gatherer = False, for_forum = False):
|
def main(fname, oname = None, verbose = True,
|
||||||
|
gatherer = False, for_forum = False, creativity = False):
|
||||||
cards = []
|
cards = []
|
||||||
valid = 0
|
valid = 0
|
||||||
invalid = 0
|
invalid = 0
|
||||||
|
@ -51,17 +53,28 @@ def main(fname, oname = None, verbose = True, gatherer = False, for_forum = Fals
|
||||||
print (str(valid) + ' valid, ' + str(invalid) + ' invalid, '
|
print (str(valid) + ' valid, ' + str(invalid) + ' invalid, '
|
||||||
+ str(unparsed) + ' failed to parse.')
|
+ str(unparsed) + ' failed to parse.')
|
||||||
|
|
||||||
|
if creativity:
|
||||||
|
cbow = CBOW()
|
||||||
|
|
||||||
|
def writecards(writer):
|
||||||
|
for card in cards:
|
||||||
|
writer.write((card.format(gatherer = gatherer, for_forum = for_forum)).encode('utf-8'))
|
||||||
|
if creativity:
|
||||||
|
writer.write('~~ closest cards ~~\n'.encode('utf-8'))
|
||||||
|
nearest = cbow.nearest(card)
|
||||||
|
for dist, cardname in nearest:
|
||||||
|
if for_forum:
|
||||||
|
cardname = '[card]' + cardname + '[/card]'
|
||||||
|
writer.write((cardname + ': ' + str(dist) + '\n').encode('utf-8'))
|
||||||
|
writer.write('\n'.encode('utf-8'))
|
||||||
|
|
||||||
if oname:
|
if oname:
|
||||||
if verbose:
|
if verbose:
|
||||||
print 'Writing output to: ' + oname
|
print 'Writing output to: ' + oname
|
||||||
with open(oname, 'w') as ofile:
|
with open(oname, 'w') as ofile:
|
||||||
for card in cards:
|
writecards(ofile)
|
||||||
ofile.write((card.format(gatherer = gatherer, for_forum = for_forum)
|
|
||||||
+ '\n').encode('utf-8'))
|
|
||||||
else:
|
else:
|
||||||
for card in cards:
|
writecards(sys.stdout)
|
||||||
sys.stdout.write((card.format(gatherer = gatherer, for_forum = for_forum)
|
|
||||||
+ '\n').encode('utf-8'))
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,10 +90,12 @@ if __name__ == '__main__':
|
||||||
help='emulate Gatherer visual spoiler')
|
help='emulate Gatherer visual spoiler')
|
||||||
parser.add_argument('-f', '--forum', action='store_true',
|
parser.add_argument('-f', '--forum', action='store_true',
|
||||||
help='use pretty mana encoding for mtgsalvation forum')
|
help='use pretty mana encoding for mtgsalvation forum')
|
||||||
|
parser.add_argument('-c', '--creativity', action='store_true',
|
||||||
|
help='use CBOW fuzzy matching to check creativity of cards')
|
||||||
parser.add_argument('-v', '--verbose', action='store_true',
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
help='verbose output')
|
help='verbose output')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
main(args.infile, args.outfile, verbose = args.verbose,
|
main(args.infile, args.outfile, verbose = args.verbose,
|
||||||
gatherer = args.gatherer, for_forum = args.forum)
|
gatherer = args.gatherer, for_forum = args.forum, creativity = args.creativity)
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
40
encode.py
40
encode.py
|
@ -29,7 +29,9 @@ def main(fname, oname = None, verbose = True, dupes = 0, encoding = 'std', stabl
|
||||||
final_sep = True
|
final_sep = True
|
||||||
|
|
||||||
# set the properties of the encoding
|
# set the properties of the encoding
|
||||||
if encoding in ['std']:
|
if encoding in ['vec']:
|
||||||
|
pass
|
||||||
|
elif encoding in ['std']:
|
||||||
if dupes == 0:
|
if dupes == 0:
|
||||||
dupes = 1
|
dupes = 1
|
||||||
elif encoding in ['rmana']:
|
elif encoding in ['rmana']:
|
||||||
|
@ -125,29 +127,27 @@ def main(fname, oname = None, verbose = True, dupes = 0, encoding = 'std', stabl
|
||||||
random.seed(1371367)
|
random.seed(1371367)
|
||||||
random.shuffle(cards)
|
random.shuffle(cards)
|
||||||
|
|
||||||
|
def writecards(writer):
|
||||||
|
for card in cards:
|
||||||
|
if encoding in ['vec']:
|
||||||
|
writer.write(card.vectorize() + '\n\n')
|
||||||
|
else:
|
||||||
|
writer.write(card.encode(fmt_ordered = fmt_ordered,
|
||||||
|
fmt_labeled = fmt_labeled,
|
||||||
|
fieldsep = fieldsep,
|
||||||
|
randomize_fields = randomize_fields,
|
||||||
|
randomize_mana = randomize_mana,
|
||||||
|
initial_sep = initial_sep,
|
||||||
|
final_sep = final_sep)
|
||||||
|
+ utils.cardsep)
|
||||||
|
|
||||||
if oname:
|
if oname:
|
||||||
if verbose:
|
if verbose:
|
||||||
print 'Writing output to: ' + oname
|
print 'Writing output to: ' + oname
|
||||||
with open(oname, 'w') as ofile:
|
with open(oname, 'w') as ofile:
|
||||||
for card in cards:
|
writecards(ofile)
|
||||||
ofile.write(card.encode(fmt_ordered = fmt_ordered,
|
|
||||||
fmt_labeled = fmt_labeled,
|
|
||||||
fieldsep = fieldsep,
|
|
||||||
randomize_fields = randomize_fields,
|
|
||||||
randomize_mana = randomize_mana,
|
|
||||||
initial_sep = initial_sep,
|
|
||||||
final_sep = final_sep)
|
|
||||||
+ utils.cardsep)
|
|
||||||
else:
|
else:
|
||||||
for card in cards:
|
writecards(sys.stdout)
|
||||||
sys.stdout.write(card.encode(fmt_ordered = fmt_ordered,
|
|
||||||
fmt_labeled = fmt_labeled,
|
|
||||||
fieldsep = fieldsep,
|
|
||||||
randomize_fields = randomize_fields,
|
|
||||||
randomize_mana = randomize_mana,
|
|
||||||
initial_sep = initial_sep,
|
|
||||||
final_sep = final_sep)
|
|
||||||
+ utils.cardsep)
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ if __name__ == '__main__':
|
||||||
parser.add_argument('-d', '--duplicate', metavar='N', type=int, default=0,
|
parser.add_argument('-d', '--duplicate', metavar='N', type=int, default=0,
|
||||||
help='number of times to duplicate each card')
|
help='number of times to duplicate each card')
|
||||||
parser.add_argument('-e', '--encoding', default='std',
|
parser.add_argument('-e', '--encoding', default='std',
|
||||||
choices=['std', 'rmana', 'rmana_dual', 'rfields'])
|
choices=['std', 'rmana', 'rmana_dual', 'rfields', 'vec'])
|
||||||
parser.add_argument('-s', '--stable', action='store_true',
|
parser.add_argument('-s', '--stable', action='store_true',
|
||||||
help="don't randomize the order of the cards")
|
help="don't randomize the order of the cards")
|
||||||
parser.add_argument('-v', '--verbose', action='store_true',
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
|
|
|
@ -6,6 +6,32 @@ import utils
|
||||||
import transforms
|
import transforms
|
||||||
from manalib import Manacost, Manatext
|
from manalib import Manacost, Manatext
|
||||||
|
|
||||||
|
# Some text prettification stuff that people may not have installed
|
||||||
|
try:
|
||||||
|
from titlecase import titlecase
|
||||||
|
except ImportError:
|
||||||
|
def titlecase(s):
|
||||||
|
return s.title()
|
||||||
|
|
||||||
|
try:
|
||||||
|
import textwrap
|
||||||
|
import nltk.data
|
||||||
|
sent_tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
|
||||||
|
# This crazy thing is actually invoked as an unpass, so newlines are still
|
||||||
|
# encoded.
|
||||||
|
def sentencecase(s):
|
||||||
|
s = s.replace(utils.x_marker, utils.reserved_marker)
|
||||||
|
lines = s.split(utils.newline)
|
||||||
|
clines = []
|
||||||
|
for line in lines:
|
||||||
|
if line:
|
||||||
|
sentences = sent_tokenizer.tokenize(line)
|
||||||
|
clines += [' '.join([sent.capitalize() for sent in sentences])]
|
||||||
|
return utils.newline.join(clines).replace(utils.reserved_marker, utils.x_marker)
|
||||||
|
except ImportError:
|
||||||
|
def sentencecase(s):
|
||||||
|
return s
|
||||||
|
|
||||||
# These are used later to determine what the fields of the Card object are called.
|
# These are used later to determine what the fields of the Card object are called.
|
||||||
# Define them here because they have nothing to do with the actual format.
|
# Define them here because they have nothing to do with the actual format.
|
||||||
field_name = 'name'
|
field_name = 'name'
|
||||||
|
@ -495,7 +521,7 @@ class Card:
|
||||||
def format(self, gatherer = False, for_forum = False):
|
def format(self, gatherer = False, for_forum = False):
|
||||||
outstr = ''
|
outstr = ''
|
||||||
if gatherer:
|
if gatherer:
|
||||||
cardname = self.__dict__[field_name].title()
|
cardname = titlecase(self.__dict__[field_name])
|
||||||
if not cardname:
|
if not cardname:
|
||||||
cardname = '_NONAME_'
|
cardname = '_NONAME_'
|
||||||
if for_forum:
|
if for_forum:
|
||||||
|
@ -516,11 +542,11 @@ class Card:
|
||||||
|
|
||||||
outstr += '\n'
|
outstr += '\n'
|
||||||
|
|
||||||
basetypes = self.__dict__[field_types]
|
basetypes = map(str.capitalize, self.__dict__[field_types])
|
||||||
if len(basetypes) < 1:
|
if len(basetypes) < 1:
|
||||||
basetypes = ['_NOTYPE_']
|
basetypes = ['_NOTYPE_']
|
||||||
|
|
||||||
outstr += ' '.join(self.__dict__[field_supertypes] + basetypes)
|
outstr += ' '.join(map(str.capitalize, self.__dict__[field_supertypes]) + basetypes)
|
||||||
|
|
||||||
if self.__dict__[field_subtypes]:
|
if self.__dict__[field_subtypes]:
|
||||||
outstr += (' ' + utils.dash_marker + ' ' +
|
outstr += (' ' + utils.dash_marker + ' ' +
|
||||||
|
@ -540,6 +566,7 @@ class Card:
|
||||||
mtext = transforms.text_unpass_2_counters(mtext)
|
mtext = transforms.text_unpass_2_counters(mtext)
|
||||||
mtext = transforms.text_unpass_3_unary(mtext)
|
mtext = transforms.text_unpass_3_unary(mtext)
|
||||||
mtext = transforms.text_unpass_4_symbols(mtext, for_forum)
|
mtext = transforms.text_unpass_4_symbols(mtext, for_forum)
|
||||||
|
mtext = sentencecase(mtext)
|
||||||
mtext = transforms.text_unpass_5_cardname(mtext, cardname)
|
mtext = transforms.text_unpass_5_cardname(mtext, cardname)
|
||||||
mtext = transforms.text_unpass_6_newlines(mtext)
|
mtext = transforms.text_unpass_6_newlines(mtext)
|
||||||
newtext = Manatext('')
|
newtext = Manatext('')
|
||||||
|
@ -615,3 +642,41 @@ class Card:
|
||||||
outstr += self.bside.format(gatherer = gatherer, for_forum = for_forum)
|
outstr += self.bside.format(gatherer = gatherer, for_forum = for_forum)
|
||||||
|
|
||||||
return outstr
|
return outstr
|
||||||
|
|
||||||
|
def vectorize(self):
|
||||||
|
ld = '('
|
||||||
|
rd = ')'
|
||||||
|
outstr = ''
|
||||||
|
|
||||||
|
if self.__dict__[field_rarity]:
|
||||||
|
outstr += ld + self.__dict__[field_rarity] + rd + ' '
|
||||||
|
|
||||||
|
coststr = self.__dict__[field_cost].vectorize(delimit = True)
|
||||||
|
if coststr:
|
||||||
|
outstr += coststr + ' '
|
||||||
|
|
||||||
|
typestr = ' '.join(map(lambda s: '(' + s + ')',
|
||||||
|
self.__dict__[field_supertypes] + self.__dict__[field_types]))
|
||||||
|
if typestr:
|
||||||
|
outstr += typestr + ' '
|
||||||
|
|
||||||
|
if self.__dict__[field_subtypes]:
|
||||||
|
outstr += ' '.join(self.__dict__[field_subtypes]) + ' '
|
||||||
|
|
||||||
|
if self.__dict__[field_pt]:
|
||||||
|
outstr += ' '.join(map(lambda s: '(' + s + ')',
|
||||||
|
self.__dict__[field_pt].replace('/', '/ /').split()))
|
||||||
|
outstr += ' '
|
||||||
|
|
||||||
|
if self.__dict__[field_loyalty]:
|
||||||
|
outstr += '((' + self.__dict__[field_loyalty] + ')) '
|
||||||
|
|
||||||
|
outstr += self.__dict__[field_text].vectorize()
|
||||||
|
|
||||||
|
if self.bside:
|
||||||
|
outstr = '_ASIDE_ ' + outstr + '\n\n_BSIDE_ ' + self.bside.vectorize()
|
||||||
|
|
||||||
|
return outstr
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
181
lib/cbow.py
Normal file
181
lib/cbow.py
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
# Infinite thanks to Talcos from the mtgsalvation forums, who among
|
||||||
|
# many, many other things wrote the original version of this code.
|
||||||
|
# I have merely ported it to fit my needs.
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
import math
|
||||||
|
import utils
|
||||||
|
import cardlib
|
||||||
|
import transforms
|
||||||
|
|
||||||
|
# # this would be nice, but doing it naively makes things worse
|
||||||
|
# from joblib import Parallel, delayed
|
||||||
|
# import multiprocessing
|
||||||
|
|
||||||
|
libdir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
datadir = os.path.realpath(os.path.join(libdir, '../data'))
|
||||||
|
|
||||||
|
# # multithreading control parameters
|
||||||
|
# cores = multiprocessing.cpu_count()
|
||||||
|
# segments = cores / 2 if cores / 2 > 0 else 1
|
||||||
|
|
||||||
|
# max length of vocabulary entries
|
||||||
|
max_w = 50
|
||||||
|
|
||||||
|
|
||||||
|
#### snip! ####
|
||||||
|
|
||||||
|
def read_vector_file(fname):
|
||||||
|
with open(fname, 'rb') as f:
|
||||||
|
words = int(f.read(4))
|
||||||
|
size = int(f.read(4))
|
||||||
|
vocab = [' '] * (words * max_w)
|
||||||
|
M = []
|
||||||
|
for b in range(0,words):
|
||||||
|
a = 0
|
||||||
|
while True:
|
||||||
|
c = f.read(1)
|
||||||
|
vocab[b * max_w + a] = c;
|
||||||
|
if len(c) == 0 or c == ' ':
|
||||||
|
break
|
||||||
|
if (a < max_w) and vocab[b * max_w + a] != '\n':
|
||||||
|
a += 1
|
||||||
|
tmp = list(struct.unpack('f'*size,f.read(4 * size)))
|
||||||
|
length = math.sqrt(sum([tmp[i] * tmp[i] for i in range(0,len(tmp))]))
|
||||||
|
for i in range(0,len(tmp)):
|
||||||
|
tmp[i] /= length
|
||||||
|
M.append(tmp)
|
||||||
|
return ((''.join(vocab)).split(),M)
|
||||||
|
|
||||||
|
def makevector(vocabulary,vecs,sequence):
|
||||||
|
words = sequence.split()
|
||||||
|
indices = []
|
||||||
|
for word in words:
|
||||||
|
if word not in vocabulary:
|
||||||
|
#print("Missing word in vocabulary: " + word)
|
||||||
|
continue
|
||||||
|
#return [0.0]*len(vecs[0])
|
||||||
|
indices.append(vocabulary.index(word))
|
||||||
|
#res = map(sum,[vecs[i] for i in indices])
|
||||||
|
res = None
|
||||||
|
for v in [vecs[i] for i in indices]:
|
||||||
|
if res == None:
|
||||||
|
res = v
|
||||||
|
else:
|
||||||
|
res = [x + y for x, y in zip(res,v)]
|
||||||
|
length = math.sqrt(sum([res[i] * res[i] for i in range(0,len(res))]))
|
||||||
|
for i in range(0,len(res)):
|
||||||
|
res[i] /= length
|
||||||
|
return res
|
||||||
|
|
||||||
|
#### !snip ####
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import numpy
|
||||||
|
def cosine_similarity(v1,v2):
|
||||||
|
A = numpy.array([v1,v2])
|
||||||
|
|
||||||
|
# from http://stackoverflow.com/questions/17627219/whats-the-fastest-way-in-python-to-calculate-cosine-similarity-given-sparse-mat
|
||||||
|
|
||||||
|
# base similarity matrix (all dot products)
|
||||||
|
# replace this with A.dot(A.T).todense() for sparse representation
|
||||||
|
similarity = numpy.dot(A, A.T)
|
||||||
|
|
||||||
|
# squared magnitude of preference vectors (number of occurrences)
|
||||||
|
square_mag = numpy.diag(similarity)
|
||||||
|
|
||||||
|
# inverse squared magnitude
|
||||||
|
inv_square_mag = 1 / square_mag
|
||||||
|
|
||||||
|
# if it doesn't occur, set it's inverse magnitude to zero (instead of inf)
|
||||||
|
inv_square_mag[numpy.isinf(inv_square_mag)] = 0
|
||||||
|
|
||||||
|
# inverse of the magnitude
|
||||||
|
inv_mag = numpy.sqrt(inv_square_mag)
|
||||||
|
|
||||||
|
# cosine similarity (elementwise multiply by inverse magnitudes)
|
||||||
|
cosine = similarity * inv_mag
|
||||||
|
cosine = cosine.T * inv_mag
|
||||||
|
|
||||||
|
return cosine[0][1]
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
def cosine_similarity(v1,v2):
|
||||||
|
#compute cosine similarity of v1 to v2: (v1 dot v1)/{||v1||*||v2||)
|
||||||
|
sumxx, sumxy, sumyy = 0, 0, 0
|
||||||
|
for i in range(len(v1)):
|
||||||
|
x = v1[i]; y = v2[i]
|
||||||
|
sumxx += x*x
|
||||||
|
sumyy += y*y
|
||||||
|
sumxy += x*y
|
||||||
|
return sumxy/math.sqrt(sumxx*sumyy)
|
||||||
|
|
||||||
|
def cosine_similarity_name(cardvec, v, name):
|
||||||
|
return (cosine_similarity(cardvec, v), name)
|
||||||
|
|
||||||
|
|
||||||
|
class CBOW:
|
||||||
|
def __init__(self, verbose = True,
|
||||||
|
vector_fname = os.path.join(datadir, 'cbow.bin'),
|
||||||
|
card_fname = os.path.join(datadir, 'output.txt')):
|
||||||
|
self.verbose = verbose
|
||||||
|
self.cardvecs = []
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print 'Building a cbow model...'
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print ' Reading binary vector data from: ' + vector_fname
|
||||||
|
(vocab, vecs) = read_vector_file(vector_fname)
|
||||||
|
self.vocab = vocab
|
||||||
|
self.vecs = vecs
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print ' Reading encoded cards from: ' + card_fname
|
||||||
|
print ' They\'d better be in the same order as the file used to build the vector model!'
|
||||||
|
with open(card_fname, 'rt') as f:
|
||||||
|
text = f.read()
|
||||||
|
for card_src in text.split(utils.cardsep):
|
||||||
|
if card_src:
|
||||||
|
card = cardlib.Card(card_src)
|
||||||
|
name = card.name
|
||||||
|
self.cardvecs += [(name, makevector(self.vocab,
|
||||||
|
self.vecs,
|
||||||
|
card.vectorize()))]
|
||||||
|
|
||||||
|
# self.par = Parallel(n_jobs=segments)
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print '... Done.'
|
||||||
|
print ' vocab size: ' + str(len(self.vocab))
|
||||||
|
print ' raw vecs: ' + str(len(self.vecs))
|
||||||
|
print ' card vecs: ' + str(len(self.cardvecs))
|
||||||
|
|
||||||
|
def nearest(self, card, n=5):
|
||||||
|
if isinstance(card, cardlib.Card):
|
||||||
|
words = card.vectorize().split('\n\n')[0]
|
||||||
|
else:
|
||||||
|
# assume it's a string (that's already a vector)
|
||||||
|
words = card
|
||||||
|
|
||||||
|
if not words:
|
||||||
|
return []
|
||||||
|
|
||||||
|
cardvec = makevector(self.vocab, self.vecs, words)
|
||||||
|
|
||||||
|
comparisons = [cosine_similarity_name(cardvec, v, name) for (name, v) in self.cardvecs]
|
||||||
|
# comparisons = self.par(delayed(cosine_similarity_name)(cardvec, v, name)
|
||||||
|
# for (name, v) in self.cardvecs)
|
||||||
|
|
||||||
|
comparisons.sort(reverse = True)
|
||||||
|
comp_n = comparisons[:n]
|
||||||
|
|
||||||
|
if isinstance(card, cardlib.Card) and card.bside:
|
||||||
|
comp_n += self.nearest(card.bside)
|
||||||
|
|
||||||
|
return comp_n
|
|
@ -126,6 +126,18 @@ class Manacost:
|
||||||
else:
|
else:
|
||||||
return utils.mana_open_delimiter + ''.join(self.sequence) + utils.mana_close_delimiter
|
return utils.mana_open_delimiter + ''.join(self.sequence) + utils.mana_close_delimiter
|
||||||
|
|
||||||
|
def vectorize(self, delimit = False):
|
||||||
|
if self.none:
|
||||||
|
return ''
|
||||||
|
elif delimit:
|
||||||
|
ld = '('
|
||||||
|
rd = ')'
|
||||||
|
else:
|
||||||
|
ld = ''
|
||||||
|
rd = ''
|
||||||
|
return ' '.join(map(lambda s: ld + s + rd, self.sequence))
|
||||||
|
|
||||||
|
|
||||||
class Manatext:
|
class Manatext:
|
||||||
'''text representation with embedded mana costs'''
|
'''text representation with embedded mana costs'''
|
||||||
|
|
||||||
|
@ -176,3 +188,25 @@ class Manatext:
|
||||||
for cost in self.costs:
|
for cost in self.costs:
|
||||||
text = text.replace(utils.reserved_mana_marker, cost.encode(randomize = randomize), 1)
|
text = text.replace(utils.reserved_mana_marker, cost.encode(randomize = randomize), 1)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
def vectorize(self):
|
||||||
|
text = self.text
|
||||||
|
special_chars = [utils.reserved_mana_marker,
|
||||||
|
utils.dash_marker,
|
||||||
|
utils.bullet_marker,
|
||||||
|
utils.this_marker,
|
||||||
|
utils.counter_marker,
|
||||||
|
utils.choice_open_delimiter,
|
||||||
|
utils.choice_close_delimiter,
|
||||||
|
utils.newline,
|
||||||
|
#utils.x_marker,
|
||||||
|
utils.tap_marker,
|
||||||
|
utils.untap_marker,
|
||||||
|
utils.newline,
|
||||||
|
';', ':', '"', ',', '.']
|
||||||
|
for char in special_chars:
|
||||||
|
text = text.replace(char, ' ' + char + ' ')
|
||||||
|
text = text.replace('/', '/ /')
|
||||||
|
for cost in self.costs:
|
||||||
|
text = text.replace(utils.reserved_mana_marker, cost.vectorize(), 1)
|
||||||
|
return ' '.join(text.split())
|
||||||
|
|
|
@ -121,6 +121,7 @@ def text_pass_4b_x(s):
|
||||||
s = s.replace(' x ', ' ' + x_marker + ' ')
|
s = s.replace(' x ', ' ' + x_marker + ' ')
|
||||||
s = s.replace('x:', x_marker + ':')
|
s = s.replace('x:', x_marker + ':')
|
||||||
s = s.replace('x~', x_marker + '~')
|
s = s.replace('x~', x_marker + '~')
|
||||||
|
s = s.replace(u'x\u2014', x_marker + u'\u2014')
|
||||||
s = s.replace('x.', x_marker + '.')
|
s = s.replace('x.', x_marker + '.')
|
||||||
s = s.replace('x,', x_marker + ',')
|
s = s.replace('x,', x_marker + ',')
|
||||||
s = s.replace('x/x', x_marker + '/' + x_marker)
|
s = s.replace('x/x', x_marker + '/' + x_marker)
|
||||||
|
|
Loading…
Reference in a new issue