1
1
/**
2
- * @author Mathang Peddi
2
+ * @author : Mathang Peddi
3
3
* A* Algorithm calculates the minimum cost path between two nodes.
4
4
* It is used to find the shortest path using heuristics.
5
5
* It uses graph data structure.
6
6
*/
7
7
8
8
// Euclidean distance heuristic for 2D points
9
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 ) ;
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 = [ ]
13
19
}
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
24
37
}
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
34
43
}
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
39
64
}
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
67
74
}
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
68
83
}
69
-
70
- isEmpty ( ) {
71
- return this . elements . length === 0 ;
72
- }
73
84
}
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 ]
99
112
}
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 )
113
130
}
114
131
}
115
-
116
- return null ; // Return null if there's no path to the target
117
132
}
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