Browse Source

- Add route generation to the app

plan_generation
Quildra 1 year ago
parent
commit
c955a02e33
  1. 386
      routes/Gold_Silver_Route.py
  2. 231
      routes/Red_Blue_Route.py
  3. 0
      routes/__init__.py
  4. 154
      routes/pokemon_game_desc.py
  5. 18
      ui/main_window_controller.py
  6. 42
      ui/main_window_view.py
  7. 30
      ui/workers/generate_PDDLs_worker.py
  8. 214
      ui/workers/solve_pddl_route.py
  9. 176
      utility/convert_to_pddl.py

386
routes/Gold_Silver_Route.py

@ -0,0 +1,386 @@
import networkx as nx
from networkx import Graph
from routes.pokemon_game_desc import PokemonGameDesc
NEW_BARK_TOWN = 'New Bark Town'
CHERRYGROVE_CITY = 'Cherrygrove City'
VIOLET_CITY = 'Violet City'
SPROUT_TOWER = 'Sprout Tower'
RUINS_OF_ALPH = 'Ruins of Alph'
UNION_CAVE = 'Union Cave'
AZALEA_TOWN = 'Azalea Town'
SLOWPOKE_WELL = 'Slowpoke Well'
ILEX_FOREST = 'Ilex Forest'
GOLDENROD_CITY = 'Goldenrod City'
NATIONAL_PARK = 'National Park'
ECRUTEAK_CITY = 'Ecruteak City'
MOOMOO_FARM = 'MooMoo Farm'
OLIVINE_CITY = 'Olivine City'
CIANWOOD_CITY = 'Cianwood City'
MAHOGANY_TOWN = 'Mahogany Town'
LAKE_OF_RAGE = 'Lake of Rage'
TEAM_ROCKET_HQ = 'Team Rocket H.Q.'
ICE_PATH = 'Ice Path'
BLACKTHORN_CITY = 'Blackthorn City'
MT_MORTAR = 'Mt. Mortar'
TIN_TOWER = 'Tin Tower'
WHIRL_ISLANDS = 'Whirl Islands'
DARK_CAVE = 'Dark Cave'
VICTORY_ROAD = 'Victory Road'
INDIGO_PLATEAU = 'Indigo Plateau'
SS_AQUA = 'S.S. Aqua'
VERMILION_CITY = 'Vermilion City'
SAFFRON_CITY = 'Saffron City'
LAVENDER_TOWN = 'Lavender Town'
ROCK_TUNNEL = 'Rock Tunnel'
CERULEAN_CITY = 'Cerulean City'
CELADON_CITY = 'Celadon City'
FUCHSIA_CITY = 'Fuchsia City'
DIGLETTS_CAVE = "Diglett's Cave"
PEWTER_CITY = 'Pewter City'
MT_MOON = 'Mt. Moon'
VIRIDIAN_CITY = 'Viridian City'
PALLET_TOWN = 'Pallet Town'
CINNABAR_ISLAND = 'Cinnabar Island'
MT_SILVER = 'Mt. Silver'
SAFARI_ZONE = 'Safari Zone'
TOHJO_FALLS = 'Tohjo Falls'
POWER_PLANT = 'Power Plant'
LEAGUE_RECEPTION_GATE = 'League Reception Gate'
CUT = 'Cut'
SURF = 'Surf'
FLASH = 'Flash'
STRENGTH = 'Strength'
ROCK_SMASH = 'Rock Smash'
WHIRLPOOL = 'Whirlpool'
WATERFALL = 'Waterfall'
ZEPHYR_BADGE = 'Zephyr Badge'
HIVE_BADGE = 'Hive Badge'
PLAIN_BADGE = 'Plain Badge'
FOG_BADGE = 'Fog Badge'
STORM_BADGE = 'Storm Badge'
MINERAL_BADGE = 'Mineral Badge'
GLACIER_BADGE = 'Glacier Badge'
RISING_BADGE = 'Rising Badge'
BOULDER_BADGE = 'Boulder Badge'
CASCADE_BADGE = 'Cascade Badge'
THUNDER_BADGE = 'Thunder Badge'
RAINBOW_BADGE = 'Rainbow Badge'
MARSH_BADGE = 'Marsh Badge'
SOUL_BADGE = 'Soul Badge'
VOLCANO_BADGE = 'Volcano Badge'
EARTH_BADGE = 'Earth Badge'
SQUIRTBOTTLE = 'Squirt bottle'
MEDICINE = 'Medicine'
CATCH_RED_GYRADOS = 'Catch Red Gryados'
ROCKET_DEAFEATED = 'Rocket Defeated'
PKMN_WING = 'Gold Silver Wing'
JOHTO_CHAMPION = 'Johto Champion'
S_S_TICKET = 'S.S. Ticket'
WOKE_SNORLAX = 'Woke Snorlax'
EXPN_CARD = 'Expn card'
POWER_RESTORED = 'Power restored'
MACHINE_PART = 'Machine Part'
CLEFAIRY_DOLL = 'Clefairy Doll'
RAIL_PASS = 'Rail Pass'
MISTY_FOUND = 'Misty Found'
FLUTE_CHANNEL = 'Flute Channel'
ROUTE_28_UNLOCKED = 'Rotue 28 Unlocked'
POWER_PLANT_VISITED = 'POWER_PLANT_VISITED'
FLY = 'Fly'
FLY_OUT_OF_BATTLE = 'Fly out of battle'
def get_gold_silver_desc() -> PokemonGameDesc:
desc: PokemonGameDesc = PokemonGameDesc()
desc.graph = get_gold_silver_route()
desc.game_name = "Gold_Silver"
desc.towns_and_cities = [NEW_BARK_TOWN, CHERRYGROVE_CITY, VIOLET_CITY, AZALEA_TOWN, GOLDENROD_CITY, ECRUTEAK_CITY, OLIVINE_CITY, CIANWOOD_CITY, MAHOGANY_TOWN, BLACKTHORN_CITY, PALLET_TOWN, VIRIDIAN_CITY, PEWTER_CITY, CERULEAN_CITY, SAFFRON_CITY, CELADON_CITY, VERMILION_CITY, FUCHSIA_CITY, CINNABAR_ISLAND]
desc.badges = [ZEPHYR_BADGE, HIVE_BADGE, PLAIN_BADGE, FOG_BADGE, STORM_BADGE, MINERAL_BADGE, GLACIER_BADGE, RISING_BADGE, BOULDER_BADGE, CASCADE_BADGE, THUNDER_BADGE, RAINBOW_BADGE, MARSH_BADGE, SOUL_BADGE, VOLCANO_BADGE, EARTH_BADGE]
desc.hms = [CUT, SURF, FLASH, STRENGTH, FLY, WATERFALL, WHIRLPOOL]
desc.additional_goals = [WOKE_SNORLAX]
desc.starting_town = NEW_BARK_TOWN
desc.end_goal = MT_SILVER
desc.must_visit = set([MT_MOON])
desc.games_covered = ["Gold", "Silver", "Crystal"]
desc.file_name = "gold_silver_crystal_problem.pddl"
desc.one_way_routes.append("Route_29 -> Route_46")
return desc
def get_gold_silver_route() -> Graph:
G = nx.Graph()
for i in range(1,46):
G.add_node(f'Route {i}', node_type='route')
G.add_node(NEW_BARK_TOWN, node_type='location')
G.add_node(CHERRYGROVE_CITY, node_type='location')
G.add_node(VIOLET_CITY, node_type='location')
G.add_node(SPROUT_TOWER, node_type='location')
G.add_node(RUINS_OF_ALPH, node_type='location')
G.add_node(UNION_CAVE, node_type='location')
G.add_node(AZALEA_TOWN, node_type='location')
G.add_node(SLOWPOKE_WELL, node_type='location')
G.add_node(ILEX_FOREST, node_type='location')
G.add_node(GOLDENROD_CITY, node_type='location')
G.add_node(NATIONAL_PARK, node_type='location')
G.add_node(ECRUTEAK_CITY, node_type='location')
G.add_node(MOOMOO_FARM, node_type='location')
G.add_node(OLIVINE_CITY, node_type='location')
G.add_node(CIANWOOD_CITY, node_type='location')
G.add_node(MAHOGANY_TOWN, node_type='location')
G.add_node(LAKE_OF_RAGE, node_type='location')
G.add_node(TEAM_ROCKET_HQ, node_type='location')
G.add_node(ICE_PATH, node_type='location')
G.add_node(BLACKTHORN_CITY, node_type='location')
G.add_node(MT_MORTAR, node_type='location')
G.add_node(TIN_TOWER, node_type='location')
G.add_node(WHIRL_ISLANDS, node_type='location')
G.add_node(DARK_CAVE, node_type='location')
G.add_node(VICTORY_ROAD, node_type='location')
G.add_node(INDIGO_PLATEAU, node_type='location')
G.add_node(SS_AQUA, node_type='location')
G.add_node(VERMILION_CITY, node_type='location')
G.add_node(SAFFRON_CITY, node_type='location')
G.add_node(LAVENDER_TOWN, node_type='location')
G.add_node(ROCK_TUNNEL, node_type='location')
G.add_node(CERULEAN_CITY, node_type='location')
G.add_node(CELADON_CITY, node_type='location')
G.add_node(FUCHSIA_CITY, node_type='location')
G.add_node(DIGLETTS_CAVE, node_type='location')
G.add_node(PEWTER_CITY, node_type='location')
G.add_node(MT_MOON, node_type='location')
G.add_node(VIRIDIAN_CITY, node_type='location')
G.add_node(PALLET_TOWN, node_type='location')
G.add_node(CINNABAR_ISLAND, node_type='location')
G.add_node(MT_SILVER, node_type='location')
G.add_node(SAFARI_ZONE, node_type='location')
G.add_node(TOHJO_FALLS, node_type='location')
G.add_node(POWER_PLANT, node_type='location')
G.add_edge(NEW_BARK_TOWN, 'Route 29', condition=None)
G.add_edge(CHERRYGROVE_CITY, 'Route 29', condition=None)
G.add_edge(CHERRYGROVE_CITY, 'Route 30', condition=None)
G.add_edge('Route 31', 'Route 30', condition=None)
G.add_edge('Route 30', DARK_CAVE, condition=None)
G.add_edge('Route 31', VIOLET_CITY, condition=None)
G.add_edge(VIOLET_CITY, 'Route 32', condition=None)
G.add_edge(VIOLET_CITY, SPROUT_TOWER, condition=None)
G.add_edge('Route 32', RUINS_OF_ALPH, condition=None)
G.add_edge('Route 32', UNION_CAVE, condition=None)
G.add_edge(UNION_CAVE, 'Route 33', condition=None)
G.add_edge(UNION_CAVE, RUINS_OF_ALPH, condition=None)
G.add_edge('Route 33', AZALEA_TOWN, condition=None)
G.add_edge('Route 33', SLOWPOKE_WELL, condition=None)
G.add_edge(AZALEA_TOWN, ILEX_FOREST, condition=None)
G.add_edge(ILEX_FOREST, 'Route 34', condition=None)
G.add_edge(GOLDENROD_CITY, 'Route 34', condition=None)
G.add_edge(GOLDENROD_CITY, 'Route 35', condition=None)
G.add_edge(NATIONAL_PARK, 'Route 35', condition=None)
G.add_edge(NATIONAL_PARK, 'Route 36', condition=[SQUIRTBOTTLE])
G.add_edge(VIOLET_CITY, 'Route 36', condition=None)
G.add_edge('Route 37', 'Route 36', condition=[SQUIRTBOTTLE])
G.add_edge('Route 37', ECRUTEAK_CITY, condition=None)
G.add_edge('Route 38', ECRUTEAK_CITY, condition=None)
G.add_edge('Route 38', 'Route 39', condition=None)
G.add_edge('Route 39', OLIVINE_CITY, condition=None)
G.add_edge('Route 40', OLIVINE_CITY, condition=[SURF])
G.add_edge(OLIVINE_CITY, SS_AQUA, condition=[S_S_TICKET])
G.add_edge('Route 40', 'Route 41', condition=[SURF])
G.add_edge('Route 40', WHIRL_ISLANDS, condition=[SURF, WHIRLPOOL])
G.add_edge('Route 41', WHIRL_ISLANDS, condition=[SURF, WHIRLPOOL])
G.add_edge('Route 41', CIANWOOD_CITY, condition=[SURF])
G.add_edge('Route 47', CIANWOOD_CITY, condition=None)
G.add_edge('Route 47', 'Route 48', condition=None)
G.add_edge('Route 47', SAFARI_ZONE, condition=None)
G.add_edge('Route 42', ECRUTEAK_CITY, condition=None)
G.add_edge(ECRUTEAK_CITY, TIN_TOWER, condition=None)
G.add_edge('Route 42', MT_MORTAR, condition=[SURF, WATERFALL])
G.add_edge('Route 42', MAHOGANY_TOWN, condition=[SURF])
G.add_edge('Route 43', MAHOGANY_TOWN, condition=None)
G.add_edge('Route 43', LAKE_OF_RAGE, condition=None)
G.add_edge('Route 44', MAHOGANY_TOWN, condition=[ROCKET_DEAFEATED])
G.add_edge(TEAM_ROCKET_HQ, MAHOGANY_TOWN, condition=[CATCH_RED_GYRADOS])
G.add_edge('Route 44', ICE_PATH, condition=[STRENGTH])
G.add_edge(BLACKTHORN_CITY, ICE_PATH, condition=[STRENGTH])
G.add_edge('Route 45', BLACKTHORN_CITY, condition=None)
G.add_edge('Route 45', DARK_CAVE, condition=[SURF])
G.add_edge('Route 45', 'Route 46', condition=None)
G.add_edge('Route 46', 'Route 29', condition=None)
G.add_edge('Route 46', DARK_CAVE, condition=[ROCK_SMASH])
G.add_edge('Route 27', NEW_BARK_TOWN, condition=[SURF, WATERFALL, WHIRLPOOL])
G.add_edge('Route 26', 'Route 27', condition=None)
G.add_edge('Route 27', TOHJO_FALLS, condition=None)
G.add_edge(LEAGUE_RECEPTION_GATE, 'Route 26', condition=None)
G.add_edge(LEAGUE_RECEPTION_GATE, 'Route 28', condition=[ROUTE_28_UNLOCKED])
G.add_edge(LEAGUE_RECEPTION_GATE, 'Route 22', condition=[WOKE_SNORLAX])
G.add_edge(LEAGUE_RECEPTION_GATE, 'Route 23', condition=None)
G.add_edge('Route 28', MT_SILVER, condition=None)
G.add_edge('Route 23', VICTORY_ROAD, condition=[ZEPHYR_BADGE, HIVE_BADGE, PLAIN_BADGE, FOG_BADGE, STORM_BADGE, MINERAL_BADGE, GLACIER_BADGE, RISING_BADGE])
G.add_edge(SS_AQUA, VERMILION_CITY, condition=None)
G.add_edge(SAFFRON_CITY, GOLDENROD_CITY, condition=[POWER_RESTORED, RAIL_PASS])
G.add_edge(DIGLETTS_CAVE, 'Route 11', condition=[WOKE_SNORLAX])
G.add_edge(PALLET_TOWN, 'Route 1', condition=None)
G.add_edge(PALLET_TOWN, 'Route 21', condition=[SURF])
G.add_edge('Route 1', VIRIDIAN_CITY, condition=None)
G.add_edge(VIRIDIAN_CITY, 'Route 2', condition=None)
G.add_edge('Route 2', 'Viridian Forest', condition=None)
G.add_edge('Route 2', 'Route 3', condition=[CUT])
G.add_edge('Viridian Forest', PEWTER_CITY, condition=None)
G.add_edge(PEWTER_CITY, 'Route 3', condition=None)
G.add_edge('Route 3', MT_MOON, condition=None)
G.add_edge(MT_MOON, 'Route 4', condition=None)
G.add_edge('Route 4', CERULEAN_CITY, condition=None)
G.add_edge(CERULEAN_CITY, 'Route 24', condition=None)
G.add_edge(CERULEAN_CITY, 'Route 9', condition=[CUT])
G.add_edge(CERULEAN_CITY, 'Route 5', condition=None)
G.add_edge('Route 5', SAFFRON_CITY, condition=None)
G.add_edge(SAFFRON_CITY, 'Route 6', condition=None)
G.add_edge(SAFFRON_CITY, 'Route 7', condition=None)
G.add_edge(SAFFRON_CITY, 'Route 8', condition=None)
G.add_edge('Route 6', VERMILION_CITY, condition=None)
G.add_edge(VERMILION_CITY, 'Route 11', condition=None)
G.add_edge('Route 11', 'Route 12', condition=None)
G.add_edge('Route 11', DIGLETTS_CAVE, condition=None)
G.add_edge('Route 2', DIGLETTS_CAVE, condition=[CUT])
G.add_edge('Route 12', 'Route 13', condition=None)
G.add_edge('Route 12', LAVENDER_TOWN, condition=None)
G.add_edge('Route 7', LAVENDER_TOWN, condition=None)
G.add_edge(LAVENDER_TOWN, 'Route 10', condition=None)
G.add_edge('Route 9', ROCK_TUNNEL, condition=[FLASH])
G.add_edge('Route 10', ROCK_TUNNEL, condition=[FLASH])
G.add_edge(CELADON_CITY, 'Route 8', condition=None)
G.add_edge(CELADON_CITY, 'Route 16', condition=None)
G.add_edge('Route 16', 'Route 17', condition=None)
G.add_edge('Route 17', 'Route 18', condition=None)
G.add_edge('Route 18', FUCHSIA_CITY, condition=None)
G.add_edge(FUCHSIA_CITY,'Route 19', condition=None)
G.add_edge(FUCHSIA_CITY,'Route 15', condition=None)
G.add_edge('Route 19', 'Seafoam Islands', condition=[SURF])
G.add_edge('Seafoam Islands', 'Route 20', condition=[SURF])
G.add_edge('Route 20', CINNABAR_ISLAND, condition=[SURF])
G.add_edge('Route 21', CINNABAR_ISLAND, condition=[SURF])
G.add_edge('Route 22', VIRIDIAN_CITY, condition=None)
G.add_edge(VICTORY_ROAD, INDIGO_PLATEAU, condition=None)
G.add_edge('Route 12', 'Route 13', condition=None)
G.add_edge('Route 13', 'Route 14', condition=None)
G.add_edge('Route 14', 'Route 15', condition=None)
G.add_edge('Route 24', 'Route 25', condition=None)
G.add_edge('Route 10', POWER_PLANT, condition=[SURF])
G.add_edge('Route 5', 'Underground Path', condition=None)
G.add_edge('Underground Path', 'Route 6', condition=None)
G.nodes[NEW_BARK_TOWN]['grants_conditions'] = [
{'condition': S_S_TICKET, 'required_conditions': [JOHTO_CHAMPION]}
]
G.nodes[VIOLET_CITY]['grants_conditions'] = [
{'condition': ZEPHYR_BADGE, 'required_conditions': []}
]
G.nodes[SPROUT_TOWER]['grants_conditions'] = [
{'condition': FLASH, 'required_conditions': []}
]
G.nodes[AZALEA_TOWN]['grants_conditions'] = [
{'condition': HIVE_BADGE, 'required_conditions': []}
]
G.nodes[ILEX_FOREST]['grants_conditions'] = [
{'condition': CUT, 'required_conditions': []}
]
G.nodes[GOLDENROD_CITY]['grants_conditions'] = [
{'condition': PLAIN_BADGE, 'required_conditions': []},
{'condition': SQUIRTBOTTLE, 'required_conditions': []},
{'condition': PKMN_WING, 'required_conditions': [GLACIER_BADGE]},
]
G.nodes[ECRUTEAK_CITY]['grants_conditions'] = [
{'condition': SURF, 'required_conditions': []},
{'condition': FOG_BADGE, 'required_conditions': []}
]
G.nodes['Route 36']['grants_conditions'] = [
{'condition': ROCK_SMASH, 'required_conditions': [SQUIRTBOTTLE]}
]
G.nodes[OLIVINE_CITY]['grants_conditions'] = [
{'condition': MINERAL_BADGE, 'required_conditions': [MEDICINE]},
{'condition': STRENGTH, 'required_conditions': []},
]
G.nodes[CIANWOOD_CITY]['grants_conditions'] = [
{'condition': STORM_BADGE, 'required_conditions': []},
{'condition': MEDICINE, 'required_conditions': []},
{'condition': FLY, 'required_conditions': [STORM_BADGE]},
{'condition': FLY_OUT_OF_BATTLE, 'required_conditions': [STORM_BADGE]},
]
G.nodes[MAHOGANY_TOWN]['grants_conditions'] = [
{'condition': GLACIER_BADGE, 'required_conditions': [ROCKET_DEAFEATED]}
]
G.nodes[TEAM_ROCKET_HQ]['grants_conditions'] = [
{'condition': ROCKET_DEAFEATED, 'required_conditions': []},
{'condition': WHIRLPOOL, 'required_conditions': []}
]
G.nodes[LAKE_OF_RAGE]['grants_conditions'] = [
{'condition': CATCH_RED_GYRADOS, 'required_conditions': []}
]
G.nodes[ICE_PATH]['grants_conditions'] = [
{'condition': WATERFALL, 'required_conditions': []}
]
G.nodes[BLACKTHORN_CITY]['grants_conditions'] = [
{'condition': RISING_BADGE, 'required_conditions': []}
]
G.nodes[INDIGO_PLATEAU]['grants_conditions'] = [
{'condition': JOHTO_CHAMPION, 'required_conditions': []}
]
G.nodes[VERMILION_CITY]['grants_conditions'] = [
{'condition': THUNDER_BADGE, 'required_conditions': []},
{'condition': FLUTE_CHANNEL, 'required_conditions': [EXPN_CARD]},
{'condition': CLEFAIRY_DOLL, 'required_conditions': [POWER_RESTORED]},
]
G.nodes[SAFFRON_CITY]['grants_conditions'] = [
{'condition': MARSH_BADGE, 'required_conditions': []},
{'condition': RAIL_PASS, 'required_conditions': [CLEFAIRY_DOLL]},
]
G.nodes[LAVENDER_TOWN]['grants_conditions'] = [
{'condition': EXPN_CARD, 'required_conditions': [POWER_RESTORED]}
]
G.nodes[POWER_PLANT]['grants_conditions'] = [
{'condition': POWER_PLANT_VISITED, 'required_conditions': []},
{'condition': POWER_RESTORED, 'required_conditions': [MACHINE_PART]}
]
G.nodes[CERULEAN_CITY]['grants_conditions'] = [
{'condition': CASCADE_BADGE, 'required_conditions': [MISTY_FOUND]},
{'condition': MACHINE_PART, 'required_conditions': [POWER_PLANT_VISITED]}
]
G.nodes['Route 25']['grants_conditions'] = [
{'condition': MISTY_FOUND, 'required_conditions': []}
]
G.nodes[CELADON_CITY]['grants_conditions'] = [
{'condition': RAINBOW_BADGE, 'required_conditions': []}
]
G.nodes[FUCHSIA_CITY]['grants_conditions'] = [
{'condition': SOUL_BADGE, 'required_conditions': []}
]
G.nodes['Route 11']['grants_conditions'] = [
{'condition': WOKE_SNORLAX, 'required_conditions': [FLUTE_CHANNEL]}
]
G.nodes[PEWTER_CITY]['grants_conditions'] = [
{'condition': BOULDER_BADGE, 'required_conditions': []},
{'condition': PKMN_WING, 'required_conditions': []},
]
G.nodes[VIRIDIAN_CITY]['grants_conditions'] = [
{'condition': EARTH_BADGE, 'required_conditions': [VOLCANO_BADGE]},
]
G.nodes[PALLET_TOWN]['grants_conditions'] = [
{'condition': ROUTE_28_UNLOCKED, 'required_conditions': [BOULDER_BADGE, CASCADE_BADGE, THUNDER_BADGE, RAINBOW_BADGE, MARSH_BADGE, SOUL_BADGE, VOLCANO_BADGE, EARTH_BADGE]},
]
G.nodes[CINNABAR_ISLAND]['grants_conditions'] = [
{'condition': VOLCANO_BADGE, 'required_conditions': []},
]
return G

