4 changed files with 483 additions and 184 deletions
@ -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 |
||||
@ -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.") |
||||
Loading…
Reference in new issue