mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
Some love for the interface & tests
This commit is contained in:
parent
b27187e99a
commit
11d803fd63
2 changed files with 121 additions and 80 deletions
|
@ -1,62 +1,82 @@
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
from pokedex.db import connect
|
from pokedex.db import connect
|
||||||
from pokedex.util.movesets import main
|
from pokedex.util.movesets import main
|
||||||
|
|
||||||
|
testcase_args = u"""
|
||||||
|
NO muk
|
||||||
|
NO beedrill rage pursuit agility endeavor toxic
|
||||||
|
NO ditto psystrike aeroblast mist-ball judgment
|
||||||
|
OK metapod tackle harden
|
||||||
|
OK lugia aeroblast punishment dive snore
|
||||||
|
OK yanmega bug-bite bug-buzz tackle whirlwind
|
||||||
|
OK crobat brave-bird quick-attack gust zen-headbutt
|
||||||
|
OK bagon defense-curl fire-fang hydro-pump shadow-claw
|
||||||
|
OK volcarona endure toxic fly fire-blast
|
||||||
|
OK hippopotas curse revenge sleep-talk swallow
|
||||||
|
OK hippopotas curse revenge sleep-talk snore
|
||||||
|
OK smeargle bug-bite bug-buzz splash fire-blast
|
||||||
|
NO smeargle bug-bite chatter splash fire-blast
|
||||||
|
NO azurill muddy-water iron-tail scald mimic
|
||||||
|
OK salamence dragon-dance dragon-claw fire-blast earthquake -v platinum
|
||||||
|
OK crawdaunt brick-break rock-slide facade toxic -v platinum
|
||||||
|
NO cleffa tickle wish amnesia splash
|
||||||
|
OK tyrogue pursuit
|
||||||
|
NO happiny softboiled
|
||||||
|
NO mamoswine bite body-slam curse double-edge
|
||||||
|
OK shedinja swords-dance
|
||||||
|
NO shedinja swords-dance screech
|
||||||
|
OK shedinja screech double-team fury-cutter x-scissor
|
||||||
|
OK raichu volt-tackle
|
||||||
|
OK raichu surf -v gold
|
||||||
|
OK pikachu volt-tackle thunderbolt bide
|
||||||
|
OK gyarados flail thrash iron-head outrage
|
||||||
|
OK drifblim memento gust thunderbolt pain-split
|
||||||
|
OK crobat nasty-plot brave-bird
|
||||||
|
NO crobat nasty-plot hypnosis
|
||||||
|
OK garchomp double-edge thrash outrage
|
||||||
|
OK nidoking counter disable amnesia head-smash
|
||||||
|
OK aggron stomp smellingsalt screech fire-punch
|
||||||
|
OK tyranitar dragon-dance outrage thunder-wave surf
|
||||||
|
NO butterfree morning-sun harden
|
||||||
|
OK pikachu reversal bide nasty-plot discharge
|
||||||
|
NO pikachu surf charge
|
||||||
|
NO blissey wish counter
|
||||||
|
NO clefairy copycat dynamicpunch
|
||||||
|
"""
|
||||||
|
|
||||||
result_map = {'OK': True, 'NO': False}
|
result_map = {'OK': True, 'NO': False}
|
||||||
|
|
||||||
def test_cases():
|
def test_cases():
|
||||||
session = connect()
|
session = connect()
|
||||||
for argstring in u"""
|
for argstring in testcase_args.strip().splitlines():
|
||||||
NO muk
|
|
||||||
NO beedrill rage pursuit agility endeavor toxic
|
|
||||||
NO ditto psystrike aeroblast mist-ball judgment
|
|
||||||
OK lugia aeroblast punishment dive snore
|
|
||||||
OK yanmega bug-bite bug-buzz tackle whirlwind
|
|
||||||
OK crobat brave-bird quick-attack gust zen-headbutt
|
|
||||||
OK bagon defense-curl fire-fang hydro-pump shadow-claw
|
|
||||||
OK volcarona endure toxic fly fire-blast
|
|
||||||
OK hippopotas curse revenge sleep-talk swallow
|
|
||||||
OK hippopotas curse revenge sleep-talk snore
|
|
||||||
OK smeargle bug-bite bug-buzz splash fire-blast
|
|
||||||
NO smeargle bug-bite chatter splash fire-blast
|
|
||||||
NO azurill muddy-water iron-tail scald mimic
|
|
||||||
OK salamence dragon-dance dragon-claw fire-blast earthquake -v platinum
|
|
||||||
OK crawdaunt brick-break rock-slide facade toxic -v platinum
|
|
||||||
NO cleffa tickle wish amnesia splash
|
|
||||||
OK tyrogue pursuit
|
|
||||||
NO happiny softboiled
|
|
||||||
NO mamoswine bite body-slam curse double-edge
|
|
||||||
OK raichu volt-tackle
|
|
||||||
OK raichu surf -v gold
|
|
||||||
OK pikachu volt-tackle thunderbolt bide
|
|
||||||
OK gyarados flail thrash iron-head outrage
|
|
||||||
OK drifblim memento gust thunderbolt pain-split
|
|
||||||
OK crobat nasty-plot brave-bird
|
|
||||||
NO crobat nasty-plot hypnosis
|
|
||||||
OK garchomp double-edge thrash outrage
|
|
||||||
OK nidoking counter disable amnesia head-smash
|
|
||||||
OK aggron stomp smellingsalt screech fire-punch
|
|
||||||
OK tyranitar dragon-dance outrage thunder-wave surf
|
|
||||||
NO butterfree morning-sun harden
|
|
||||||
OK pikachu reversal bide nasty-plot discharge
|
|
||||||
NO pikachu surf charge
|
|
||||||
NO blissey wish counter
|
|
||||||
NO clefairy copycat dynamicpunch
|
|
||||||
""".strip().splitlines():
|
|
||||||
def run_test(argstring):
|
def run_test(argstring):
|
||||||
args = argstring.split()
|
args = argstring.split() + ['-q']
|
||||||
assert main(args[1:], session=session) == result_map[args[0]]
|
assert bool(main(args[1:], session=session)) == result_map[args[0]]
|
||||||
run_test.description = 'Moveset checker test: ' + argstring.strip()
|
run_test.description = 'Moveset checker test: ' + argstring.strip()
|
||||||
yield run_test, argstring.strip()
|
yield run_test, argstring.strip()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
# Nose's default profiler, the unmaintained hotshot, sucks.
|
||||||
|
# Use cProfile instead.
|
||||||
filename = 'movesets.profile'
|
filename = 'movesets.profile'
|
||||||
print 'Profiling the moveset checker'
|
print 'Profiling the moveset checker'
|
||||||
import cProfile
|
import cProfile
|
||||||
def header(str):
|
ok_fail = [0, 0]
|
||||||
print
|
def run_case(f, argv):
|
||||||
print str
|
print argv, '...',
|
||||||
cProfile.runctx("[(header(argv), f(argv)) for f, argv in test_cases()]",
|
sys.stdout.flush()
|
||||||
|
try:
|
||||||
|
f(argv)
|
||||||
|
ok_fail[0] += 1
|
||||||
|
print 'ok'
|
||||||
|
except AssertionError:
|
||||||
|
ok_fail[1] += 1
|
||||||
|
print 'FAIL'
|
||||||
|
cases = list(test_cases())
|
||||||
|
cProfile.runctx("[(run_case(f, argv)) for f, argv in cases]",
|
||||||
globals(), locals(), filename=filename)
|
globals(), locals(), filename=filename)
|
||||||
|
print "{0} tests: {1[0]} OK, {1[1]} failed".format(sum(ok_fail), ok_fail)
|
||||||
print 'Profile stats saved to', filename
|
print 'Profile stats saved to', filename
|
||||||
|
|
|
@ -45,7 +45,7 @@ class MovesetSearch(object):
|
||||||
_cache = WeakKeyDictionary()
|
_cache = WeakKeyDictionary()
|
||||||
|
|
||||||
def __init__(self, session, pokemon, version, moves, level=100, costs=None,
|
def __init__(self, session, pokemon, version, moves, level=100, costs=None,
|
||||||
exclude_versions=(), exclude_pokemon=(), debug_level=False):
|
exclude_versions=(), exclude_pokemon=(), debug_level=0):
|
||||||
|
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
|
@ -1263,9 +1263,23 @@ class GoalNode(PokemonNode):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
###
|
###
|
||||||
### CLI interface
|
### Interface
|
||||||
###
|
###
|
||||||
|
|
||||||
|
def verify_moveset(session, pokemon, version, moves, level=100, **kwargs):
|
||||||
|
"""Verify the given moveset.
|
||||||
|
|
||||||
|
Returns a result with a hint on how to obtain the moveset, if it is valid.
|
||||||
|
Otherwise, returns a false value.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
search = MovesetSearch(session, pokemon, version, moves, level, **kwargs)
|
||||||
|
except IllegalMoveCombination, e:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
for result in search:
|
||||||
|
return result
|
||||||
|
|
||||||
def print_result(result, moves=()):
|
def print_result(result, moves=()):
|
||||||
template = u"{cost:4} {est:4} {action:45.45}{long:1} {pokemon:10}{level:>3}{nl:1}{versions:2} {moves}"
|
template = u"{cost:4} {est:4} {action:45.45}{long:1} {pokemon:10}{level:>3}{nl:1}{versions:2} {moves}"
|
||||||
print template.format(cost='Cost', est='Est.', action='Action', pokemon='Pokemon',
|
print template.format(cost='Cost', est='Est.', action='Action', pokemon='Pokemon',
|
||||||
|
@ -1304,6 +1318,10 @@ def main(argv, session=None):
|
||||||
default='black',
|
default='black',
|
||||||
help='Version to search in.')
|
help='Version to search in.')
|
||||||
|
|
||||||
|
parser.add_argument('-q', '--quiet', action='store_true', default=False,
|
||||||
|
help="Don't print out the result, only indicate it by the return "
|
||||||
|
"value.")
|
||||||
|
|
||||||
parser.add_argument('-V', '--exclude-version', metavar='VER', type=unicode,
|
parser.add_argument('-V', '--exclude-version', metavar='VER', type=unicode,
|
||||||
action='append', default=[],
|
action='append', default=[],
|
||||||
help='Versions to exclude (along with their '
|
help='Versions to exclude (along with their '
|
||||||
|
@ -1316,8 +1334,8 @@ def main(argv, session=None):
|
||||||
|
|
||||||
parser.add_argument('-d', '--debug', action='append_const', const=1,
|
parser.add_argument('-d', '--debug', action='append_const', const=1,
|
||||||
default=[],
|
default=[],
|
||||||
help='Output timing and debugging information (can be specified more '
|
help='Output timing and debugging information. Can be specified more '
|
||||||
'than once).')
|
'than once for even more verbosity.')
|
||||||
|
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
args.debug = len(args.debug)
|
args.debug = len(args.debug)
|
||||||
|
@ -1331,54 +1349,57 @@ def main(argv, session=None):
|
||||||
if args.debug:
|
if args.debug:
|
||||||
print 'Parsing arguments'
|
print 'Parsing arguments'
|
||||||
|
|
||||||
|
class BadArgs(ValueError): pass
|
||||||
|
|
||||||
def _get_list(table, idents, name):
|
def _get_list(table, idents, name):
|
||||||
|
if not idents:
|
||||||
|
return []
|
||||||
result = []
|
result = []
|
||||||
|
query = session.query(table).filter(table.identifier.in_(idents))
|
||||||
|
query = query.order_by(table.id.desc()) # overwrite pokemon alt. forms
|
||||||
|
ident_map = dict((thing.identifier, thing) for thing in query)
|
||||||
for ident in idents:
|
for ident in idents:
|
||||||
try:
|
try:
|
||||||
result.append(util.get(session, table, identifier=ident))
|
result.append(ident_map[ident])
|
||||||
except NoResultFound:
|
except KeyError:
|
||||||
print>>sys.stderr, ('%s %s not found. Please use '
|
print>>sys.stderr, ('%s %s not found. Please use '
|
||||||
'the identifier.' % (name, ident))
|
'the identifier.' % (name, ident))
|
||||||
return False
|
raise BadArgs
|
||||||
return result
|
return result
|
||||||
|
|
||||||
pokemon = _get_list(tables.Pokemon, [args.pokemon], 'Pokemon')[0]
|
try:
|
||||||
moves = _get_list(tables.Move, args.move, 'Move')
|
all_pokemon = _get_list(tables.Pokemon,
|
||||||
version = _get_list(tables.Version, [args.version], 'Version')[0]
|
[args.pokemon] + args.exclude_pokemon, 'Pokemon')
|
||||||
excl_versions = _get_list(tables.Version, args.exclude_version, 'Version')
|
all_versions = _get_list(tables.Version,
|
||||||
excl_pokemon = _get_list(tables.Pokemon, args.exclude_pokemon, 'Pokemon')
|
[args.version] + args.exclude_version, 'Version')
|
||||||
|
|
||||||
|
pokemon = all_pokemon[0]
|
||||||
|
moves = _get_list(tables.Move, args.move, 'Move')
|
||||||
|
version = all_versions[0]
|
||||||
|
excl_versions = all_versions[1:]
|
||||||
|
excl_pokemon = all_pokemon[1:]
|
||||||
|
except BadArgs:
|
||||||
|
return False
|
||||||
|
|
||||||
if args.debug:
|
if args.debug:
|
||||||
print 'Starting search'
|
print 'Starting search'
|
||||||
|
|
||||||
no_results = True
|
result = verify_moveset(session, pokemon, version, moves, args.level,
|
||||||
try:
|
exclude_versions=excl_versions, exclude_pokemon=excl_pokemon,
|
||||||
search = MovesetSearch(session, pokemon, version, moves, args.level,
|
debug_level=args.debug)
|
||||||
exclude_versions=excl_versions, exclude_pokemon=excl_pokemon,
|
# XXX: Support more than one result
|
||||||
debug_level=args.debug)
|
if result:
|
||||||
except IllegalMoveCombination, e:
|
if args.debug:
|
||||||
print 'Error:', e
|
print '-' * 79
|
||||||
|
if not args.quiet:
|
||||||
|
print_result(result, moves=moves)
|
||||||
else:
|
else:
|
||||||
if args.debug:
|
if args.debug:
|
||||||
print 'Setup done'
|
print ' ' * 79
|
||||||
|
if not args.quiet:
|
||||||
|
print 'Illegal move combination.'
|
||||||
|
|
||||||
for result in search:
|
return result
|
||||||
if args.debug and search.output_objects:
|
|
||||||
print '**warning: search looked up output objects**'
|
|
||||||
no_results = False
|
|
||||||
print '-' * 79
|
|
||||||
print_result(result, moves=moves)
|
|
||||||
# XXX: Support more than one result
|
|
||||||
break
|
|
||||||
|
|
||||||
if args.debug:
|
|
||||||
print
|
|
||||||
print 'Done'
|
|
||||||
|
|
||||||
if no_results:
|
|
||||||
print 'Illegal move combination.'
|
|
||||||
|
|
||||||
return (not no_results)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(not main(sys.argv[1:]))
|
sys.exit(not main(sys.argv[1:]))
|
||||||
|
|
Loading…
Reference in a new issue