2020-06-09 19:45:09 +08:00
|
|
|
|
///<reference path="./PriorityQueueNode.ts" />
|
2020-07-23 11:00:46 +08:00
|
|
|
|
module es {
|
2020-07-09 16:36:42 +08:00
|
|
|
|
/**
|
2020-07-23 11:00:46 +08:00
|
|
|
|
* 计算路径给定的IAstarGraph和开始/目标位置
|
2020-07-09 16:36:42 +08:00
|
|
|
|
*/
|
2020-07-23 11:00:46 +08:00
|
|
|
|
export class AStarPathfinder {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 尽可能从开始到目标找到一条路径。如果没有找到路径,则返回null。
|
|
|
|
|
|
* @param graph
|
|
|
|
|
|
* @param start
|
|
|
|
|
|
* @param goal
|
|
|
|
|
|
*/
|
2020-07-28 16:25:20 +08:00
|
|
|
|
public static search<T>(graph: IAstarGraph<T>, start: T, goal: T) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
let foundPath = false;
|
|
|
|
|
|
let cameFrom = new Map<T, T>();
|
|
|
|
|
|
cameFrom.set(start, start);
|
|
|
|
|
|
|
|
|
|
|
|
let costSoFar = new Map<T, number>();
|
|
|
|
|
|
let frontier = new PriorityQueue<AStarNode<T>>(1000);
|
|
|
|
|
|
frontier.enqueue(new AStarNode<T>(start), 0);
|
|
|
|
|
|
|
|
|
|
|
|
costSoFar.set(start, 0);
|
|
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
while (frontier.count > 0) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
let current = frontier.dequeue();
|
|
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (JSON.stringify(current.data) == JSON.stringify(goal)) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
foundPath = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2020-06-09 19:45:09 +08:00
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
graph.getNeighbors(current.data).forEach(next => {
|
|
|
|
|
|
let newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (!this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
costSoFar.set(next, newCost);
|
|
|
|
|
|
let priority = newCost + graph.heuristic(next, goal);
|
|
|
|
|
|
frontier.enqueue(new AStarNode<T>(next), priority);
|
|
|
|
|
|
cameFrom.set(next, current.data);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2020-06-09 19:45:09 +08:00
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
|
|
|
|
|
}
|
2020-06-09 19:45:09 +08:00
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 从cameFrom字典重新构造路径
|
|
|
|
|
|
* @param cameFrom
|
|
|
|
|
|
* @param start
|
|
|
|
|
|
* @param goal
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[] {
|
|
|
|
|
|
let path = [];
|
|
|
|
|
|
let current = goal;
|
|
|
|
|
|
path.push(goal);
|
|
|
|
|
|
|
|
|
|
|
|
while (current != start) {
|
|
|
|
|
|
current = this.getKey(cameFrom, current);
|
|
|
|
|
|
path.push(current);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
path.reverse();
|
|
|
|
|
|
|
|
|
|
|
|
return path;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static hasKey<T>(map: Map<T, number>, compareKey: T) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
let iterator = map.keys();
|
|
|
|
|
|
let r: IteratorResult<T>;
|
|
|
|
|
|
while (r = iterator.next() , !r.done) {
|
|
|
|
|
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
|
|
|
|
|
return true;
|
2020-06-09 19:45:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
return false;
|
2020-06-09 19:45:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
private static getKey<T>(map: Map<T, T>, compareKey: T) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
let iterator = map.keys();
|
|
|
|
|
|
let valueIterator = map.values();
|
|
|
|
|
|
let r: IteratorResult<T>;
|
|
|
|
|
|
let v: IteratorResult<T>;
|
|
|
|
|
|
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
|
|
|
|
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
|
|
|
|
|
return v.value;
|
|
|
|
|
|
}
|
2020-06-09 19:45:09 +08:00
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
return null;
|
2020-06-09 19:45:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-09 16:36:42 +08:00
|
|
|
|
/**
|
2020-07-23 11:00:46 +08:00
|
|
|
|
* 使用PriorityQueue需要的额外字段将原始数据封装在一个小类中
|
2020-07-09 16:36:42 +08:00
|
|
|
|
*/
|
2020-07-23 11:00:46 +08:00
|
|
|
|
export class AStarNode<T> extends PriorityQueueNode {
|
|
|
|
|
|
public data: T;
|
2020-06-09 19:45:09 +08:00
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
constructor(data: T) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
super();
|
|
|
|
|
|
this.data = data;
|
|
|
|
|
|
}
|
2020-06-09 19:45:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|