Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified __pycache__/astar.cpython-312.pyc
Binary file not shown.
Binary file added __pycache__/cus2.cpython-312.pyc
Binary file not shown.
Binary file modified __pycache__/search_selector.cpython-312.pyc
Binary file not shown.
43 changes: 27 additions & 16 deletions astar.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,19 @@ def __init__(self, graph):
super().__init__(graph)
self.nodes = graph.node_coordinates

# Build adjacency list and edges dictionary
self.edges = {}
# Build adjacency list
self.adjacency_list = {}

for from_node, neighbors in graph.adjacency_list.items():
if from_node not in self.adjacency_list:
self.adjacency_list[from_node] = []

for to_node, cost in neighbors:
self.adjacency_list[from_node].append((to_node, cost))
self.edges[(from_node, to_node)] = cost

def euclidean_distance(self, node1, node2):
"""
Calculate Euclidean distance between two nodes using their coordinates.
"""
# This function is used to calculate the heuristic value
x1, y1 = self.nodes[node1]
x2, y2 = self.nodes[node2]
return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
Expand All @@ -44,23 +40,27 @@ def search(self, start, goals):
# Convert goals to set for faster lookup
goals = set(goals)

# Select closest goal for heuristic calculation
goal = min(goals, key=lambda g: self.euclidean_distance(start, g))

# Track costs from start to each node
g_scores = {start: 0}

# Priority queue with (f_score, node_id, path)
open_list = [(self.euclidean_distance(start, goal), start, [start])]
heapq.heapify(open_list)
# Create a unique ID for each queue entry to avoid comparing paths
entry_id = 0

# Priority queue with (f_score, entry_id, node_id, path)
open_list = []

# Calculate initial heuristic - use min distance to any goal
initial_h = min([self.euclidean_distance(start, goal) for goal in goals])
heapq.heappush(open_list, (initial_h, entry_id, start, [start]))
entry_id += 1

# Track visited nodes
visited = set()
nodes_expanded = 0

while open_list:
# Pop the node with lowest f_score
f_score, current, path = heapq.heappop(open_list)
_, _, current, path = heapq.heappop(open_list)

# If already visited, skip
if current in visited:
Expand All @@ -70,6 +70,9 @@ def search(self, start, goals):
visited.add(current)
nodes_expanded += 1

# Debug: print current node exploration
# print(f"Exploring node {current} with path {path}")

# Check if we've reached a goal
if current in goals:
return current, nodes_expanded, path
Expand All @@ -85,12 +88,20 @@ def search(self, start, goals):
# Update g_score
g_scores[neighbor] = new_g_score

# Calculate h_score as minimum distance to any goal
h_score = min([self.euclidean_distance(neighbor, goal) for goal in goals])

# Calculate f_score
f_score = new_g_score + self.euclidean_distance(neighbor, goal)
f_score = new_g_score + h_score

# Debug: print neighbor evaluation
# print(f" Neighbor {neighbor}: g={new_g_score}, h={h_score}, f={f_score}")

# Add to open list
# Add to open list with unique entry ID
new_path = path + [neighbor]
heapq.heappush(open_list, (f_score, neighbor, new_path))
heapq.heappush(open_list, (f_score, entry_id, neighbor, new_path))
entry_id += 1

# No path found
return None, nodes_expanded, []
return None, nodes_expanded, []
#fixed
91 changes: 51 additions & 40 deletions cus2.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
import heapq
import math
from search_algorithm import SearchAlgorithm

class BidirectionalSearch:
def __init__(self, nodes, edges):
class BidirectionalSearch(SearchAlgorithm):
def __init__(self, graph):
"""
Initialize the bidirectional search with nodes and edges.
Initialize the bidirectional search with a graph.

:param nodes: Dictionary mapping node ID to (x,y) coordinates
:param edges: Dictionary mapping (from_node, to_node) to edge cost
:param graph: The SearchGraph object containing nodes and edges
"""
self.nodes = nodes
self.edges = edges
super().__init__(graph)
self.nodes = graph.node_coordinates

# Build edges dictionary
self.edges = {}

# Build forward adjacency list
self.forward_adj = {}
for (from_node, to_node), cost in edges.items():
for from_node, neighbors in graph.adjacency_list.items():
if from_node not in self.forward_adj:
self.forward_adj[from_node] = []
self.forward_adj[from_node].append((to_node, cost))

