mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
Add Sketch. And some tests.
This commit is contained in:
parent
266f863334
commit
7fe9e62be3
2 changed files with 89 additions and 8 deletions
49
pokedex/tests/test_movesets.py
Normal file
49
pokedex/tests/test_movesets.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
from pokedex.util.movesets import main
|
||||||
|
|
||||||
|
result_map = {'OK': True, 'NO': False}
|
||||||
|
|
||||||
|
def test_cases():
|
||||||
|
for argstring in u"""
|
||||||
|
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 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
|
||||||
|
OK crobat brave-bird hypnosis
|
||||||
|
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
|
||||||
|
NO aggron endeavor body-slam
|
||||||
|
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):
|
||||||
|
args = argstring.split()
|
||||||
|
assert main(args[1:]) == result_map[args[0]]
|
||||||
|
run_test.description = 'Moveset checker test: ' + argstring.strip()
|
||||||
|
yield run_test, argstring
|
|
@ -25,6 +25,7 @@ class NoMoves(IllegalMoveCombination): pass
|
||||||
class MovesNotLearnable(IllegalMoveCombination): pass
|
class MovesNotLearnable(IllegalMoveCombination): pass
|
||||||
class NoParent(IllegalMoveCombination): pass
|
class NoParent(IllegalMoveCombination): pass
|
||||||
class TargetExcluded(IllegalMoveCombination): pass
|
class TargetExcluded(IllegalMoveCombination): pass
|
||||||
|
class DuplicateMoves(IllegalMoveCombination): pass
|
||||||
|
|
||||||
###
|
###
|
||||||
### Generic helpers
|
### Generic helpers
|
||||||
|
@ -57,6 +58,10 @@ class MovesetSearch(object):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
self.sketch = util.get(session, tables.Move, identifier=u'sketch').id
|
self.sketch = util.get(session, tables.Move, identifier=u'sketch').id
|
||||||
|
self.unsketchable = set([
|
||||||
|
util.get(session, tables.Move, identifier=u'struggle').id,
|
||||||
|
util.get(session, tables.Move, identifier=u'chatter').id,
|
||||||
|
])
|
||||||
self.no_eggs_group = util.get(session, tables.EggGroup,
|
self.no_eggs_group = util.get(session, tables.EggGroup,
|
||||||
identifier=u'no-eggs').id
|
identifier=u'no-eggs').id
|
||||||
self.ditto_group = util.get(session, tables.EggGroup,
|
self.ditto_group = util.get(session, tables.EggGroup,
|
||||||
|
@ -81,6 +86,9 @@ class MovesetSearch(object):
|
||||||
self.goal_version_group = version.version_group_id
|
self.goal_version_group = version.version_group_id
|
||||||
self.goal_level = level
|
self.goal_level = level
|
||||||
|
|
||||||
|
if len(self.goal_moves) < len(moves):
|
||||||
|
raise DuplicateMoves('Cannot learn duplicate moves')
|
||||||
|
|
||||||
if pokemon:
|
if pokemon:
|
||||||
self.goal_evolution_chain = pokemon.evolution_chain_id
|
self.goal_evolution_chain = pokemon.evolution_chain_id
|
||||||
if self.goal_evolution_chain in self.excluded_families:
|
if self.goal_evolution_chain in self.excluded_families:
|
||||||
|
@ -103,9 +111,9 @@ class MovesetSearch(object):
|
||||||
easy_moves, non_egg_moves = self.load_pokemon_moves(
|
easy_moves, non_egg_moves = self.load_pokemon_moves(
|
||||||
self.goal_evolution_chain, 'family')
|
self.goal_evolution_chain, 'family')
|
||||||
|
|
||||||
hard_moves = self.goal_moves - easy_moves
|
self.hard_moves = self.goal_moves - easy_moves
|
||||||
self.egg_moves = self.goal_moves - non_egg_moves
|
self.egg_moves = self.goal_moves - non_egg_moves
|
||||||
if hard_moves:
|
if self.hard_moves:
|
||||||
# Have to breed!
|
# Have to breed!
|
||||||
self.load_pokemon_moves(self.goal_evolution_chain, 'others')
|
self.load_pokemon_moves(self.goal_evolution_chain, 'others')
|
||||||
|
|
||||||
|
@ -482,7 +490,7 @@ class MovesetSearch(object):
|
||||||
success = True
|
success = True
|
||||||
return success
|
return success
|
||||||
for group in goal_egg_groups:
|
for group in goal_egg_groups:
|
||||||
handle(group, self.goal_moves, ())
|
handle(group, self.hard_moves, ())
|
||||||
for moves in powerset(self.goal_moves):
|
for moves in powerset(self.goal_moves):
|
||||||
if moves:
|
if moves:
|
||||||
breeds_required[group][frozenset(moves)] = 1
|
breeds_required[group][frozenset(moves)] = 1
|
||||||
|
@ -575,10 +583,9 @@ default_costs = {
|
||||||
'tutor-once': 2100, # gen III: tutors only work once (well except Emerald frontier ones)
|
'tutor-once': 2100, # gen III: tutors only work once (well except Emerald frontier ones)
|
||||||
|
|
||||||
# For technical reasons, 'sketch' is also used for learning Sketch and
|
# For technical reasons, 'sketch' is also used for learning Sketch and
|
||||||
# evolution-inducing moves by normal means, if they aren't included in the
|
# by normal means, if it isn't included in the target moveset.
|
||||||
# target moveset.
|
|
||||||
# So the actual cost of a sketched move will be double this number.
|
# So the actual cost of a sketched move will be double this number.
|
||||||
'sketch': 5, # Cheap. Exclude Smeargle if you think it's too cheap.
|
'sketch': 100, # Cheap. Exclude Smeargle if you think it's too cheap.
|
||||||
|
|
||||||
# Gimmick moves – we need to use this method to learn the move anyway,
|
# Gimmick moves – we need to use this method to learn the move anyway,
|
||||||
# so make a big-ish dent in the score if missing
|
# so make a big-ish dent in the score if missing
|
||||||
|
@ -699,6 +706,12 @@ class GrowAction(Action, namedtuple('GrowAction', 'search level')):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Grow to level {0.level}".format(self)
|
return "Grow to level {0.level}".format(self)
|
||||||
|
|
||||||
|
class SketchAction(Action, namedtuple('SketchAction', 'search move_')):
|
||||||
|
keyword = 'grow'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Sketch {0.move.name}".format(self)
|
||||||
|
|
||||||
class BreedAction(Action, namedtuple('BreedAction', 'search pokemon_ moves_')):
|
class BreedAction(Action, namedtuple('BreedAction', 'search pokemon_ moves_')):
|
||||||
keyword = 'grow'
|
keyword = 'grow'
|
||||||
|
|
||||||
|
@ -754,6 +767,7 @@ class PokemonNode(Node, Facade, namedtuple('PokemonNode',
|
||||||
self.expand_grow(),
|
self.expand_grow(),
|
||||||
self.expand_evolutions(),
|
self.expand_evolutions(),
|
||||||
self.expand_breed(),
|
self.expand_breed(),
|
||||||
|
self.expand_sketch(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def expand_learn(self):
|
def expand_learn(self):
|
||||||
|
@ -896,6 +910,23 @@ class PokemonNode(Node, Facade, namedtuple('PokemonNode',
|
||||||
yield cost, None, GoalBreedNode(search=self.search, dummy='g',
|
yield cost, None, GoalBreedNode(search=self.search, dummy='g',
|
||||||
version_group_=self.version_group_, moves_=self.moves_)
|
version_group_=self.version_group_, moves_=self.moves_)
|
||||||
|
|
||||||
|
def expand_sketch(self):
|
||||||
|
moves = self.moves_
|
||||||
|
for sketch in moves:
|
||||||
|
if sketch == self.search.sketch:
|
||||||
|
for sketched in self.search.goal_moves:
|
||||||
|
if sketched in self.search.unsketchable:
|
||||||
|
continue
|
||||||
|
if sketched not in moves:
|
||||||
|
moves = set(moves)
|
||||||
|
moves.remove(sketch)
|
||||||
|
moves.add(sketched)
|
||||||
|
action = SketchAction(self.search, sketched)
|
||||||
|
cost = self.search.costs['sketch']
|
||||||
|
yield cost, action, self._replace(
|
||||||
|
new_level=False, moves_=frozenset(moves))
|
||||||
|
return
|
||||||
|
|
||||||
class BaseBreedNode(Node):
|
class BaseBreedNode(Node):
|
||||||
"""Breed node
|
"""Breed node
|
||||||
This serves to prevent duplicate breeds, by storing only the needed info
|
This serves to prevent duplicate breeds, by storing only the needed info
|
||||||
|
@ -1035,7 +1066,7 @@ def main(argv):
|
||||||
if args.debug:
|
if args.debug:
|
||||||
print 'Setup done'
|
print 'Setup done'
|
||||||
|
|
||||||
template = "{cost:4} {action:50.50} {pokemon:10}{level:>3}{nl:1}{versions:2} {moves}"
|
template = "{cost:4} {action:50.50}{long:1} {pokemon:10}{level:>3}{nl:1}{versions:2} {moves}"
|
||||||
for result in search:
|
for result in search:
|
||||||
print '-' * 79
|
print '-' * 79
|
||||||
if no_results:
|
if no_results:
|
||||||
|
@ -1043,13 +1074,14 @@ def main(argv):
|
||||||
print '**warning: search looked up output objects**'
|
print '**warning: search looked up output objects**'
|
||||||
no_results = False
|
no_results = False
|
||||||
print template.format(cost='Cost', action='Action', pokemon='Pokemon',
|
print template.format(cost='Cost', action='Action', pokemon='Pokemon',
|
||||||
level='Lv.', nl='V', versions='er',
|
long='',level='Lv.', nl='V', versions='er',
|
||||||
moves=''.join(m.name[0].lower() for m in moves))
|
moves=''.join(m.name[0].lower() for m in moves))
|
||||||
for cost, action, node in reversed(list(result)):
|
for cost, action, node in reversed(list(result)):
|
||||||
if action:
|
if action:
|
||||||
print template.format(
|
print template.format(
|
||||||
cost=cost,
|
cost=cost,
|
||||||
action=action,
|
action=action,
|
||||||
|
long='>' if len(str(action)) > 50 else '',
|
||||||
pokemon=node.pokemon.name,
|
pokemon=node.pokemon.name,
|
||||||
nl='.' if node.new_level else ' ',
|
nl='.' if node.new_level else ' ',
|
||||||
level=node.level,
|
level=node.level,
|
||||||
|
|
Loading…
Reference in a new issue