From 939e357668010181e8507532c3976eeb47136afa Mon Sep 17 00:00:00 2001 From: that-ar-guy Date: Wed, 26 Feb 2025 15:54:13 +0530 Subject: [PATCH] added a star --- pysnippets/Path_Algoithms/a_star.py | 41 ++++++++++++++++++++ pysnippets/Path_Algoithms/test_a_star.py | 49 ++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 pysnippets/Path_Algoithms/a_star.py create mode 100644 pysnippets/Path_Algoithms/test_a_star.py diff --git a/pysnippets/Path_Algoithms/a_star.py b/pysnippets/Path_Algoithms/a_star.py new file mode 100644 index 0000000..813c5dc --- /dev/null +++ b/pysnippets/Path_Algoithms/a_star.py @@ -0,0 +1,41 @@ +from typing import Tuple, List, Dict +from heapq import heappop, heappush +import math +from data_classes import Graph +from logger_config import logger + +def heuristic(node: Tuple[int, int], goal: Tuple[int, int]) -> float: + """Euclidean distance heuristic.""" + return math.sqrt((node[0] - goal[0])**2 + (node[1] - goal[1])**2) + +def a_star(graph: Graph, start: Tuple[int, int], end: Tuple[int, int]) -> Tuple[List[Tuple[int, int]], float]: + try: + open_set = [] + heappush(open_set, (0, start)) + g_score: Dict[Tuple[int, int], float] = {vertex: float('inf') for vertex in graph.vertices} + g_score[start] = 0 + came_from: Dict[Tuple[int, int], Tuple[int, int]] = {} + + while open_set: + _, current = heappop(open_set) + + if current == end: + path = [] + while current in came_from: + path.insert(0, current) + current = came_from[current] + path.insert(0, start) + return path, g_score[end] + + for neighbor, weight in graph.edges.get(current, {}).items(): + tentative_g_score = g_score[current] + weight + if tentative_g_score < g_score[neighbor]: + g_score[neighbor] = tentative_g_score + f_score = tentative_g_score + heuristic(neighbor, end) + heappush(open_set, (f_score, neighbor)) + came_from[neighbor] = current + + raise ValueError("Path not found") + except Exception as e: + logger.error(f"Error in A* algorithm: {e}") + raise \ No newline at end of file diff --git a/pysnippets/Path_Algoithms/test_a_star.py b/pysnippets/Path_Algoithms/test_a_star.py new file mode 100644 index 0000000..99f78cd --- /dev/null +++ b/pysnippets/Path_Algoithms/test_a_star.py @@ -0,0 +1,49 @@ +import unittest +from data_classes import Graph +from a_star import a_star +import sys +import os +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +class TestAStar(unittest.TestCase): + def setUp(self): + vertices = {(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)} + edges = { + (0, 0): {(1, 0): 1}, + (1, 0): {(1, 1): 1, (0, 0): 1}, + (1, 1): {(2, 1): 1, (1, 0): 1}, + (2, 1): {(2, 2): 1, (1, 1): 1}, + (2, 2): {(2, 1): 1} + } + self.graph = Graph(vertices, edges) + self.graph.vertices = { + (0, 0), (1, 0), (1, 1), (2, 1), (2, 2) + } + self.graph.edges = { + (0, 0): {(1, 0): 1}, + (1, 0): {(1, 1): 1, (0, 0): 1}, + (1, 1): {(2, 1): 1, (1, 0): 1}, + (2, 1): {(2, 2): 1, (1, 1): 1}, + (2, 2): {(2, 1): 1} + } + + def test_valid_path(self): + path, cost = a_star(self.graph, (0, 0), (2, 2)) + self.assertEqual(path, [(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)]) + self.assertEqual(cost, 4) + + def test_no_path(self): + self.graph.edges = { + (0, 0): {(1, 0): 1}, + (1, 0): {(0, 0): 1}, + (2, 2): {} # Disconnected node + } + with self.assertRaises(ValueError): + a_star(self.graph, (0, 0), (2, 2)) + + def test_same_start_end(self): + path, cost = a_star(self.graph, (1, 1), (1, 1)) + self.assertEqual(path, [(1, 1)]) + self.assertEqual(cost, 0) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file