11/**
2- * Author: Mathang Peddi
3- * A* Search Algorithm implementation in JavaScript
2+ * @author Mathang Peddi
43 * A* Algorithm calculates the minimum cost path between two nodes.
54 * It is used to find the shortest path using heuristics.
65 * It uses graph data structure.
76 */
87
9- function createGraph ( V , E ) {
10- // V - Number of vertices in graph
11- // E - Number of edges in graph (u,v,w)
12- const adjList = [ ] // Adjacency list
13- for ( let i = 0 ; i < V ; i ++ ) {
14- adjList . push ( [ ] )
8+ // Euclidean distance heuristic for 2D points
9+ function euclideanHeuristic ( pointA , pointB ) {
10+ const dx = pointA [ 0 ] - pointB [ 0 ] ;
11+ const dy = pointA [ 1 ] - pointB [ 1 ] ;
12+ return Math . sqrt ( dx * dx + dy * dy ) ;
1513 }
16- for ( let i = 0 ; i < E . length ; i ++ ) {
17- adjList [ E [ i ] [ 0 ] ] . push ( [ E [ i ] [ 1 ] , E [ i ] [ 2 ] ] )
18- adjList [ E [ i ] [ 1 ] ] . push ( [ E [ i ] [ 0 ] , E [ i ] [ 2 ] ] )
19- }
20- return adjList
21- }
22-
23- // Heuristic function to estimate the cost to reach the goal
24- // You can modify this based on your specific problem, for now, we're using Manhattan distance
25- function heuristic ( a , b ) {
26- return Math . abs ( a - b )
27- }
28-
29- function aStar ( graph , V , src , target ) {
30- const openSet = new Set ( [ src ] ) // Nodes to explore
31- const cameFrom = Array ( V ) . fill ( - 1 ) // Keep track of path
32- const gScore = Array ( V ) . fill ( Infinity ) // Actual cost from start to a node
33- gScore [ src ] = 0
34-
35- const fScore = Array ( V ) . fill ( Infinity ) // Estimated cost from start to goal (g + h)
36- fScore [ src ] = heuristic ( src , target )
37-
38- while ( openSet . size > 0 ) {
39- // Get the node in openSet with the lowest fScore
40- let current = - 1
41- openSet . forEach ( ( node ) => {
42- if ( current === - 1 || fScore [ node ] < fScore [ current ] ) {
43- current = node
14+
15+ // Priority Queue (Min-Heap) implementation
16+ class PriorityQueue {
17+ constructor ( ) {
18+ this . elements = [ ] ;
19+ }
20+
21+ enqueue ( node , priority ) {
22+ this . elements . push ( { node, priority } ) ;
23+ this . bubbleUp ( ) ;
24+ }
25+
26+ bubbleUp ( ) {
27+ let index = this . elements . length - 1 ;
28+ while ( index > 0 ) {
29+ let parentIndex = Math . floor ( ( index - 1 ) / 2 ) ;
30+ if ( this . elements [ index ] . priority >= this . elements [ parentIndex ] . priority ) break ;
31+ [ this . elements [ index ] , this . elements [ parentIndex ] ] = [ this . elements [ parentIndex ] , this . elements [ index ] ] ;
32+ index = parentIndex ;
4433 }
45- } )
46-
47- // If the current node is the target, reconstruct the path and return
48- if ( current === target ) {
49- const path = [ ]
50- while ( cameFrom [ current ] !== - 1 ) {
51- path . push ( current )
52- current = cameFrom [ current ]
34+ }
35+
36+ dequeue ( ) {
37+ if ( this . elements . length === 1 ) {
38+ return this . elements . pop ( ) . node ;
5339 }
54- path . push ( src )
55- return path . reverse ( )
40+
41+ const node = this . elements [ 0 ] . node ;
42+ this . elements [ 0 ] = this . elements . pop ( ) ;
43+ this . sinkDown ( 0 ) ;
44+ return node ;
5645 }
57-
58- openSet . delete ( current )
59-
60- // Explore neighbors
61- for ( let i = 0 ; i < graph [ current ] . length ; i ++ ) {
62- const neighbor = graph [ current ] [ i ] [ 0 ]
63- const tentative_gScore = gScore [ current ] + graph [ current ] [ i ] [ 1 ]
64-
65- if ( tentative_gScore < gScore [ neighbor ] ) {
66- cameFrom [ neighbor ] = current
67- gScore [ neighbor ] = tentative_gScore
68- fScore [ neighbor ] = gScore [ neighbor ] + heuristic ( neighbor , target )
69-
70- if ( ! openSet . has ( neighbor ) ) {
71- openSet . add ( neighbor )
46+
47+ sinkDown ( index ) {
48+ const length = this . elements . length ;
49+ const element = this . elements [ index ] ;
50+ while ( true ) {
51+ let leftChildIndex = 2 * index + 1 ;
52+ let rightChildIndex = 2 * index + 2 ;
53+ let swapIndex = null ;
54+
55+ if ( leftChildIndex < length && this . elements [ leftChildIndex ] . priority < element . priority ) {
56+ swapIndex = leftChildIndex ;
57+ }
58+
59+ if ( rightChildIndex < length && this . elements [ rightChildIndex ] . priority < ( swapIndex === null ? element . priority : this . elements [ leftChildIndex ] . priority ) ) {
60+ swapIndex = rightChildIndex ;
7261 }
62+
63+ if ( swapIndex === null ) break ;
64+
65+ [ this . elements [ index ] , this . elements [ swapIndex ] ] = [ this . elements [ swapIndex ] , this . elements [ index ] ] ;
66+ index = swapIndex ;
7367 }
7468 }
69+
70+ isEmpty ( ) {
71+ return this . elements . length === 0 ;
72+ }
7573 }
76-
77- return [ ] // Return empty path if there's no path to the target
78- }
79-
80- module . exports = { createGraph, aStar }
81-
82- // const V = 9
83- // const E = [
84- // [0, 1, 4],
85- // [0, 7, 8],
86- // [1, 7, 11],
87- // [1, 2, 8],
88- // [7, 8, 7],
89- // [6, 7, 1],
90- // [2, 8, 2],
91- // [6, 8, 6],
92- // [5, 6, 2],
93- // [2, 5, 4],
94- // [2, 3, 7],
95- // [3, 5, 14],
96- // [3, 4, 9],
97- // [4, 5, 10]
98- // ]
99-
100- // const graph = createGraph(V, E)
101- // const path = aStar(graph, V, 0, 4) // Find path from node 0 to node 4
102- // console.log(path)
103-
104- /**
105- * The function returns the optimal path from the source to the target node.
106- * The heuristic used is Manhattan distance but it can be modified.
107- */
74+
75+ function aStar ( graph , src , target , points ) {
76+ const openSet = new PriorityQueue ( ) ; // Priority queue to explore nodes
77+ openSet . enqueue ( src , 0 ) ;
78+
79+ const cameFrom = Array ( graph . length ) . fill ( null ) ; // Keep track of path
80+ const gScore = Array ( graph . length ) . fill ( Infinity ) ; // Actual cost from start to a node
81+ gScore [ src ] = 0 ;
82+
83+ const fScore = Array ( graph . length ) . fill ( Infinity ) ; // Estimated cost from start to goal (g + h)
84+ fScore [ src ] = euclideanHeuristic ( points [ src ] , points [ target ] ) ;
85+
86+ while ( ! openSet . isEmpty ( ) ) {
87+ // Get the node in openSet with the lowest fScore
88+ const current = openSet . dequeue ( ) ;
89+
90+ // If the current node is the target, reconstruct the path and return
91+ if ( current === target ) {
92+ const path = [ ] ;
93+ while ( cameFrom [ current ] !== - 1 ) {
94+ path . push ( current ) ;
95+ current = cameFrom [ current ] ;
96+ }
97+ path . push ( src ) ;
98+ return path . reverse ( ) ;
99+ }
100+
101+ // Explore neighbors using destructuring for cleaner code
102+ for ( const [ neighbor , weight ] of graph [ current ] ) {
103+ const tentative_gScore = gScore [ current ] + weight ;
104+
105+ if ( tentative_gScore < gScore [ neighbor ] ) {
106+ cameFrom [ neighbor ] = current ;
107+ gScore [ neighbor ] = tentative_gScore ;
108+ const priority = gScore [ neighbor ] + euclideanHeuristic ( points [ neighbor ] , points [ target ] ) ;
109+ fScore [ neighbor ] = priority ;
110+
111+ openSet . enqueue ( neighbor , priority ) ;
112+ }
113+ }
114+ }
115+
116+ return null ; // Return null if there's no path to the target
117+ }
118+
119+ // Define the graph as an adjacency list
120+ const graph = [
121+ [ [ 1 , 4 ] , [ 7 , 8 ] ] , // Node 0 connects to node 1 (weight 4), node 7 (weight 8)
122+ [ [ 0 , 4 ] , [ 2 , 8 ] , [ 7 , 11 ] ] , // Node 1 connects to node 0, node 2, node 7
123+ [ [ 1 , 8 ] , [ 3 , 7 ] , [ 5 , 4 ] , [ 8 , 2 ] ] , // Node 2 connects to several nodes
124+ [ [ 2 , 7 ] , [ 4 , 9 ] , [ 5 , 14 ] ] , // Node 3 connects to nodes 2, 4, 5
125+ [ [ 3 , 9 ] , [ 5 , 10 ] ] , // Node 4 connects to nodes 3 and 5
126+ [ [ 2 , 4 ] , [ 3 , 14 ] , [ 4 , 10 ] , [ 6 , 2 ] ] , // Node 5 connects to several nodes
127+ [ [ 5 , 2 ] , [ 7 , 1 ] , [ 8 , 6 ] ] , // Node 6 connects to nodes 5, 7, 8
128+ [ [ 0 , 8 ] , [ 1 , 11 ] , [ 6 , 1 ] , [ 8 , 7 ] ] , // Node 7 connects to several nodes
129+ [ [ 2 , 2 ] , [ 6 , 6 ] , [ 7 , 7 ] ] // Node 8 connects to nodes 2, 6, 7
130+ ] ;
131+
132+ // Define 2D coordinates for each node (these can be changed based on actual node positions)
133+ const points = [
134+ [ 0 , 0 ] , // Point for node 0
135+ [ 1 , 2 ] , // Point for node 1
136+ [ 2 , 1 ] , // Point for node 2
137+ [ 3 , 5 ] , // Point for node 3
138+ [ 4 , 3 ] , // Point for node 4
139+ [ 5 , 6 ] , // Point for node 5
140+ [ 6 , 8 ] , // Point for node 6
141+ [ 7 , 10 ] , // Point for node 7
142+ [ 8 , 12 ] // Point for node 8
143+ ] ;
144+
145+ // Call the aStar function with the graph, source node (0), and target node (4)
146+ const path = aStar ( graph , 0 , 4 , points ) ;
147+
148+ console . log ( 'Shortest path from node 0 to node 4:' , path ) ;
149+
150+ /**
151+ * The function returns the optimal path from the source to the target node.
152+ * The heuristic used is Euclidean distance between nodes' 2D coordinates.
153+ */
154+
0 commit comments