You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
22 KiB
473 lines
22 KiB
import sqlite3
|
|
import os
|
|
from collections import defaultdict
|
|
|
|
class EfficiencyOriginDexPlanner:
|
|
def __init__(self, db_path):
|
|
self.conn = sqlite3.connect(db_path)
|
|
self.conn.row_factory = sqlite3.Row
|
|
|
|
def get_all_data(self):
|
|
cursor = self.conn.cursor()
|
|
cursor.execute("SELECT * FROM games ORDER BY generation, id")
|
|
games = cursor.fetchall()
|
|
|
|
cursor.execute("SELECT * FROM pokemon_forms ORDER BY generation, national_dex")
|
|
pokemon_forms = cursor.fetchall()
|
|
|
|
cursor.execute("SELECT * FROM encounters")
|
|
encounters = cursor.fetchall()
|
|
|
|
cursor.execute("SELECT * FROM evolution_chains")
|
|
evolutions = cursor.fetchall()
|
|
|
|
return games, pokemon_forms, encounters, evolutions
|
|
|
|
def build_data_structures(self):
|
|
games, pokemon_forms, encounters, evolutions = self.get_all_data()
|
|
|
|
pokemon_by_gen = defaultdict(list)
|
|
game_by_gen = defaultdict(list)
|
|
encounter_data = defaultdict(list)
|
|
evolution_map = defaultdict(list)
|
|
|
|
for game in games:
|
|
game_by_gen[game['generation']].append(game)
|
|
|
|
for pokemon in pokemon_forms:
|
|
pokemon_by_gen[pokemon['generation']].append(pokemon)
|
|
|
|
for encounter in encounters:
|
|
encounter_data[encounter['game_id']].append(encounter)
|
|
|
|
for evolution in evolutions:
|
|
evolution_map[evolution['from_pfic']].append(evolution['to_pfic'])
|
|
|
|
return pokemon_by_gen, game_by_gen, encounter_data, evolution_map
|
|
|
|
def generate_efficient_plan(self, generation_groups):
|
|
pokemon_by_gen, game_by_gen, encounter_data, evolution_map = self.build_data_structures()
|
|
plan = []
|
|
caught_pokemon = set()
|
|
|
|
for group in generation_groups:
|
|
group_plan = self.plan_for_group(group, pokemon_by_gen, game_by_gen, encounter_data, evolution_map, caught_pokemon)
|
|
if group_plan:
|
|
plan.extend(group_plan)
|
|
|
|
return plan
|
|
|
|
def plan_for_group(self, generations, pokemon_by_gen, game_by_gen, encounter_data, evolution_map, caught_pokemon):
|
|
group_plan = []
|
|
needed_pokemon = set()
|
|
games_in_group = []
|
|
|
|
for gen in generations:
|
|
needed_pokemon.update(pokemon['PFIC'] for pokemon in pokemon_by_gen[gen])
|
|
games_in_group.extend(game_by_gen[gen])
|
|
|
|
print(f"Initial needed_pokemon count: {len(needed_pokemon)}")
|
|
print(f"Games in group: {[game['name'] for game in games_in_group]}")
|
|
|
|
# Check for new evolutions of already caught Pokémon
|
|
new_evolutions = set()
|
|
for pfic in caught_pokemon:
|
|
if pfic in evolution_map:
|
|
for evolved_pfic in evolution_map[pfic]:
|
|
if self.get_pokemon_generation(evolved_pfic) in generations and evolved_pfic not in caught_pokemon:
|
|
new_evolutions.add(pfic)
|
|
needed_pokemon.add(evolved_pfic)
|
|
|
|
print(f"Needed pokemon after adding new evolutions: {len(needed_pokemon)}")
|
|
|
|
# Create a dictionary to store the Pokémon available in each game, including evolutions
|
|
game_pokemon = {}
|
|
for game in games_in_group:
|
|
game_pokemon[game['id']] = set()
|
|
for encounter in encounter_data[game['id']]:
|
|
game_pokemon[game['id']].add(encounter['pfic'])
|
|
# Add all possible evolutions
|
|
current_pfic = encounter['pfic']
|
|
while current_pfic in evolution_map:
|
|
for evolved_pfic in evolution_map[current_pfic]:
|
|
if self.get_pokemon_generation(evolved_pfic) in generations:
|
|
game_pokemon[game['id']].add(evolved_pfic)
|
|
current_pfic = evolved_pfic
|
|
break
|
|
else:
|
|
break
|
|
|
|
selected_games = []
|
|
remaining_pokemon = needed_pokemon.copy()
|
|
|
|
while remaining_pokemon:
|
|
# Add debugging information
|
|
print("\nDebug: Game selection process")
|
|
for game in games_in_group:
|
|
exclusive_groups = self.get_exclusive_groups(game['id'])
|
|
pokemon_covered = set()
|
|
|
|
for pfic in remaining_pokemon & game_pokemon[game['id']]:
|
|
is_exclusive = False
|
|
for group in exclusive_groups.values():
|
|
if any(ge['pfic'] == pfic for ge in group):
|
|
is_exclusive = True
|
|
# If it's exclusive, only add it if no other Pokémon from its group is already covered
|
|
if not any(other_ge['pfic'] in pokemon_covered for other_ge in group):
|
|
pokemon_covered.add(pfic)
|
|
break
|
|
if not is_exclusive:
|
|
pokemon_covered.add(pfic)
|
|
|
|
print(f" {game['name']}: {len(pokemon_covered)} Pokemon covered")
|
|
print(f" First 15 Pokemon: {[self.get_pokemon_name(pfic) for pfic in list(pokemon_covered)[:15]]}")
|
|
|
|
best_game = max(games_in_group, key=lambda g: len(self.get_covered_pokemon(g, remaining_pokemon, game_pokemon[g['id']])))
|
|
pokemon_covered = self.get_covered_pokemon(best_game, remaining_pokemon, game_pokemon[best_game['id']])
|
|
print(f"\nBest game: {best_game['name']}, Pokemon covered: {len(pokemon_covered)}")
|
|
print(f"First 10 Pokemon covered by {best_game['name']}: {[self.get_pokemon_name(pfic) for pfic in list(pokemon_covered)[:10]]}")
|
|
|
|
if not pokemon_covered:
|
|
print("No more Pokémon can be covered. Breaking loop.")
|
|
break
|
|
|
|
selected_games.append(best_game)
|
|
remaining_pokemon -= pokemon_covered
|
|
|
|
# Check if we can remove any previously selected games
|
|
for game in selected_games[:-1]:
|
|
if all(pokemon in self.get_covered_pokemon(best_game, needed_pokemon, game_pokemon[best_game['id']])
|
|
for pokemon in self.get_covered_pokemon(game, needed_pokemon, game_pokemon[game['id']])):
|
|
selected_games.remove(game)
|
|
print(f"Removed {game['name']} as it's covered by {best_game['name']}")
|
|
|
|
print(f"Selected games: {[game['name'] for game in selected_games]}")
|
|
|
|
for game in selected_games:
|
|
game_plan = self.plan_for_game(game, needed_pokemon, encounter_data, evolution_map, caught_pokemon, set(generations), new_evolutions)
|
|
if game_plan:
|
|
# Update caught_pokemon and game_pokemon based on the new JSON structure
|
|
for plan_entry in game_plan:
|
|
for pokemon in plan_entry["pokemon"]:
|
|
pfic = pokemon["pfic"]
|
|
caught_pokemon.add(pfic)
|
|
|
|
# Handle evolutions
|
|
for evolution in pokemon["evolve_to"]:
|
|
caught_pokemon.add(evolution["pfic"])
|
|
|
|
# Handle breeding
|
|
for breeding in pokemon["breed_for"]:
|
|
caught_pokemon.add(breeding["pfic"])
|
|
|
|
# Update game_pokemon to remove exclusive encounters that weren't used
|
|
exclusive_groups = self.get_exclusive_groups(game['id'])
|
|
used_exclusive_groups = set()
|
|
|
|
# Track used exclusive groups based on the new structure
|
|
for pokemon in plan_entry["pokemon"]:
|
|
pfic = pokemon["pfic"]
|
|
for group_id, group_encounters in exclusive_groups.items():
|
|
if any(ge['pfic'] == pfic for ge in group_encounters):
|
|
used_exclusive_groups.add(group_id)
|
|
|
|
# Remove unused exclusive encounters
|
|
for group_id, group_encounters in exclusive_groups.items():
|
|
if group_id not in used_exclusive_groups:
|
|
for ge in group_encounters:
|
|
if ge['pfic'] in game_pokemon[game['id']]:
|
|
game_pokemon[game['id']].remove(ge['pfic'])
|
|
|
|
group_plan.extend(game_plan)
|
|
|
|
if not group_plan:
|
|
print("Warning: No games were selected or no Pokémon were planned to be caught.")
|
|
|
|
return group_plan
|
|
|
|
def plan_for_game(self, game, needed_pokemon, encounter_data, evolution_map, caught_pokemon, target_generations, new_evolutions):
|
|
game_plan = {
|
|
"game_name": game['name'],
|
|
"pokemon": []
|
|
}
|
|
to_catch = defaultdict(int)
|
|
to_evolve = defaultdict(list)
|
|
to_breed = defaultdict(list)
|
|
planned_evolutions = defaultdict(int)
|
|
processed_chains = set()
|
|
|
|
exclusive_groups = self.get_exclusive_groups(game['id'])
|
|
used_exclusive_groups = set()
|
|
|
|
# First, handle new evolutions for already caught Pokémon
|
|
for pfic in new_evolutions:
|
|
evolution_chains, visit_counts, baby_forms = self.get_evolution_chains(pfic, evolution_map, target_generations)
|
|
|
|
new_evolutions_count = sum(1 for chain in evolution_chains for pokemon in chain if pokemon not in caught_pokemon)
|
|
|
|
if new_evolutions_count > 0:
|
|
if any(encounter['pfic'] == pfic for encounter in encounter_data[game['id']]):
|
|
to_catch[pfic] += new_evolutions_count - planned_evolutions[pfic]
|
|
planned_evolutions[pfic] += new_evolutions_count
|
|
|
|
for chain in evolution_chains:
|
|
if len(chain) > 1:
|
|
for i in range(len(chain) - 1):
|
|
from_pfic, to_pfic = chain[i], chain[i+1]
|
|
if to_pfic not in caught_pokemon:
|
|
to_evolve[from_pfic].append(to_pfic)
|
|
caught_pokemon.add(to_pfic)
|
|
needed_pokemon.discard(to_pfic)
|
|
|
|
# Then proceed with the regular planning
|
|
for encounter in encounter_data[game['id']]:
|
|
if encounter['pfic'] in needed_pokemon and encounter['pfic'] not in caught_pokemon:
|
|
# Check if this encounter is part of an exclusive group
|
|
skip_encounter = False
|
|
for group_id, group_encounters in exclusive_groups.items():
|
|
if group_id in used_exclusive_groups:
|
|
if any(ge['encounter_id'] == encounter['id'] for ge in group_encounters):
|
|
skip_encounter = True
|
|
break
|
|
else:
|
|
for group_encounter in group_encounters:
|
|
if group_encounter['encounter_id'] == encounter['id']:
|
|
if not group_encounter['starter']:
|
|
used_exclusive_groups.add(group_id)
|
|
break
|
|
else:
|
|
continue
|
|
break
|
|
|
|
if skip_encounter:
|
|
continue
|
|
|
|
evolution_chains, visit_counts, baby_forms = self.get_evolution_chains(encounter['pfic'], evolution_map, target_generations)
|
|
base_form = evolution_chains[0][0]
|
|
|
|
if base_form not in caught_pokemon:
|
|
chain_key = tuple(sorted(set(pokemon for chain in evolution_chains for pokemon in chain)))
|
|
if chain_key not in processed_chains:
|
|
processed_chains.add(chain_key)
|
|
|
|
num_to_catch = max(0, visit_counts[base_form] - planned_evolutions[base_form])
|
|
if num_to_catch > 0:
|
|
to_catch[encounter['pfic']] += num_to_catch
|
|
planned_evolutions[base_form] += num_to_catch
|
|
|
|
for chain in evolution_chains:
|
|
if len(chain) > 1:
|
|
for i in range(len(chain) - 1):
|
|
from_pfic, to_pfic = chain[i], chain[i+1]
|
|
if from_pfic not in baby_forms:
|
|
if not self.is_final_evolution(to_pfic, evolution_map) or (self.is_final_evolution(to_pfic, evolution_map) and not self.is_in_to_evolve(to_pfic, to_evolve)):
|
|
to_evolve[from_pfic].append(to_pfic)
|
|
else:
|
|
if encounter['pfic'] in to_catch:
|
|
to_catch[encounter['pfic']] += -1
|
|
if base_form in planned_evolutions:
|
|
planned_evolutions[base_form] += -1
|
|
|
|
if from_pfic in baby_forms and from_pfic not in encounter_data[game['id']]:
|
|
if to_pfic not in to_breed or from_pfic not in to_breed[to_pfic]:
|
|
to_breed[to_pfic].append(from_pfic)
|
|
|
|
if from_pfic in to_catch:
|
|
del to_catch[from_pfic]
|
|
|
|
caught_pokemon.update(pfic for chain in evolution_chains for pfic in chain)
|
|
needed_pokemon.difference_update(pfic for chain in evolution_chains for pfic in chain)
|
|
|
|
# Remove encounters from exclusive groups that weren't used
|
|
for group_id, group_encounters in exclusive_groups.items():
|
|
if group_id not in used_exclusive_groups:
|
|
for group_encounter in group_encounters:
|
|
if group_encounter['pfic'] in to_catch:
|
|
del to_catch[group_encounter['pfic']]
|
|
if group_encounter['pfic'] in to_evolve:
|
|
del to_evolve[group_encounter['pfic']]
|
|
if group_encounter['pfic'] in to_breed:
|
|
del to_breed[group_encounter['pfic']]
|
|
|
|
if to_catch or to_evolve or to_breed:
|
|
for pfic, count in to_catch.items():
|
|
pokemon_entry = {
|
|
"pfic": pfic,
|
|
"name": self.get_pokemon_name(pfic).split(" (")[0],
|
|
"form_name": self.get_form_name(pfic),
|
|
"catch_count": count,
|
|
"evolve_to": [],
|
|
"breed_for": []
|
|
}
|
|
|
|
# Add evolution information as flat structure
|
|
if pfic in to_evolve:
|
|
processed_evolutions = set()
|
|
evolution_queue = [(pfic, to_pfic, 1) for to_pfic in to_evolve[pfic]]
|
|
|
|
while evolution_queue:
|
|
from_pfic, to_pfic, count = evolution_queue.pop(0)
|
|
if (from_pfic, to_pfic) not in processed_evolutions:
|
|
processed_evolutions.add((from_pfic, to_pfic))
|
|
|
|
evolution = {
|
|
"pfic": to_pfic,
|
|
"name": self.get_pokemon_name(to_pfic).split(" (")[0],
|
|
"form_name": self.get_form_name(to_pfic),
|
|
"count": count
|
|
}
|
|
|
|
pokemon_entry["evolve_to"].append(evolution)
|
|
|
|
# Add next evolution stage to queue if it exists
|
|
if to_pfic in evolution_map:
|
|
for next_pfic in evolution_map[to_pfic]:
|
|
if next_pfic in to_evolve[to_pfic]:
|
|
evolution_queue.append((to_pfic, next_pfic, count))
|
|
|
|
# Add breeding information
|
|
if pfic in to_breed:
|
|
for baby_pfic in to_breed[pfic]:
|
|
breeding = {
|
|
"pfic": baby_pfic,
|
|
"name": self.get_pokemon_name(baby_pfic).split(" (")[0],
|
|
"form_name": self.get_form_name(baby_pfic),
|
|
"count": 1
|
|
}
|
|
pokemon_entry["breed_for"].append(breeding)
|
|
|
|
game_plan["pokemon"].append(pokemon_entry)
|
|
|
|
return [game_plan] if game_plan["pokemon"] else []
|
|
|
|
def plan_evolutions(self, evolution_chains, evolution_map, caught_pokemon):
|
|
chains, visit_counts = self.get_evolution_chains(evolution_chains[0][0], evolution_map)
|
|
evolution_plan = []
|
|
needed_counts = defaultdict(int)
|
|
|
|
# Count how many of each Pokémon we need based on visit counts
|
|
for pfic, count in visit_counts.items():
|
|
if pfic not in caught_pokemon:
|
|
needed_counts[pfic] = count
|
|
|
|
# Plan evolutions
|
|
for chain in chains:
|
|
for i in range(len(chain) - 1):
|
|
from_pfic, to_pfic = chain[i], chain[i+1]
|
|
if needed_counts[to_pfic] > 0:
|
|
evolution_plan.append((from_pfic, to_pfic))
|
|
needed_counts[to_pfic] -= 1
|
|
needed_counts[from_pfic] -= 1
|
|
|
|
return evolution_plan, needed_counts
|
|
|
|
def get_evolution_chains(self, pfic, evolution_map, target_generations):
|
|
visited = defaultdict(int)
|
|
unique_paths = set()
|
|
baby_forms = set()
|
|
|
|
def dfs(current_pfic, path):
|
|
path_tuple = tuple(path)
|
|
if path_tuple in unique_paths:
|
|
return
|
|
|
|
unique_paths.add(path_tuple)
|
|
for pfic in path:
|
|
visited[pfic] += 1
|
|
if self.is_baby_pokemon(pfic):
|
|
baby_forms.add(pfic)
|
|
|
|
if current_pfic in evolution_map:
|
|
for next_pfic in evolution_map[current_pfic]:
|
|
if self.get_pokemon_generation(next_pfic) in target_generations:
|
|
dfs(next_pfic, path + [next_pfic])
|
|
|
|
dfs(pfic, [pfic])
|
|
chains = [list(path) for path in unique_paths]
|
|
|
|
# Adjust visit counts for baby Pokémon
|
|
for baby in baby_forms:
|
|
visited[baby] = 1
|
|
|
|
return chains, visited, baby_forms
|
|
|
|
def get_pokemon_name(self, pfic):
|
|
cursor = self.conn.cursor()
|
|
cursor.execute("SELECT name, form_name FROM pokemon_forms WHERE PFIC = ?", (pfic,))
|
|
result = cursor.fetchone()
|
|
if result['form_name']:
|
|
return f"{result['name']} ({result['form_name']})"
|
|
return result['name']
|
|
|
|
def get_form_name(self, pfic):
|
|
cursor = self.conn.cursor()
|
|
cursor.execute("SELECT form_name FROM pokemon_forms WHERE PFIC = ?", (pfic,))
|
|
result = cursor.fetchone()
|
|
return result['form_name'] if result and result['form_name'] else ""
|
|
|
|
def display_plan(self, generation_groups, output_file='efficiency_plan.json'):
|
|
plan = self.generate_efficient_plan(generation_groups)
|
|
|
|
# Write to JSON file
|
|
import json
|
|
with open(output_file, 'w', encoding='utf-8') as f:
|
|
json.dump(plan, f, indent=4, ensure_ascii=False)
|
|
|
|
print(f"\nPlan has been written to {os.path.abspath(output_file)}")
|
|
|
|
def is_baby_pokemon(self, pfic):
|
|
cursor = self.conn.cursor()
|
|
cursor.execute("SELECT is_baby_form FROM pokemon_forms WHERE PFIC = ?", (pfic,))
|
|
result = cursor.fetchone()
|
|
return result['is_baby_form'] if result else False
|
|
|
|
def get_pokemon_generation(self, pfic):
|
|
cursor = self.conn.cursor()
|
|
cursor.execute("SELECT generation FROM pokemon_forms WHERE PFIC = ?", (pfic,))
|
|
result = cursor.fetchone()
|
|
return result['generation'] if result else None
|
|
|
|
def is_final_evolution(self, pfic, evolution_map):
|
|
return pfic not in evolution_map
|
|
|
|
def is_in_to_evolve(self, pfic, to_evolve):
|
|
return any(pfic in to_pfics for to_pfics in to_evolve.values())
|
|
|
|
def get_exclusive_groups(self, game_id):
|
|
cursor = self.conn.cursor()
|
|
cursor.execute('''
|
|
SELECT eeg.group_id, e.id as encounter_id, e.pfic, e.starter
|
|
FROM encounter_exclusive_groups eeg
|
|
JOIN encounters e ON eeg.encounter_id = e.id
|
|
JOIN exclusive_encounter_groups eeg2 ON eeg.group_id = eeg2.id
|
|
WHERE eeg2.game_id = ?
|
|
''', (game_id,))
|
|
groups = defaultdict(list)
|
|
for row in cursor.fetchall():
|
|
groups[row['group_id']].append({
|
|
'encounter_id': row['encounter_id'],
|
|
'pfic': row['pfic'],
|
|
'starter': row['starter']
|
|
})
|
|
return groups
|
|
|
|
def get_covered_pokemon(self, game, needed_pokemon, available_pokemon):
|
|
exclusive_groups = self.get_exclusive_groups(game['id'])
|
|
pokemon_covered = set()
|
|
|
|
for pfic in needed_pokemon & available_pokemon:
|
|
is_exclusive = False
|
|
for group in exclusive_groups.values():
|
|
if any(ge['pfic'] == pfic for ge in group):
|
|
is_exclusive = True
|
|
# If it's exclusive, only add it if no other Pokémon from its group is already covered
|
|
if not any(other_ge['pfic'] in pokemon_covered for other_ge in group):
|
|
pokemon_covered.add(pfic)
|
|
break
|
|
if not is_exclusive:
|
|
pokemon_covered.add(pfic)
|
|
|
|
return pokemon_covered
|
|
|
|
planner = EfficiencyOriginDexPlanner('pokemon_forms.db')
|
|
planner.display_plan([[1, 2], [3, 4, 5], [6], [7], [8], [9]])
|
|
|