mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
Breed graph construction, part II
This commit is contained in:
parent
2f8611458d
commit
0d75f50b7b
1 changed files with 68 additions and 1 deletions
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
|
import itertools
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from sqlalchemy.orm import aliased
|
from sqlalchemy.orm import aliased
|
||||||
|
@ -21,6 +22,13 @@ class MovesNotLearnable(IllegalMoveCombination): pass
|
||||||
class NoParent(IllegalMoveCombination): pass
|
class NoParent(IllegalMoveCombination): pass
|
||||||
class TargetExcluded(IllegalMoveCombination): pass
|
class TargetExcluded(IllegalMoveCombination): pass
|
||||||
|
|
||||||
|
def powerset(iterable):
|
||||||
|
# recipe from: http://docs.python.org/library/itertools.html
|
||||||
|
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
|
||||||
|
s = list(iterable)
|
||||||
|
return itertools.chain.from_iterable(itertools.combinations(s, r)
|
||||||
|
for r in range(len(s)+1))
|
||||||
|
|
||||||
class MovesetSearch(object):
|
class MovesetSearch(object):
|
||||||
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=False):
|
exclude_versions=(), exclude_pokemon=(), debug=False):
|
||||||
|
@ -84,7 +92,7 @@ class MovesetSearch(object):
|
||||||
self.goal_evolution_chain, 'family')
|
self.goal_evolution_chain, 'family')
|
||||||
|
|
||||||
hard_moves = self.goal_moves - easy_moves
|
hard_moves = self.goal_moves - easy_moves
|
||||||
egg_moves = self.goal_moves - non_egg_moves
|
self.egg_moves = self.goal_moves - non_egg_moves
|
||||||
if hard_moves:
|
if 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')
|
||||||
|
@ -311,6 +319,18 @@ class MovesetSearch(object):
|
||||||
print 'Evolution moves: %s' % self.evolution_moves
|
print 'Evolution moves: %s' % self.evolution_moves
|
||||||
|
|
||||||
def construct_breed_graph(self):
|
def construct_breed_graph(self):
|
||||||
|
"""Fills breeds_required
|
||||||
|
|
||||||
|
breeds_required[egg_group][moveset] = minimum number of breeds needed
|
||||||
|
from a pokemon in this group with this moveset to the goal pokemon
|
||||||
|
with the goal moveset.
|
||||||
|
The score cannot get lower by learning new moves, only by breeding.
|
||||||
|
If missing, breeding or raising the pokemon won't do any good.
|
||||||
|
For pokemon in the target family, breeds_required doesn't apply.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Part I. Determining what moves can be passed/learned
|
||||||
|
|
||||||
# eg1_movepools[egg_group_id] = set of moves passable by pkmn in that group
|
# eg1_movepools[egg_group_id] = set of moves passable by pkmn in that group
|
||||||
eg1_movepools = defaultdict(set)
|
eg1_movepools = defaultdict(set)
|
||||||
# eg2_movepools[b_g_id1, b_g_id2] = ditto for pkmn in *both* groups
|
# eg2_movepools[b_g_id1, b_g_id2] = ditto for pkmn in *both* groups
|
||||||
|
@ -350,6 +370,53 @@ class MovesetSearch(object):
|
||||||
print " %2s/%2s pass: %s" % (g1, g2, sorted(eg2_movepools[g1, g2]))
|
print " %2s/%2s pass: %s" % (g1, g2, sorted(eg2_movepools[g1, g2]))
|
||||||
print 'Goal groups:', goal_egg_groups
|
print 'Goal groups:', goal_egg_groups
|
||||||
|
|
||||||
|
# Part II. Determining which moves are worthwhile to pass
|
||||||
|
|
||||||
|
# We want *all* paths, not just shortest ones, so use DFS.
|
||||||
|
breeds_required = defaultdict(dict)
|
||||||
|
def handle(group, moves, path):
|
||||||
|
"""
|
||||||
|
group: the group of the parent
|
||||||
|
moves: moves the parent should pass down
|
||||||
|
path: previously visited groups - to prevent cycles
|
||||||
|
"""
|
||||||
|
if not moves:
|
||||||
|
# No more moves needed to pass down: success!
|
||||||
|
return True
|
||||||
|
if breeds_required[group].get(moves, 999) <= len(path):
|
||||||
|
# Already done
|
||||||
|
return True
|
||||||
|
success = False
|
||||||
|
# Breed some more
|
||||||
|
path = path + (group, )
|
||||||
|
for new_group in all_groups.difference(path):
|
||||||
|
new_groups = tuple(sorted([group, new_group]))
|
||||||
|
# Can we pass down all the requested moves?
|
||||||
|
if moves.issubset(eg1_movepools[new_group]):
|
||||||
|
# Learn some of the moves: they don't have to be passed to us
|
||||||
|
for learned in powerset(moves & learn_pools[new_group]):
|
||||||
|
new_moves = moves.difference(learned)
|
||||||
|
local_success = handle(new_group, new_moves, path)
|
||||||
|
# If this chain eventually ended up being successful,
|
||||||
|
# it means that it is useful to pass this moveset
|
||||||
|
# to this group.
|
||||||
|
if local_success:
|
||||||
|
breeds_required[group][moves] = min(breeds_required[group].get(moves, 999), len(path) - 1)
|
||||||
|
success = True
|
||||||
|
return success
|
||||||
|
for group in goal_egg_groups:
|
||||||
|
handle(group, self.goal_moves, ())
|
||||||
|
for moves in powerset(self.goal_moves.difference(self.egg_moves)):
|
||||||
|
if moves:
|
||||||
|
breeds_required[group][frozenset(moves) | self.egg_moves] = 1
|
||||||
|
self.breeds_required = breeds_required
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
for group, movesetlist in breeds_required.items():
|
||||||
|
print 'From egg group', group
|
||||||
|
for moveset, cost in movesetlist.items():
|
||||||
|
print " %s breeds with %s" % (cost, sorted(moveset))
|
||||||
|
|
||||||
default_costs = {
|
default_costs = {
|
||||||
# Costs for learning a move in verious ways
|
# Costs for learning a move in verious ways
|
||||||
'level-up': 20, # The normal way
|
'level-up': 20, # The normal way
|
||||||
|
|
Loading…
Reference in a new issue