231
routes/Red_Blue_Route.py

@ -0,0 +1,231 @@
import networkx as nx
from networkx import Graph
from routes.pokemon_game_desc import PokemonGameDesc
PALLET_TOWN = 'Pallet Town'
VIRIDIAN_CITY = 'Viridian City'
VIRIDIAN_FOREST = 'Viridian Forest'
PEWTER_CITY = 'Pewter City'
MT_MOON = 'Mt. Moon'
CERULEAN_CITY = 'Cerulean City'
POWER_PLANT = 'Power Plant'
SAFFRON_CITY = 'Saffron City'
CELADON_CITY = 'Celadon City'
CERULEAN_CAVE = 'Cerulean Cave'
VERMILLION_CITY = 'Vermillion City'
BILLS_HOUSE = 'Bill\'s House'
FUCHSIA_CITY = 'Fuchsia City'
SS_ANNE = 'S.S. Anne'
CINNABAR_ISLAND = 'Cinnabar Island'
SEAFOAM_ISLANDS ='Seafoam Islands'
VICTORY_ROAD = 'Victory Road'
INDIGO_PLATEAU = 'Indigo Plateau'
ROCK_TUNNEL = 'Rock Tunnel'
UNDERGROUND_PATH = 'Underground Path'
UNDERGROUND_PASSAGE = 'Underground Passage'
DIGLETT_CAVE = 'Diglett Cave'
POKEMON_TOWER = 'Pokemon Tower'
LAVENDER_TOWN = 'Lavender Town'
BOULDER_BADGE = 'Boulder Badge'
CASCADE_BADGE = 'Cascade Badge'
THUNDER_BADGE = 'Thunder Badge'
RAINBOW_BADGE = 'Rainbow Badge'
MARSH_BADGE = 'Marsh Badge'
SOUL_BADGE = 'Soul Badge'
VOLCANO_BADGE = 'Volcano Badge'
EARTH_BADGE = 'Earth Badge'
BIKE = 'Bike'
BIKE_VOUCHER = 'Bike Voucer'
SS_ANNE_TICKET = 'S.S. Anne Ticket'
QUENCHED_THURST = 'Quenched Thrust'
POKE_FLUTE = 'Poke Flute'
SILPH_SCOPE = 'Silph Scope'
GIOVANNI_FIGHT = 'Giovanni Fight'
CHAMPION = 'Champion'
MEWTWO = 'Mewtwo'
ARCTICUNO = 'Arcticuno'
ZAPDOS = 'Zapdos'
MOLTRES = 'Moltres'
CUT = 'Cut'
SURF = 'Surf'
FLASH = 'Flash'
STRENGTH = 'Strength'
FLY = 'Fly'
FLY_OUT_OF_BATTLE = 'Fly out of battle'
def get_red_blue_desc() -> PokemonGameDesc:
desc: PokemonGameDesc = PokemonGameDesc()
desc.graph = get_red_blue_route()
desc.game_name = "Red_Blue"
desc.towns_and_cities = [PALLET_TOWN, VIRIDIAN_CITY, PEWTER_CITY, CERULEAN_CITY, SAFFRON_CITY, CELADON_CITY, VERMILLION_CITY, FUCHSIA_CITY, CINNABAR_ISLAND]
desc.badges = [BOULDER_BADGE, CASCADE_BADGE, THUNDER_BADGE, RAINBOW_BADGE, MARSH_BADGE, SOUL_BADGE, VOLCANO_BADGE, EARTH_BADGE]
desc.hms = [CUT, SURF, FLASH, STRENGTH, FLY]
desc.starting_town = PALLET_TOWN
desc.end_goal = CERULEAN_CAVE
desc.file_name = "red_blue_yellow_problem.pddl"
desc.games_covered = ["Red", "Blue", "Yellow"]
return desc
def get_red_blue_route() -> Graph:
G = nx.Graph()
for i in range(1,25):
G.add_node(f'Route {i}', node_type='route')
G.nodes['Route 2']['grants_conditions'] = [
{'condition': FLASH, 'required_conditions': [CUT]}
]
G.nodes['Route 16']['grants_conditions'] = [
{'condition': FLY, 'required_conditions': [CUT]}
]
G.add_node(PALLET_TOWN, node_type='location')
G.add_node(VIRIDIAN_CITY, node_type='location')
G.nodes[VIRIDIAN_CITY]['grants_conditions'] = [
{'condition': EARTH_BADGE, 'required_conditions': [GIOVANNI_FIGHT]}
]
G.add_node(VIRIDIAN_FOREST, node_type='location')
G.add_node(PEWTER_CITY, node_type='location')
G.nodes[PEWTER_CITY]['grants_conditions'] = [
{'condition': BOULDER_BADGE, 'required_conditions': []}
]
G.add_node(MT_MOON, node_type='location')
G.add_node(CERULEAN_CITY, node_type='location')
G.nodes[CERULEAN_CITY]['grants_conditions'] = [
{'condition': CASCADE_BADGE, 'required_conditions': []},
{'condition': BIKE, 'required_conditions': [BIKE_VOUCHER]},
]
G.add_node(BILLS_HOUSE, node_type='location')
G.nodes[BILLS_HOUSE]['grants_conditions'] = [
{'condition': SS_ANNE_TICKET, 'required_conditions': []}
]
G.add_node(POWER_PLANT, node_type='location')
G.nodes[POWER_PLANT]['grants_conditions'] = [
{'condition': ZAPDOS, 'required_conditions': []}
]
G.add_node(SAFFRON_CITY, node_type='location')
G.nodes[SAFFRON_CITY]['grants_conditions'] = [
{'condition': MARSH_BADGE, 'required_conditions': []},
{'condition': GIOVANNI_FIGHT, 'required_conditions': []},
]
G.add_node(SS_ANNE, node_type='location')
G.nodes[SS_ANNE]['grants_conditions'] = [
{'condition': CUT, 'required_conditions': []}
]
G.add_node(CERULEAN_CAVE, node_type='location')
G.nodes[CERULEAN_CAVE]['grants_conditions'] = [
{'condition': MEWTWO, 'required_conditions': []}
]
G.add_node(VERMILLION_CITY, node_type='location')
G.nodes[VERMILLION_CITY]['grants_conditions'] = [
{'condition': BIKE_VOUCHER, 'required_conditions': []},
{'condition': THUNDER_BADGE, 'required_conditions': [CUT]},
{'condition': FLY_OUT_OF_BATTLE, 'required_conditions': [CUT]}
]
G.add_node(CELADON_CITY, node_type='location')
G.nodes[CELADON_CITY]['grants_conditions'] = [
{'condition': QUENCHED_THURST, 'required_conditions': []},
{'condition': RAINBOW_BADGE, 'required_conditions': []},
{'condition': SILPH_SCOPE, 'required_conditions': []}
]
G.add_node(FUCHSIA_CITY, node_type='location')
G.nodes[FUCHSIA_CITY]['grants_conditions'] = [
{'condition': SURF, 'required_conditions': []},
{'condition': STRENGTH, 'required_conditions': []},
{'condition': SOUL_BADGE, 'required_conditions': []},
]
G.add_node(CINNABAR_ISLAND, node_type='location')
G.nodes[CINNABAR_ISLAND]['grants_conditions'] = [
{'condition': VOLCANO_BADGE, 'required_conditions': []}
]
G.add_node(SEAFOAM_ISLANDS, node_type='location')
G.nodes[SEAFOAM_ISLANDS]['grants_conditions'] = [
{'condition': ARCTICUNO, 'required_conditions': []}
]
G.add_node(VICTORY_ROAD, node_type='location')
G.nodes[VICTORY_ROAD]['grants_conditions'] = [
{'condition': MOLTRES, 'required_conditions': []}
]
G.add_node(INDIGO_PLATEAU, node_type='location')
G.nodes[INDIGO_PLATEAU]['grants_conditions'] = [
{'condition': CHAMPION, 'required_conditions': []}
]
G.add_node(ROCK_TUNNEL, node_type='location')
G.add_node(UNDERGROUND_PATH, node_type='location')
G.add_node(DIGLETT_CAVE, node_type='location')
G.add_node(POKEMON_TOWER, node_type='location')
G.nodes[POKEMON_TOWER]['grants_conditions'] = [
{'condition': POKE_FLUTE, 'required_conditions': []}
]
G.add_node(LAVENDER_TOWN, node_type='location')
G.add_node(UNDERGROUND_PASSAGE, node_type='location')
G.add_edge('Pallet Town', 'Route 1', condition=None)
G.add_edge('Pallet Town', 'Route 21', condition=[SURF])
G.add_edge('Route 1', 'Viridian City', condition=None)
G.add_edge('Viridian City', 'Route 2', condition=None)
G.add_edge('Route 2', 'Viridian Forest', condition=None)
G.add_edge('Route 2', 'Route 3', condition=[CUT])
G.add_edge('Viridian Forest', 'Pewter City', condition=None)
G.add_edge('Pewter City', 'Route 3', condition=None)
G.add_edge('Route 3', 'Mt. Moon', condition=None)
G.add_edge('Mt. Moon', 'Route 4', condition=None)
G.add_edge('Route 4', 'Cerulean City', condition=None)
G.add_edge('Cerulean City', 'Route 24', condition=None)
G.add_edge('Cerulean City', 'Route 9', condition=[CUT])
G.add_edge('Cerulean City', 'Route 5', condition=None)
G.add_edge('Route 5', 'Saffron City', condition=[QUENCHED_THURST])
G.add_edge('Saffron City', 'Route 6', condition=[QUENCHED_THURST])
G.add_edge('Saffron City', 'Route 7', condition=[QUENCHED_THURST])
G.add_edge('Saffron City', 'Route 8', condition=[QUENCHED_THURST])
G.add_edge('Route 6', 'Vermillion City', condition=None)
G.add_edge('Vermillion City', 'Route 11', condition=None)
G.add_edge('Vermillion City', SS_ANNE, condition=[SS_ANNE_TICKET])
G.add_edge('Route 11', 'Route 12', condition=[POKE_FLUTE])
G.add_edge('Route 11', DIGLETT_CAVE, condition=None)
G.add_edge('Route 2', DIGLETT_CAVE, condition=[CUT])
G.add_edge('Route 12', 'Route 13', condition=None)
G.add_edge('Route 12', LAVENDER_TOWN, condition=None)
G.add_edge('Route 7', LAVENDER_TOWN, condition=None)
G.add_edge(LAVENDER_TOWN, 'Route 10', condition=None)
G.add_edge('Route 9', 'Rock Tunnel', condition=[FLASH])
G.add_edge('Route 10', 'Rock Tunnel', condition=[FLASH])
G.add_edge('Celadon City', 'Route 8', condition=None)
G.add_edge('Celadon City', 'Route 16', condition=None)
G.add_edge('Route 16', 'Route 17', condition=[POKE_FLUTE])
G.add_edge('Route 17', 'Route 18', condition=[BIKE])
G.add_edge('Route 18', 'Fuchsia City', condition=[BIKE])
G.add_edge('Fuchsia City','Route 19', condition=None)
G.add_edge('Fuchsia City','Route 15', condition=None)
G.add_edge('Fuchsia City','Safari Zone', condition=None)
G.add_edge('Route 19', 'Seafoam Islands', condition=[SURF])
G.add_edge('Seafoam Islands', 'Route 20', condition=[SURF])
G.add_edge('Route 20', 'Cinnabar Island', condition=[SURF])
G.add_edge('Route 21', 'Cinnabar Island', condition=[SURF])
G.add_edge('Route 22', 'Viridian City', condition=None)
G.add_edge('Route 22', 'Route 23', condition=[SURF])
G.add_edge('Route 23', 'Victory Road', condition=[BOULDER_BADGE, CASCADE_BADGE, THUNDER_BADGE, RAINBOW_BADGE, MARSH_BADGE, SOUL_BADGE, VOLCANO_BADGE, EARTH_BADGE])
G.add_edge('Victory Road', 'Indigo Plateau', condition=None)
G.add_edge('Route 12', 'Route 13', condition=None)
G.add_edge('Route 13', 'Route 14', condition=None)
G.add_edge('Route 14', 'Route 15', condition=None)
G.add_edge('Route 24', 'Cerulean Cave', condition=[SURF, CHAMPION])
G.add_edge('Route 24', 'Route 25', condition=None)
G.add_edge('Route 25', BILLS_HOUSE, condition=None)
G.add_edge('Route 10', 'Power Plant', condition=[SURF])
G.add_edge('Route 5', 'Underground Path', condition=None)
G.add_edge('Underground Path', 'Route 6', condition=None)
G.add_edge(LAVENDER_TOWN, POKEMON_TOWER, condition=[SILPH_SCOPE])
G.add_edge(UNDERGROUND_PASSAGE, 'Route 7', condition=None)
G.add_edge(UNDERGROUND_PASSAGE, 'Route 8', condition=None)
return G

