forked from CodingTrain/AStar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathastarpathfinder.js
129 lines (112 loc) · 4.54 KB
/
astarpathfinder.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
function AStarPathFinder(map, start, end, allowDiagonals) {
this.map = map;
this.lastCheckedNode = start;
this.openSet = [];
// openSet starts with beginning node only
this.openSet.push(start);
this.closedSet = [];
this.start = start;
this.end = end;
this.allowDiagonals = allowDiagonals;
//This function returns a measure of aesthetic preference for
//use when ordering the openSet. It is used to prioritise
//between equal standard heuristic scores. It can therefore
//be anything you like without affecting the ability to find
//a minimum cost path.
this.visualDist = function(a, b) {
return dist(a.i, a.j, b.i, b.j);
}
// An educated guess of how far it is between two points
this.heuristic = function(a, b) {
var d;
if (allowDiagonals) {
d = dist(a.i, a.j, b.i, b.j);
} else {
d = abs(a.i - b.i) + abs(a.j - b.j);
}
return d;
}
// Function to delete element from the array
this.removeFromArray = function(arr, elt) {
// Could use indexOf here instead to be more efficient
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i] == elt) {
arr.splice(i, 1);
}
}
}
//Run one finding step.
//returns 0 if search ongoing
//returns 1 if goal reached
//returns -1 if no solution
this.step = function() {
if (this.openSet.length > 0) {
// Best next option
var winner = 0;
for (var i = 1; i < this.openSet.length; i++) {
if (this.openSet[i].f < this.openSet[winner].f) {
winner = i;
}
//if we have a tie according to the standard heuristic
if (this.openSet[i].f == this.openSet[winner].f) {
//Prefer to explore options with longer known paths (closer to goal)
if (this.openSet[i].g > this.openSet[winner].g) {
winner = i;
}
//if we're using Manhattan distances then also break ties
//of the known distance measure by using the visual heuristic.
//This ensures that the search concentrates on routes that look
//more direct. This makes no difference to the actual path distance
//but improves the look for things like games or more closely
//approximates the real shortest path if using grid sampled data for
//planning natural paths.
if (!this.allowDiagonals) {
if (this.openSet[i].g == this.openSet[winner].g &&
this.openSet[i].vh < this.openSet[winner].vh) {
winner = i;
}
}
}
}
var current = this.openSet[winner];
this.lastCheckedNode = current;
// Did I finish?
if (current === this.end) {
console.log("DONE!");
return 1;
}
// Best option moves from openSet to closedSet
this.removeFromArray(this.openSet, current);
this.closedSet.push(current);
// Check all the neighbors
var neighbors = current.getNeighbors();
for (var i = 0; i < neighbors.length; i++) {
var neighbor = neighbors[i];
// Valid next spot?
if (!this.closedSet.includes(neighbor)) {
// Is this a better path than before?
var tempG = current.g + this.heuristic(neighbor, current);
// Is this a better path than before?
if (!this.openSet.includes(neighbor)) {
this.openSet.push(neighbor);
} else if (tempG >= neighbor.g) {
// No, it's not a better path
continue;
}
neighbor.g = tempG;
neighbor.h = this.heuristic(neighbor, end);
if (!allowDiagonals) {
neighbor.vh = this.visualDist(neighbor, end);
}
neighbor.f = neighbor.g + neighbor.h;
neighbor.previous = current;
}
}
return 0;
// Uh oh, no solution
} else {
console.log('no solution');
return -1;
}
}
}