-
Notifications
You must be signed in to change notification settings - Fork 178
/
Copy pathcode.js
140 lines (125 loc) · 3.64 KB
/
code.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// import visualization libraries {
const {
Tracer,
Array1DTracer,
GraphTracer,
Randomize,
Layout,
VerticalLayout,
} = require("algorithm-visualizer");
// }
const G = Randomize.Graph({
N: 5,
ratio: 0.6,
directed: false,
weighted: true,
});
const MAX_VALUE = Infinity;
const S = []; // S[end] returns the distance from start node to end node
for (let i = 0; i < G.length; i++) S[i] = MAX_VALUE;
// Heuristic function based on actual distances between nodes
function heuristic(node, goal) {
if (G[node][goal] !== 0) {
return G[node][goal]; // Use the direct edge weight if available
} else {
// Use an alternative if no direct edge exists (optional)
let minEdge = MAX_VALUE;
for (let i = 0; i < G.length; i++) {
if (G[node][i] !== 0 && G[node][i] < minEdge) {
minEdge = G[node][i];
}
}
return minEdge !== MAX_VALUE ? minEdge : 1; // Use the smallest edge connected to the node as a fallback heuristic
}
}
// define tracer variables {
const tracer = new GraphTracer().directed(false).weighted();
const tracerS = new Array1DTracer();
Layout.setRoot(new VerticalLayout([tracer, tracerS]));
tracer.set(G);
tracerS.set(S);
Tracer.delay();
// }
function AStar(start, end) {
let minIndex;
let minDistance;
const D = []; // D[i] indicates whether the i-th node is discovered or not
const openSet = []; // Nodes to be evaluated
const previous = []; // Array to store the previous node for path reconstruction
for (let i = 0; i < G.length; i++) {
D.push(false);
previous.push(null);
}
S[start] = 0; // Starting node is at distance 0 from itself
openSet.push({ node: start, fCost: heuristic(start, end) });
// visualize {
tracerS.patch(start, S[start]);
Tracer.delay();
tracerS.depatch(start);
tracerS.select(start);
// }
while (openSet.length > 0) {
// Find the node in the open set with the lowest fCost (gCost + hCost)
minIndex = openSet.reduce((prev, curr, idx, arr) => {
return arr[prev].fCost < curr.fCost ? prev : idx;
}, 0);
const current = openSet[minIndex].node;
if (current === end) {
break; // If we reached the goal, stop
}
openSet.splice(minIndex, 1);
D[current] = true;
// visualize {
tracerS.select(current);
tracer.visit(current);
Tracer.delay();
// }
// For every unvisited neighbor of the current node
for (let i = 0; i < G.length; i++) {
if (G[current][i] && !D[i]) {
const tentative_gCost = S[current] + G[current][i];
if (tentative_gCost < S[i]) {
S[i] = tentative_gCost;
previous[i] = current; // Store the previous node
const fCost = tentative_gCost + heuristic(i, end);
openSet.push({ node: i, fCost });
// visualize {
tracerS.patch(i, S[i]);
tracer.visit(i, current, S[i]);
Tracer.delay();
tracerS.depatch(i);
tracer.leave(i, current);
Tracer.delay();
// }
}
}
}
// visualize {
tracer.leave(current);
Tracer.delay();
// }
}
// If end is reached, reconstruct the path
if (S[end] !== MAX_VALUE) {
let path = [];
for (let at = end; at !== null; at = previous[at]) {
path.push(at);
}
path.reverse();
// visualize the final path
for (let i = 0; i < path.length; i++) {
tracer.select(path[i]);
if (i > 0) {
tracer.visit(path[i], path[i - 1]);
}
Tracer.delay();
}
}
}
const s = Randomize.Integer({ min: 0, max: G.length - 1 }); // s = start node
let e; // e = end node
do {
e = Randomize.Integer({ min: 0, max: G.length - 1 });
} while (s === e);
Tracer.delay();
AStar(s, e);