0
routes/__init__.py

154
routes/pokemon_game_desc.py

@ -0,0 +1,154 @@
import heapq
import networkx as nx
from typing import List, Set
FLY_OUT_OF_BATTLE = 'Fly out of battle'
class State:
def __init__(self, location, conditions, cost, path, visited_required_nodes):
self.location = location
self.conditions = conditions # A frozenset of conditions
self.cost = cost
self.path = path # List of locations visited in order
self.visited_required_nodes = visited_required_nodes # A frozenset of required nodes visited
def __lt__(self, other):
return self.cost < other.cost # For priority queue
def heuristic(state, goal_conditions, required_nodes):
# Since we don't have actual distances, we can use the number of badges remaining as the heuristic
remaining_conditions = goal_conditions - state.conditions
remaining_nodes = required_nodes - state.visited_required_nodes
return len(remaining_conditions) + len(remaining_nodes)
def is_goal_state(state, goal_location, goals, required_nodes):
return (
state.location == goal_location and
goals.issubset(state.conditions) and
required_nodes.issubset(state.visited_required_nodes)
)
class PokemonGameDesc:
def __init__(self):
self.game_name: str = ""
self.towns_and_cities: Set[str] = set()
self.badges: Set[str] = set()
self.items: Set[str] = set()
self.hms: Set[str] = set()
self.starting_town: str
self.end_goal: str
self.flying_badge: str
self.additional_goals: List[str] = []
self.one_way_routes: List[str] = []
self.must_visit: Set[str] = set()
self.graph: nx.Graph = nx.Graph()
self.games_covered: List[str] = []
self.file_name: str = ""
def astar_search(self):
from collections import deque
self.goals = set(self.badges + self.additional_goals)
# Priority queue for open states
open_list = []
heapq.heappush(open_list, (0, State(
location=self.starting_town,
conditions=frozenset(), # Start with no conditions
cost=0,
path=[self.starting_town],
visited_required_nodes=frozenset([self.starting_town]) if self.starting_town in self.must_visit else frozenset()
)))
# Closed set to keep track of visited states
closed_set = {}
while open_list:
_, current_state = heapq.heappop(open_list)
# Check if we've reached the goal location with all required conditions
if is_goal_state(current_state, self.end_goal, self.goals, self.must_visit):
return current_state.path, current_state.cost, current_state.conditions
# Check if we've already visited this state with equal or better conditions
state_key = (current_state.location, current_state.conditions, current_state.visited_required_nodes)
if state_key in closed_set and closed_set[state_key] <= current_state.cost:
continue # Skip this state
closed_set[state_key] = current_state.cost
# Expand neighbors via normal moves
for neighbor in self.graph.neighbors(current_state.location):
edge_data = self.graph.get_edge_data(current_state.location, neighbor)
edge_condition = edge_data.get('condition', [])
if edge_condition is None:
edge_requires = set()
else:
edge_requires = set(edge_condition)
# Check if we have the required conditions to traverse this edge
if not edge_requires.issubset(current_state.conditions):
continue # Can't traverse this edge
# Update conditions based on grants at the neighbor node
neighbor_data = self.graph.nodes[neighbor]
new_conditions = set(current_state.conditions)
# Check if the neighbor grants any conditions
grants = neighbor_data.get('grants_conditions', [])
for grant in grants:
required_for_grant = set(grant.get('required_conditions', []))
if required_for_grant.issubset(new_conditions):
# We can acquire the condition
new_conditions.add(grant['condition'])
# Update visited required nodes
new_visited_required_nodes = set(current_state.visited_required_nodes)
if neighbor in self.must_visit:
new_visited_required_nodes.add(neighbor)
new_state = State(
location=neighbor,
conditions=frozenset(new_conditions),
cost=current_state.cost + 1, # Assuming uniform cost; adjust if needed
path=current_state.path + [neighbor],
visited_required_nodes=frozenset(new_visited_required_nodes)
)
estimated_total_cost = new_state.cost + heuristic(new_state, self.goals, self.must_visit)
heapq.heappush(open_list, (estimated_total_cost, new_state))
# Expand neighbors via FLY if applicable
if FLY_OUT_OF_BATTLE in current_state.conditions and current_state.location in self.towns_and_cities:
for fly_target in self.towns_and_cities:
if fly_target != current_state.location and fly_target in current_state.path:
# You can fly to this location
new_conditions = set(current_state.conditions)
neighbor_data = self.graph.nodes[fly_target]
grants = neighbor_data.get('grants_conditions', [])
for grant in grants:
required_for_grant = set(grant.get('required_conditions', []))
if required_for_grant.issubset(new_conditions):
new_conditions.add(grant['condition'])
# Update visited required nodes
new_visited_required_nodes = set(current_state.visited_required_nodes)
if fly_target in self.must_visit:
new_visited_required_nodes.add(fly_target)
fly_state = State(
location=fly_target,
conditions=frozenset(new_conditions),
cost=current_state.cost + 1, # Adjust cost if flying is different
path=current_state.path + [fly_target],
visited_required_nodes=frozenset(new_visited_required_nodes)
)
estimated_total_cost = fly_state.cost + heuristic(fly_state, self.goals, self.must_visit)
heapq.heappush(open_list, (estimated_total_cost, fly_state))
return None # No path found
__all__ = ["PokemonGameDesc"]

