diff --git a/Pokemon_Red_Route_Planner.py b/Pokemon_Red_Route_Planner.py index 7890360..3aa8fb6 100644 --- a/Pokemon_Red_Route_Planner.py +++ b/Pokemon_Red_Route_Planner.py @@ -1,211 +1,159 @@ import networkx as nx +from networkx import Graph from pyvis.network import Network +import heapq +from copy import deepcopy -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' - -CUT = 'Cut' -SURF = 'Surf' -FLASH = 'Flash' -STRENGTH = 'Strength' +from Routes.Red_Blue_Route import CERULEAN_CAVE, PALLET_TOWN, get_red_blue_route -def main(): - G = nx.DiGraph() - - 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.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.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.add_node(VERMILLION_CITY, node_type='location') - G.nodes[VERMILLION_CITY]['grants_conditions'] = [ - {'condition': BIKE_VOUCHER, 'required_conditions': []}, - {'condition': THUNDER_BADGE, '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[SS_ANNE]['grants_conditions'] = [ - {'condition': VOLCANO_BADGE, 'required_conditions': []} - ] - G.add_node(SEAFOAM_ISLANDS, node_type='location') - G.add_node(VICTORY_ROAD, node_type='location') - 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') - +class PlayerState: + def __init__(self): + self.visited_nodes = {} + self.conditions_met = set() + self.goals = set() # Tracks unmet conditions required for future traversal + +def find_optimal_path(G, start_node, end_node): + # Priority queue elements: (total_cost, current_node, player_state, path) + queue = [] + initial_state = PlayerState() + heapq.heappush(queue, (0, start_node, [start_node], initial_state)) + visited_states = {} + + while queue: + total_cost, current_node, path , player_state= heapq.heappop(queue) + + # If end node is reached + if current_node == end_node: + return path + + if current_node in player_state.visited_nodes: + player_state.visited_nodes[current_node] += 1 + else: + player_state.visited_nodes[current_node] = 1 + + # Visit the current node and update the player's state + visit_node(current_node, G, player_state) + + # Explore neighbors + for neighbor in G.neighbors(current_node): + edge_attrs = G[current_node][neighbor] + if can_traverse(edge_attrs, player_state): + visit_count = player_state.visited_nodes.get(neighbor, 0) + edge_weight = max(1, visit_count) - G.add_edge('Pallet Town', 'Route 1', condition=None) - G.add_edge('Pallet Town', 'Route 21', condition=None) - 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('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 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) + # Apply weight reduction if this neighbor helps meet a goal + if any(condition in player_state.goals for condition in player_state.conditions_met): + edge_weight = max(1, edge_weight - 1) # Reduce weight to incentivize this path + + new_total_cost = total_cost + edge_weight + + # Copy path, but keep player_state as is (state should persist across paths) + new_path = path + [neighbor] + heapq.heappush(queue, (new_total_cost, neighbor, new_path, deepcopy(player_state))) + else: + unmet_conditions = edge_attrs.get('condition', []) + for condition in unmet_conditions: + if condition not in player_state.conditions_met: + player_state.goals.add(condition) + + return None # No path found + +def can_traverse(edge_attrs, player_state: PlayerState): + conditions = edge_attrs.get('condition') + if not conditions: + return True + return any(condition in conditions for condition in player_state.conditions_met) + +def visit_node(node_name, G, player_state: PlayerState): + node_attrs = G.nodes[node_name] + grants_conditions = node_attrs.get('grants_conditions', []) + for grant in grants_conditions: + condition = grant['condition'] + required_conditions = grant.get('required_conditions', []) + + if all(cond in player_state.conditions_met for cond in required_conditions): + if condition not in player_state.conditions_met: + player_state.conditions_met.add(condition) + print(f"Condition '{condition}' granted upon visiting '{node_name}'.") + + if condition in player_state.goals: + player_state.goals.remove(condition) + else: + print(f"Cannot obtain '{condition}' from '{node_name}' because required conditions are not met: {required_conditions}") + +def get_available_moves(current_node, G, player_state): + available_moves = [] + for neighbor in G.neighbors(current_node): + edge_attrs = G[current_node][neighbor] + if can_traverse(edge_attrs, player_state): + available_moves.append(neighbor) + return available_moves + + +def main(): + player_state = { + 'visited_nodes': set(), + 'conditions_met': set() + } + + G = get_red_blue_route() net = Network(notebook=True) + optimal_path = find_optimal_path(G, PALLET_TOWN, CERULEAN_CAVE) + + if optimal_path is None: + return + + # Create a set of edges in the optimal path for quick lookup + optimal_edges = set(zip(optimal_path, optimal_path[1:])) + + # Add nodes to the network for node, attrs in G.nodes(data=True): net.add_node( node, label=node, - title=f"Type: {attrs}", - color='lightblue' if attrs == 'location' else 'lightgreen' + title=f"Type: {attrs['node_type']}", + color='lightblue' if attrs['node_type'] == 'location' else 'lightgreen' ) + # Add edges, highlighting those in the optimal path for source, target, attrs in G.edges(data=True): condition = attrs['condition'] edge_label = f"Condition: {condition}" if condition else "No condition" + edge_color = 'blue' if (source, target) in optimal_edges else 'gray' net.add_edge( source, target, title=edge_label, label=edge_label, - color='red' if condition else 'gray' + color=edge_color ) - net.show('pokemon_map.html') + net.show('optimal_pokemon_map.html') + + net2 = Network(notebook=True) + # Add nodes with special styling for conditional grants + for node, attrs in G.nodes(data=True): + color = 'lightblue' + title = f"Type: {attrs.get('node_type', 'Unknown')}" + if 'grants_condition' in attrs: + title += f"
Grants: {attrs['grants_condition']}" + if 'required_conditions' in attrs: + title += f" (Requires: {attrs['required_conditions']})" + color = 'orange' # Highlight nodes with conditional grants + else: + color = 'green' # Nodes that grant conditions without prerequisites + net2.add_node(node, label=node, title=title, color=color) + + # Add edges + for source, target, attrs in G.edges(data=True): + condition = attrs.get('condition') + edge_label = f"Condition: {condition}" if condition else "No condition" + color = 'red' if condition else 'gray' + net2.add_edge(source, target, title=edge_label, label=edge_label, color=color) + + net2.show('pokemon_map_with_conditions.html') if __name__ == "__main__": diff --git a/Routes/Red_Blue_Route.py b/Routes/Red_Blue_Route.py new file mode 100644 index 0000000..763eb47 --- /dev/null +++ b/Routes/Red_Blue_Route.py @@ -0,0 +1,206 @@ +import networkx as nx +from networkx import Graph + +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' + +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.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]} + ] + 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 \ No newline at end of file diff --git a/Routes/__init__.py b/Routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/red_blue_goal_path.py b/red_blue_goal_path.py new file mode 100644 index 0000000..fc22eb5 --- /dev/null +++ b/red_blue_goal_path.py @@ -0,0 +1,145 @@ +import networkx as nx +from networkx import Graph +from pyvis.network import Network +import heapq +from copy import deepcopy + +from Routes.Red_Blue_Route import ARCTICUNO, BOULDER_BADGE, CASCADE_BADGE, CERULEAN_CAVE, CHAMPION, CUT, FLASH, GIOVANNI_FIGHT, MARSH_BADGE, MEWTWO, MOLTRES, PALLET_TOWN, POKE_FLUTE, QUENCHED_THURST, RAINBOW_BADGE, SILPH_SCOPE, SOUL_BADGE, SS_ANNE_TICKET, THUNDER_BADGE, VOLCANO_BADGE, ZAPDOS, get_red_blue_route + +class PlayerState: + def __init__(self): + self.visited_nodes = set() # Keeps track of nodes visited + self.conditions_met = set() # Tracks conditions the player has acquired + + def has_condition(self, condition): + return condition in self.conditions_met + + def add_condition(self, condition): + self.conditions_met.add(condition) + +def find_path_to_goal(G, start_node, goal_condition, player_state): + # Priority queue elements: (total_cost, current_node, path, state) + queue = [] + initial_state = deepcopy(player_state) + heapq.heappush(queue, (0, start_node, [start_node], initial_state)) + visited = set() + + while queue: + total_cost, current_node, path, state = heapq.heappop(queue) + + if current_node in visited: + continue + visited.add(current_node) + + # Visit the current node and update the player's state if needed + new_state = deepcopy(state) + visit_node(current_node, G, new_state) + + # Check if the goal condition is met + if goal_condition in new_state.conditions_met: + # Commit the updated state to the player state + player_state.visited_nodes = new_state.visited_nodes + player_state.conditions_met = new_state.conditions_met + return path + + # Explore neighbors + for neighbor in G.neighbors(current_node): + edge_attrs = G[current_node][neighbor] + if can_traverse(edge_attrs, new_state): + edge_weight = edge_attrs.get('weight', 1) + new_total_cost = total_cost + edge_weight + new_path = path + [neighbor] + heapq.heappush(queue, (new_total_cost, neighbor, new_path, new_state)) + + return None # No path found to the goal + +def find_optimal_path_with_goals(G, start_node, end_node, goals): + current_node = start_node + full_path = [start_node] # Start with the starting node in the path + player_state = PlayerState() + + # Iterate through each goal to find paths in succession + for goal in goals: + path_to_goal = find_path_to_goal(G, current_node, goal, player_state) + if path_to_goal is None: + print(f"Failed to find a path to the goal: {goal}") + return None + + # Extend the full path with all nodes in path_to_goal + # Avoid repeating the current_node, so exclude the first node + full_path.extend(path_to_goal[1:]) + + # Update current node to the goal's location + current_node = path_to_goal[-1] + + # Finally, find the path to the end node + #path_to_end = find_path_to_goal(G, current_node, None, player_state) + #if path_to_end is None: + # print("Failed to find a path to the end node.") + # return None + + # Extend the full path with all nodes in path_to_end, excluding the first node to avoid repetition + #full_path.extend(path_to_end[1:]) + + return full_path + +def visit_node(node_name, G, player_state: PlayerState): + # Add the current node to the visited set + player_state.visited_nodes.add(node_name) + + # Grant conditions or items found at this node + node_attrs = G.nodes[node_name] + grants_conditions = node_attrs.get('grants_conditions', []) + + for grant in grants_conditions: + condition = grant['condition'] + required_conditions = grant.get('required_conditions', []) + + # Only add conditions if requirements are met + if all(cond in player_state.conditions_met for cond in required_conditions): + if not player_state.has_condition(condition): + player_state.add_condition(condition) + print(f"Condition '{condition}' granted upon visiting '{node_name}'.") + +def can_traverse(edge_attrs, player_state: PlayerState): + # Check if the player meets all the conditions required to traverse this edge + conditions = edge_attrs.get('condition') + if not conditions: + return True + return all(condition in player_state.conditions_met for condition in conditions) + +# Example usage +if __name__ == "__main__": + + G = get_red_blue_route() + + # Define the ordered goals that the player must acquire + goals = [ + BOULDER_BADGE, + CASCADE_BADGE, + SS_ANNE_TICKET, + CUT, + THUNDER_BADGE, + FLASH, + SILPH_SCOPE, + RAINBOW_BADGE, + QUENCHED_THURST, + POKE_FLUTE, + GIOVANNI_FIGHT, + MARSH_BADGE, + SOUL_BADGE, + VOLCANO_BADGE, + ARCTICUNO, + ZAPDOS, + MOLTRES, + CHAMPION, + MEWTWO + ] + + # Find the optimal path while fulfilling goals in succession + optimal_path = find_optimal_path_with_goals(G, PALLET_TOWN, CERULEAN_CAVE, goals) + + if optimal_path: + print("Optimal Path:", " -> ".join(optimal_path)) + else: + print("No path found to fulfill all goals.")