Browse Source

- Add in AStar pathfinding

rough-outline
Dan 1 year ago
parent
commit
4509054f48
  1. 1
      Routes/Gold_Silver_Route.py
  2. 135
      Routes/pokemon_game_desc.py
  3. 2
      convert_to_pddl.py
  4. 9
      red_blue_goal_path.py

1
Routes/Gold_Silver_Route.py

@ -104,6 +104,7 @@ def get_gold_silver_desc() -> PokemonGameDesc:
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.must_visit = set([MT_MOON])
desc.starting_town = NEW_BARK_TOWN
desc.end_goal = MT_SILVER

135
Routes/pokemon_game_desc.py

@ -1,6 +1,33 @@
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 = ""
@ -13,7 +40,113 @@ class PokemonGameDesc:
self.flying_badge: str
self.additional_goals: List[str] = []
self.one_way_routes: List[str] = []
self.has_visited: List[str] = []
self.must_visit: Set[str] = set()
self.graph: nx.Graph = nx.Graph()
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"]

2
convert_to_pddl.py

@ -158,7 +158,7 @@ def generate_pddl_problem(desc: PokemonGameDesc):
# Goal state
badges = desc.badges
additional_conditions = desc.additional_goals
visited = desc.has_visited
visited = desc.must_visit
problem_pddl += " (:goal\n"
problem_pddl += " (and\n"
problem_pddl += f" (at {format_name(desc.end_goal)})\n"

9
red_blue_goal_path.py

@ -184,5 +184,10 @@ if __name__ == "__main__":
red_blue = get_red_blue_desc()
gold_silver = get_gold_silver_desc()
generate_pddl_problem(red_blue)
generate_pddl_problem(gold_silver)
path, _, _ = red_blue.astar_search()
print(path)
path, _, _ = gold_silver.astar_search()
print(path)
#generate_pddl_problem(red_blue)
#generate_pddl_problem(gold_silver)

Loading…
Cancel
Save