18
ui/main_window_controller.py

@ -4,6 +4,7 @@ from PyQt6.QtWidgets import QMenu
from PyQt6.QtGui import QAction
import os
from routes.pokemon_game_desc import PokemonGameDesc
from ui.workers.calculate_origin_mark_worker import CalculateOriginMarkWorker
from ui.workers.gather_baby_form_status import GatherBabyStatusWorker
from ui.workers.gather_encounter_locations import GatherEncountersWorker
@ -11,7 +12,9 @@ from ui.workers.gather_home_storage_status_worker import GatherHomeStorageStatus
from ui.workers.gather_pokemon_forms_worker import GatherPokemonFormsWorker
from ui.workers.gather_evolutions_worker import GatherEvolutions
from ui.workers.generate_PDDLs_worker import GeneratePDDLsWorker
from ui.workers.generate_plan_worker import GeneratePlanWorker
from ui.workers.solve_pddl_route import SolvePDDLsWorker
from utility.functions import get_display_name, sanitize_filename
from db import db
@ -256,3 +259,18 @@ class MainWindowController:
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(full_plan, f, indent=4, ensure_ascii=False)
def generate_pddls(self):
worker = GeneratePDDLsWorker()
worker.signals.finished.connect(self.on_pddls_generated)
self.thread_pool.start(worker)
def on_pddls_generated(self, data):
self.view.update_route_list(data)
def solve_route(self, data: PokemonGameDesc):
worker = SolvePDDLsWorker(data)
worker.signals.finished.connect(self.on_route_solved)
self.thread_pool.start(worker)
def on_route_solved(self, data):
pass

