11/**
2- * @author Mathang Peddi
2+ * @author : Mathang Peddi
33 * A* Algorithm calculates the minimum cost path between two nodes.
44 * It is used to find the shortest path using heuristics.
55 * It uses graph data structure.
66 */
77
88// Euclidean distance heuristic for 2D points
99function 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 ) ;
10+ const dx = pointA [ 0 ] - pointB [ 0 ]
11+ const dy = pointA [ 1 ] - pointB [ 1 ]
12+ return Math . sqrt ( dx * dx + dy * dy )
13+ }
14+
15+ // Priority Queue (Min-Heap) implementation
16+ class PriorityQueue {
17+ constructor ( ) {
18+ this . elements = [ ]
1319 }
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 ( ) ;
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 )
31+ break
32+ ; [ this . elements [ index ] , this . elements [ parentIndex ] ] = [
33+ this . elements [ parentIndex ] ,
34+ this . elements [ index ]
35+ ]
36+ index = parentIndex
2437 }
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 ;
33- }
38+ }
39+
40+ dequeue ( ) {
41+ if ( this . elements . length === 1 ) {
42+ return this . elements . pop ( ) . node
3443 }
35-
36- dequeue ( ) {
37- if ( this . elements . length === 1 ) {
38- return this . elements . pop ( ) . node ;
44+
45+ const node = this . elements [ 0 ] . node
46+ this . elements [ 0 ] = this . elements . pop ( )
47+ this . sinkDown ( 0 )
48+ return node
49+ }
50+
51+ sinkDown ( index ) {
52+ const length = this . elements . length
53+ const element = this . elements [ index ]
54+ while ( true ) {
55+ let leftChildIndex = 2 * index + 1
56+ let rightChildIndex = 2 * index + 2
57+ let swapIndex = null
58+
59+ if (
60+ leftChildIndex < length &&
61+ this . elements [ leftChildIndex ] . priority < element . priority
62+ ) {
63+ swapIndex = leftChildIndex
3964 }
40-
41- const node = this . elements [ 0 ] . node ;
42- this . elements [ 0 ] = this . elements . pop ( ) ;
43- this . sinkDown ( 0 ) ;
44- return node ;
45- }
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 ;
61- }
62-
63- if ( swapIndex === null ) break ;
64-
65- [ this . elements [ index ] , this . elements [ swapIndex ] ] = [ this . elements [ swapIndex ] , this . elements [ index ] ] ;
66- index = swapIndex ;
65+
66+ if (
67+ rightChildIndex < length &&
68+ this . elements [ rightChildIndex ] . priority <
69+ ( swapIndex === null
70+ ? element . priority
71+ : this . elements [ leftChildIndex ] . priority )
72+ ) {
73+ swapIndex = rightChildIndex
6774 }
75+
76+ if ( swapIndex === null ) break
77+
78+ ; [ this . elements [ index ] , this . elements [ swapIndex ] ] = [
79+ this . elements [ swapIndex ] ,
80+ this . elements [ index ]
81+ ]
82+ index = swapIndex
6883 }
69-
70- isEmpty ( ) {
71- return this . elements . length === 0 ;
72- }
7384 }
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 ( ) ;
85+
86+ isEmpty ( ) {
87+ return this . elements . length === 0
88+ }
89+ }
90+
91+ function aStar ( graph , src , target , points ) {
92+ const openSet = new PriorityQueue ( ) // Priority queue to explore nodes
93+ openSet . enqueue ( src , 0 )
94+
95+ const cameFrom = Array ( graph . length ) . fill ( null ) // Keep track of path
96+ const gScore = Array ( graph . length ) . fill ( Infinity ) // Actual cost from start to a node
97+ gScore [ src ] = 0
98+
99+ const fScore = Array ( graph . length ) . fill ( Infinity ) // Estimated cost from start to goal (g + h)
100+ fScore [ src ] = euclideanHeuristic ( points [ src ] , points [ target ] )
101+
102+ while ( ! openSet . isEmpty ( ) ) {
103+ // Get the node in openSet with the lowest fScore
104+ const current = openSet . dequeue ( )
105+
106+ // If the current node is the target, reconstruct the path and return
107+ if ( current === target ) {
108+ const path = [ ]
109+ while ( cameFrom [ current ] !== - 1 ) {
110+ path . push ( current )
111+ current = cameFrom [ current ]
99112 }
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+ path . push ( src )
114+ return path . reverse ( )
115+ }
116+
117+ // Explore neighbors using destructuring for cleaner code
118+ for ( const [ neighbor , weight ] of graph [ current ] ) {
119+ const tentative_gScore = gScore [ current ] + weight
120+
121+ if ( tentative_gScore < gScore [ neighbor ] ) {
122+ cameFrom [ neighbor ] = current
123+ gScore [ neighbor ] = tentative_gScore
124+ const priority =
125+ gScore [ neighbor ] +
126+ euclideanHeuristic ( points [ neighbor ] , points [ target ] )
127+ fScore [ neighbor ] = priority
128+
129+ openSet . enqueue ( neighbor , priority )
113130 }
114131 }
115-
116- return null ; // Return null if there's no path to the target
117132 }
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-
133+
134+ return null // Return null if there's no path to the target
135+ }
136+
137+ // Define the graph as an adjacency list
138+ const graph = [
139+ [
140+ [ 1 , 4 ] ,
141+ [ 7 , 8 ]
142+ ] , // Node 0 connects to node 1 (weight 4), node 7 (weight 8)
143+ [
144+ [ 0 , 4 ] ,
145+ [ 2 , 8 ] ,
146+ [ 7 , 11 ]
147+ ] , // Node 1 connects to node 0, node 2, node 7
148+ [
149+ [ 1 , 8 ] ,
150+ [ 3 , 7 ] ,
151+ [ 5 , 4 ] ,
152+ [ 8 , 2 ]
153+ ] , // Node 2 connects to several nodes
154+ [
155+ [ 2 , 7 ] ,
156+ [ 4 , 9 ] ,
157+ [ 5 , 14 ]
158+ ] , // Node 3 connects to nodes 2, 4, 5
159+ [
160+ [ 3 , 9 ] ,
161+ [ 5 , 10 ]
162+ ] , // Node 4 connects to nodes 3 and 5
163+ [
164+ [ 2 , 4 ] ,
165+ [ 3 , 14 ] ,
166+ [ 4 , 10 ] ,
167+ [ 6 , 2 ]
168+ ] , // Node 5 connects to several nodes
169+ [
170+ [ 5 , 2 ] ,
171+ [ 7 , 1 ] ,
172+ [ 8 , 6 ]
173+ ] , // Node 6 connects to nodes 5, 7, 8
174+ [
175+ [ 0 , 8 ] ,
176+ [ 1 , 11 ] ,
177+ [ 6 , 1 ] ,
178+ [ 8 , 7 ]
179+ ] , // Node 7 connects to several nodes
180+ [
181+ [ 2 , 2 ] ,
182+ [ 6 , 6 ] ,
183+ [ 7 , 7 ]
184+ ] // Node 8 connects to nodes 2, 6, 7
185+ ]
186+
187+ // Define 2D coordinates for each node (these can be changed based on actual node positions)
188+ const points = [
189+ [ 0 , 0 ] , // Point for node 0
190+ [ 1 , 2 ] , // Point for node 1
191+ [ 2 , 1 ] , // Point for node 2
192+ [ 3 , 5 ] , // Point for node 3
193+ [ 4 , 3 ] , // Point for node 4
194+ [ 5 , 6 ] , // Point for node 5
195+ [ 6 , 8 ] , // Point for node 6
196+ [ 7 , 10 ] , // Point for node 7
197+ [ 8 , 12 ] // Point for node 8
198+ ]
199+
200+ // Call the aStar function with the graph, source node (0), and target node (4)
201+ const path = aStar ( graph , 0 , 4 , points )
202+
203+ console . log ( 'Shortest path from node 0 to node 4:' , path )
204+
205+ /**
206+ * The function returns the optimal path from the source to the target node.
207+ * The heuristic used is Euclidean distance between nodes' 2D coordinates.
208+ */
0 commit comments