-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathastar.ts
124 lines (102 loc) · 2.64 KB
/
astar.ts
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
import { konsole } from ".";
interface XY<T> {
x(): number;
y(): number;
neighbors(): T[];
isTraversable(): boolean;
}
export class AStarNode<T extends XY<T>> {
constructor(
public value: T,
public g: number = 0,
public h: number = 0,
public f: number = 0,
public parent: AStarNode<T> | null = null
) {}
sameAs(node: AStarNode<T>) {
return (
this.value.x() === node.value.x() && this.value.y() === node.value.y()
);
}
}
export const astar = <T extends XY<T>>(
start: T,
end: T,
maxSearchDepth?: number
) => {
let searchDepth = 0;
const startNode = new AStarNode(start);
const endNode = new AStarNode(end);
let openSet = [startNode];
const closedSet: AStarNode<T>[] = [];
while (openSet.length > 0) {
if (maxSearchDepth && searchDepth > maxSearchDepth) {
return [];
}
let currentIndex = 0;
let currentNode = openSet[0];
openSet.forEach((node, index) => {
if (node.f < currentNode.f) {
currentIndex = index;
currentNode = node;
}
});
openSet = openSet.filter((node) => !node.sameAs(currentNode));
closedSet.push(currentNode);
if (currentNode.sameAs(endNode)) {
const path = [];
let current: null | AStarNode<T> = currentNode;
while (current) {
path.push(current);
current = current.parent;
}
if (path.length === 2) {
// meaning the last step is the target
return [];
}
return path
.reverse()
.map((node) => node.value)
.slice(1);
}
let children: AStarNode<T>[] = [];
currentNode.value.neighbors().forEach((neighbor) => {
// When this is not commented out, the returned path is always undefined
const node = new AStarNode(neighbor);
node.parent = currentNode;
if (!neighbor.isTraversable() && !node.sameAs(endNode)) {
return;
}
children.push(node);
});
for (const child of children) {
let childInClosed = false;
for (const closed of closedSet) {
if (child.sameAs(closed)) {
childInClosed = true;
break;
}
}
if (childInClosed) {
continue;
}
let childInOpen = false;
for (const open of openSet) {
if (child.sameAs(open)) {
childInOpen = true;
break;
}
}
if (childInOpen) {
continue;
}
child.g = currentNode.g + 1;
child.h =
(child.value.x() - endNode.value.x()) ** 2 +
(child.value.y() - endNode.value.y()) ** 2;
child.f = child.g + child.h;
searchDepth++;
openSet.push(child);
}
}
};