42
ui/main_window_view.py

@ -1,9 +1,12 @@
from typing import List
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QListWidget, QLineEdit,
QLabel, QCheckBox, QPushButton, QFormLayout, QListWidgetItem, QSplitter, QTreeWidget,
QTreeWidgetItem, QDialog, QDialogButtonBox, QComboBox, QMessageBox, QSpinBox, QMenu, QTabWidget,
QTextEdit)
from PyQt6.QtCore import Qt, QSize, QTimer, QMetaObject
from PyQt6.QtGui import QPixmap, QFontMetrics, QColor, QAction
from routes.pokemon_game_desc import PokemonGameDesc
from .main_window_controller import MainWindowController
from utility.functions import get_display_name
@ -27,6 +30,7 @@ class PokemonUI(QWidget):
self.setup_db_operations_tab()
self.setup_manage_encounters_tab()
self.setup_plan_tab()
self.setup_route_planning_tab()
self.save_button = QPushButton("Save Changes")
self.save_button.clicked.connect(self.controller.save_changes)
@ -225,9 +229,28 @@ class PokemonUI(QWidget):
plan_tab_layout.addStretch(1)
generate_plan__btn = QPushButton("Generate Plan")
generate_plan__btn.clicked.connect(self.controller.generate_plan)
plan_tab_layout.addWidget(generate_plan__btn)
generate_plan_btn = QPushButton("Generate Plan")
generate_plan_btn.clicked.connect(self.controller.generate_plan)
plan_tab_layout.addWidget(generate_plan_btn)
def setup_route_planning_tab(self):
route_plan_tab = QWidget()
route_plan_tab_layout = QVBoxLayout(route_plan_tab)
self.tab_widget.addTab(route_plan_tab, "Route Generation")
self.route_combo_box = QComboBox()
route_plan_tab_layout.addWidget(self.route_combo_box)
solve_route_btn = QPushButton("Solve Selected Route")
solve_route_btn.clicked.connect(self.solve_route)
route_plan_tab_layout.addWidget(solve_route_btn)
route_plan_tab_layout.addStretch(1)
generate_pddls_btn = QPushButton("Generate PDDLs")
generate_pddls_btn.clicked.connect(self.controller.generate_pddls)
route_plan_tab_layout.addWidget(generate_pddls_btn)
def update_pokemon_forms(self, data):
self.pokemon_list.clear()
@ -365,4 +388,15 @@ class PokemonUI(QWidget):
#self.encounter_cache[pfic] = len(encounters) > 0
# After updating the locations tree
#self.update_pokemon_list_highlights()
#self.update_pokemon_list_highlights()
def update_route_list(self, data: List[PokemonGameDesc]):
self.route_combo_box.clear()
for item in data:
self.route_combo_box.addItem(item.game_name, item)
def solve_route(self):
index = self.route_combo_box.currentIndex()
data = self.route_combo_box.itemData(index, role=Qt.ItemDataRole.UserRole)
self.controller.solve_route(data)