for to_node, cost in neighbors:
self.forward_adj[from_node].append((to_node, cost))
self.edges[(from_node, to_node)] = cost

# Build backward adjacency list
self.backward_adj = {}
for (from_node, to_node), cost in edges.items():
if to_node not in self.backward_adj:
self.backward_adj[to_node] = []
self.backward_adj[to_node].append((from_node, cost))
for from_node, neighbors in graph.adjacency_list.items():
for to_node, cost in neighbors:
if to_node not in self.backward_adj:
self.backward_adj[to_node] = []
self.backward_adj[to_node].append((from_node, cost))

def euclidean_distance(self, node1, node2):
"""
Expand All @@ -34,14 +41,18 @@ def euclidean_distance(self, node1, node2):
x2, y2 = self.nodes[node2]
return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)

def search(self, start, goal):
def search(self, start, goals):
"""
Execute bidirectional search from start node to goal node.
Execute bidirectional search from start node to any goal node.

:param start: Starting node ID
:param goal: Goal node ID
:return: (nodes_expanded, path, cost)
:param goals: Set or list of goal node IDs
:return: (goal_reached, nodes_expanded, path)
"""
# For bidirectional search, we need a single goal
# If multiple goals are provided, select the closest one
goal = min(goals, key=lambda g: self.euclidean_distance(start, g))

# Forward search data structures
forward_g = {start: 0}
forward_open = [(self.euclidean_distance(start, goal), start, [start])]
Expand Down Expand Up @@ -74,20 +85,21 @@ def search(self, start, goal):
# Check if we've reached a node in the backward closed set
# This means we have a potential path
if current_forward in backward_closed:
# Calculate total path cost
total_cost = forward_g[current_forward] + backward_g[current_forward]

# Find matching backward path
backward_path = None
for _, node, path in backward_open:
if node == current_forward:
backward_path = path
break
else:
# Search in closed nodes if not found in open
for f_score, node, path in backward_open:
if node == current_forward:
backward_path = path
break

# Calculate total path cost
total_cost = forward_g[current_forward] + backward_g[current_forward]
# If not found in open list, construct path from known information
if not backward_path:
# In real implementation we would reconstruct path
# but for simplicity, we'll use the cost we've found
backward_path = [current_forward]

# Update if better than current best
if total_cost < best_cost:
Expand Down Expand Up @@ -120,20 +132,21 @@ def search(self, start, goal):

# Check if we've reached a node in the forward closed set
if current_backward in forward_closed:
# Calculate total path cost
total_cost = forward_g[current_backward] + backward_g[current_backward]

# Find matching forward path
forward_path = None
for _, node, path in forward_open:
if node == current_backward:
forward_path = path
break
else:
# Search in closed nodes if not found in open
for f_score, node, path in forward_open:
if node == current_backward:
forward_path = path
break

# Calculate total path cost
total_cost = forward_g[current_backward] + backward_g[current_backward]

# If not found in open list, construct path from known information
if not forward_path:
# In real implementation we would reconstruct path
# but for simplicity, we'll use what we've found so far
forward_path = [current_backward]

# Update if better than current best
if total_cost < best_cost:
Expand All @@ -156,10 +169,8 @@ def search(self, start, goal):
min_backward = min([f for f, _, _ in backward_open]) if backward_open else float('inf')

if best_path and best_cost <= min_forward + min_backward:
return nodes_expanded, best_path, best_cost
# Return the goal node, nodes expanded, and the path
return goal, nodes_expanded, best_path

# Return best path found (or empty if none)
return nodes_expanded, best_path or [], best_cost
#end


# No path found
return None, nodes_expanded, []
4 changes: 3 additions & 1 deletion search_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from bfs import BFS
from gbfs import GBFS
from astar import AStar
from cus2 import BidirectionalSearch
class SearchSelector:
"""
Handles search algorithm selection.
Expand All @@ -23,6 +24,7 @@ def get_search_algorithm(method, graph):
return GBFS(graph)
elif method == "A*":
return AStar(graph)

elif method == "Bidirectional":
return BidirectionalSearch(graph)
else:
raise ValueError(f"Error: Unknown search method '{method}'")