30
ui/workers/generate_PDDLs_worker.py

@ -0,0 +1,30 @@
from PyQt6.QtCore import QObject, pyqtSignal, QRunnable
from routes.Gold_Silver_Route import get_gold_silver_desc
from routes.Red_Blue_Route import get_red_blue_desc
from utility.convert_to_pddl import generate_pddl_domain, generate_pddl_problem
class GeneratePDDLsWorkerSignals(QObject):
finished = pyqtSignal(list)
class GeneratePDDLsWorker(QRunnable):
def __init__(self):
super().__init__()
self.signals = GeneratePDDLsWorkerSignals()
def run(self):
try:
gathered_data = self.generate_PDDLs()
self.signals.finished.emit(gathered_data)
except Exception as e:
print(f"Error gathering Pokémon home storage status: {e}")
def generate_PDDLs(self):
generate_pddl_domain()
red_blue = get_red_blue_desc()
gold_silver = get_gold_silver_desc()
generate_pddl_problem(red_blue)
generate_pddl_problem(gold_silver)
return [red_blue, gold_silver]

214
ui/workers/solve_pddl_route.py

@ -0,0 +1,214 @@
import json
import re
import sys
from PyQt6.QtCore import QObject, pyqtSignal, QRunnable
import subprocess
import os
from db import db
from routes.pokemon_game_desc import PokemonGameDesc
from downward.driver.main import main
from utility.convert_to_pddl import format_name, generate_pddl_problem
class SolvePDDLsWorkerSignals(QObject):
finished = pyqtSignal(list)
class SolvePDDLsWorker(QRunnable):
def __init__(self, data: PokemonGameDesc):
super().__init__()
self.signals = SolvePDDLsWorkerSignals()
self.data = data
def run(self):
try:
gathered_data = self.solve_PDDL(self.data)
self.signals.finished.emit(gathered_data)
except Exception as e:
print(f"Error gathering Pokémon home storage status: {e}")
def solve_PDDL(self, data: PokemonGameDesc, game_name: str = ""):
if game_name == "":
game_name = "Crystal"
route, _, _ = data.astar_search()
locations = set(route)
print("Unique locations visisted: ", locations)
output_file = "./temp/plan.json"
plan = {}
with open(output_file, 'r', encoding='utf-8') as f:
plan = json.load(f)
target = None
for game in plan:
if game["game_name"] == game_name:
target = game
break
if not target:
return []
# gather up all the pokemon needed for say crystal and find all the encounter routes/locations
needed_locations = set()
game_info = db.get_game_id_by_name(game_name)
for key in target["pokemon"]:
catch_stats = target["pokemon"][key]
rep = catch_stats["representative"]
rand = db.get_encounters(rep, "random")
static = db.get_encounters(rep, "static")
encounters = []
encounters.extend(rand)
encounters.extend(static)
for encounter in encounters:
if encounter["game_id"] != game_info["id"]:
continue
encounter_data = json.loads(encounter["data"])
needed_locations.add(encounter_data["location"].replace("*", ""))
# compare those to all the ones we visit in the generated route
missing_locations = needed_locations - locations
print("missing_locations:", missing_locations)
node_list = list(data.graph.nodes())
# add missing ones to the has_visited list and re-gen the problem, then run it again
nodes_to_visit = []
for loc in missing_locations:
if loc in node_list:
nodes_to_visit.append(loc)
print("Additional Nodes to Visit:", nodes_to_visit)
data.must_visit = data.must_visit | set(nodes_to_visit)
route, _, _ = data.astar_search()
ordered_dict = dict.fromkeys(route)
unique_list = list(ordered_dict)
return unique_list
def solve_PDDL_old(self, data: PokemonGameDesc, game_name: str = ""):
if game_name == "":
game_name = "Crystal"
plan_file_name = f'./{game_name.lower()}_sas_plan'
if os.path.exists(os.path.abspath(plan_file_name)) == False:
self.generate_sas_plan(data)
locations = set()
pattern = re.compile(r"\((?:move|fly)\s+(\w+)\s+(\w+)\)")
with open(plan_file_name, 'r') as f:
for line in f:
# Find all matches for move and fly actions
matches = pattern.findall(line)
for loc1, loc2 in matches:
# Add both locations to the set
locations.add(loc1)
locations.add(loc2)
print("Unique locations visisted: ", locations)
output_file = "./temp/plan.json"
plan = {}
with open(output_file, 'r', encoding='utf-8') as f:
plan = json.load(f)
target = None
for game in plan:
if game["game_name"] == game_name:
target = game
break
if not target:
return []
# gather up all the pokemon needed for say crystal and find all the encounter routes/locations
needed_locations = set()
game_info = db.get_game_id_by_name(game_name)
for key in target["pokemon"]:
catch_stats = target["pokemon"][key]
rep = catch_stats["representative"]
rand = db.get_encounters(rep, "random")
static = db.get_encounters(rep, "static")
encounters = []
encounters.extend(rand)
encounters.extend(static)
for encounter in encounters:
if encounter["game_id"] != game_info["id"]:
continue
encounter_data = json.loads(encounter["data"])
needed_locations.add(format_name(encounter_data["location"].replace("*", "")).lower())
# compare those to all the ones we visit in the generated route
missing_locations = needed_locations - locations
print("missing_locations:", missing_locations)
node_list = list(data.graph.nodes())
for i in range(len(node_list)):
node_list[i] = format_name(node_list[i])
# add missing ones to the has_visited list and re-gen the problem, then run it again
nodes_to_visit = []
for loc in missing_locations:
if loc in node_list:
nodes_to_visit.append(loc)
print("Additional Nodes to Visit:", nodes_to_visit)
data.has_visited.extend(nodes_to_visit)
generate_pddl_problem(data)
self.generate_sas_plan(data)
return []
def generate_sas_plan(self, data: PokemonGameDesc):
print("Starting fast-downward...")
try:
command_translate = [
'python',
os.path.abspath('./downward/builds/release/bin/translate/translate.py'),
os.path.abspath('./temp/pokemon_domain.pddl'),
os.path.abspath(f'./temp/{data.file_name}'),
'--sas-file',
'output.sas'
]
command_search = [
os.path.abspath('./downward/builds/release/bin/downward.exe'),
'--search',
'astar(hmax())',
'--internal-plan-file',
'sas_plan'
]
if os.path.exists(os.path.abspath('./output.sas')):
os.remove(os.path.abspath('./output.sas'))
if os.path.exists(os.path.abspath('./sas_plan')):
os.remove(os.path.abspath('./sas_plan'))
print("Command to be executed:", command_translate)
ret = subprocess.run(command_translate, check=True)
with open(f'./output.sas', 'r') as f:
print("Command to be executed:", command_translate)
ret = subprocess.run(command_search, check=True, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP, stdin=f)
except subprocess.CalledProcessError as e:
print(f"An error occurred: {e}")
print("fast-downward compelted")

176
utility/convert_to_pddl.py

@ -0,0 +1,176 @@
from routes.pokemon_game_desc import PokemonGameDesc
def format_name(name):
return name.replace(' ', '_').replace('.', '').replace("'", '').lower()
def generate_pddl_domain():
# Define the types, predicates, and action as shown above
domain_pddl = """
(define (domain pokemon)
(:requirements :strips :typing :equality :quantified-preconditions :conditional-effects :negative-preconditions)
(:types
location
condition
)
(:predicates
(at ?loc - location)
(connected ?from ?to - location)
(has ?cond - condition)
(grants ?loc - location ?cond - condition)
(requires ?from ?to - location ?cond - condition)
(requires_grant ?loc - location ?cond - condition ?req - condition)
(visited ?loc - location)
(is_town_or_city ?loc - location)
)
(:action move
:parameters (?from ?to - location)
:precondition (and
(at ?from)
(connected ?from ?to)
; Remove (not (visited ?to)) if re-visiting is allowed
; Universal preconditions for required conditions
(forall (?cond - condition)
(imply (requires ?from ?to ?cond) (has ?cond))
)
)
:effect (and
(at ?to)
(not (at ?from))
(visited ?to)
)
)
(:action acquire
:parameters (?loc - location ?cond - condition)
:precondition (and
(at ?loc)
(grants ?loc ?cond)
(not (has ?cond))
; Universal preconditions for required grants
(forall (?req - condition)
(imply (requires_grant ?loc ?cond ?req) (has ?req))
)
)
:effect (and
(has ?cond)
)
)
(:action fly
:parameters (?from ?to - location)
:precondition (and
(at ?from)
(has Fly)
(has Fly_out_of_battle)
(visited ?to)
(is_town_or_city ?to)
(not (at ?to)) ; Prevents flying to the current location
)
:effect (and
(at ?to)
(not (at ?from))
)
)
)
"""
with open('./temp/pokemon_domain.pddl', 'w') as f:
f.write(domain_pddl)
def generate_pddl_problem(desc: PokemonGameDesc):
# Extract objects, init, and goal
locations = set()
conditions = set()
connections = []
grants = []
requirements = []
requires_grant = []
# Gather conditions from node grants and edge requirements
for node, attrs in desc.graph.nodes(data=True):
node_formatted = format_name(node)
locations.add(node_formatted)
grants_conditions = attrs.get('grants_conditions', [])
for grant in grants_conditions:
condition = format_name(grant['condition'])
conditions.add(condition)
grants.append((node_formatted, condition))
required_conditions = grant.get('required_conditions', [])
for req in required_conditions:
req_condition = format_name(req)
conditions.add(req_condition)
requires_grant.append((node_formatted, condition, req_condition))
for u, v, attrs in desc.graph.edges(data=True):
u_formatted = format_name(u)
v_formatted = format_name(v)
locations.update([u_formatted, v_formatted])
# Add both directions for bidirectional movement
if f'{u_formatted} -> {v_formatted}' not in desc.one_way_routes:
connections.append((u_formatted, v_formatted))
if f'{v_formatted} -> {u_formatted}' not in desc.one_way_routes:
connections.append((v_formatted, u_formatted)) # Add reverse connection
edge_condition = attrs.get('condition')
if edge_condition:
if isinstance(edge_condition, list):
for cond in edge_condition:
cond_formatted = format_name(cond)
conditions.add(cond_formatted)
requirements.append((u_formatted, v_formatted, cond_formatted))
requirements.append((v_formatted, u_formatted, cond_formatted)) # Reverse
else:
cond_formatted = format_name(edge_condition)
conditions.add(cond_formatted)
requirements.append((u_formatted, v_formatted, cond_formatted))
requirements.append((v_formatted, u_formatted, cond_formatted)) # Reverse
conditions.add(format_name('Fly'))
conditions.add(format_name('Fly out of battle'))
formatted_towns_and_cities = [format_name(town) for town in desc.towns_and_cities]
# Prepare the PDDL problem file content
problem_pddl = "(define (problem pokemon_problem)\n"
problem_pddl += " (:domain pokemon)\n"
# Objects
problem_pddl += " (:objects\n"
problem_pddl += " " + " ".join(sorted(locations)) + " - location\n"
problem_pddl += " " + " ".join(sorted(conditions)) + " - condition\n"
problem_pddl += " )\n"
# Initial state
problem_pddl += " (:init\n"
problem_pddl += f" (at {format_name(desc.starting_town)})\n"
for u, v in connections:
problem_pddl += f" (connected {u} {v})\n"
for loc, cond in grants:
problem_pddl += f" (grants {loc} {cond})\n"
for u, v, cond in requirements:
problem_pddl += f" (requires {u} {v} {cond})\n"
for loc, cond, req in requires_grant:
problem_pddl += f" (requires_grant {loc} {cond} {req})\n"
for town in formatted_towns_and_cities:
problem_pddl += f" (is_town_or_city {town})\n"
problem_pddl += " )\n"
# Goal state
badges = desc.badges
additional_conditions = desc.additional_goals
visited = desc.must_visit
problem_pddl += " (:goal\n"
problem_pddl += " (and\n"
problem_pddl += f" (at {format_name(desc.end_goal)})\n"
for badge in badges:
problem_pddl += f" (has {format_name(badge)})\n"
for cond in additional_conditions:
problem_pddl += f" (has {format_name(cond)})\n"
for location in visited:
problem_pddl += f" (visited {format_name(location)})\n"
problem_pddl += " )\n"
problem_pddl += " )\n"
problem_pddl += ")\n"
with open(f'./temp/{desc.file_name}', 'w') as f:
f.write(problem_pddl)
Loading…
Cancel
Save