新增breadthfirst与dijkstra寻路算法
This commit is contained in:
69
demo/libs/framework/framework.d.ts
vendored
69
demo/libs/framework/framework.d.ts
vendored
@@ -72,6 +72,69 @@ declare class PriorityQueue<T extends PriorityQueueNode> {
|
|||||||
private swap;
|
private swap;
|
||||||
private hasHigherPriority;
|
private hasHigherPriority;
|
||||||
}
|
}
|
||||||
|
declare class BreadthFirstPathfinder {
|
||||||
|
static search<T>(graph: IUnweightedGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
}
|
||||||
|
interface IUnweightedGraph<T> {
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
|
declare class UnweightedGraph<T> implements IUnweightedGraph<T> {
|
||||||
|
edges: Map<T, T[]>;
|
||||||
|
addEdgesForNode(node: T, edges: T[]): this;
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
|
declare class Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
constructor(x: number, y: number);
|
||||||
|
}
|
||||||
|
declare class UnweightedGridGraph implements IUnweightedGraph<Point> {
|
||||||
|
private static readonly CARDINAL_DIRS;
|
||||||
|
private static readonly COMPASS_DIRS;
|
||||||
|
walls: Point[];
|
||||||
|
private _width;
|
||||||
|
private _hegiht;
|
||||||
|
private _dirs;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch?: boolean);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
}
|
||||||
|
interface IWeightedGraph<T> {
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
cost(from: T, to: T): number;
|
||||||
|
}
|
||||||
|
declare class WeightedGridGraph implements IWeightedGraph<Point> {
|
||||||
|
static readonly CARDINAL_DIRS: Point[];
|
||||||
|
private static readonly COMPASS_DIRS;
|
||||||
|
walls: Point[];
|
||||||
|
weightedNodes: Point[];
|
||||||
|
defaultWeight: number;
|
||||||
|
weightedNodeWeight: number;
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
private _dirs;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch?: boolean);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
cost(from: Point, to: Point): number;
|
||||||
|
}
|
||||||
|
declare class WeightedNode<T> extends PriorityQueueNode {
|
||||||
|
data: T;
|
||||||
|
constructor(data: T);
|
||||||
|
}
|
||||||
|
declare class WeightedPathfinder {
|
||||||
|
static search<T>(graph: IWeightedGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
private static getKey;
|
||||||
|
static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[];
|
||||||
|
}
|
||||||
declare abstract class Component {
|
declare abstract class Component {
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
displayRender: egret.DisplayObject;
|
displayRender: egret.DisplayObject;
|
||||||
@@ -266,6 +329,7 @@ declare class Mesh extends Component {
|
|||||||
setVertPosition(positions: Vector2[]): this;
|
setVertPosition(positions: Vector2[]): this;
|
||||||
setTriangles(triangles: number[]): this;
|
setTriangles(triangles: number[]): this;
|
||||||
recalculateBounds(): this;
|
recalculateBounds(): this;
|
||||||
|
render(): void;
|
||||||
}
|
}
|
||||||
declare class VertexPosition {
|
declare class VertexPosition {
|
||||||
position: Vector2;
|
position: Vector2;
|
||||||
@@ -422,11 +486,6 @@ declare class Matrix2D {
|
|||||||
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
||||||
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
||||||
}
|
}
|
||||||
declare class Point {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
constructor(x: number, y: number);
|
|
||||||
}
|
|
||||||
declare class Rectangle {
|
declare class Rectangle {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ var AstarGridGraph = (function () {
|
|||||||
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||||
};
|
};
|
||||||
AstarGridGraph.prototype.isNodePassable = function (node) {
|
AstarGridGraph.prototype.isNodePassable = function (node) {
|
||||||
return !this.walls.contains(node);
|
return !this.walls.firstOrDefault(function (wall) { return JSON.stringify(wall) == JSON.stringify(node); });
|
||||||
};
|
};
|
||||||
AstarGridGraph.prototype.search = function (start, goal) {
|
AstarGridGraph.prototype.search = function (start, goal) {
|
||||||
return AStarPathfinder.search(this, start, goal);
|
return AStarPathfinder.search(this, start, goal);
|
||||||
@@ -494,6 +494,242 @@ var PriorityQueue = (function () {
|
|||||||
};
|
};
|
||||||
return PriorityQueue;
|
return PriorityQueue;
|
||||||
}());
|
}());
|
||||||
|
var BreadthFirstPathfinder = (function () {
|
||||||
|
function BreadthFirstPathfinder() {
|
||||||
|
}
|
||||||
|
BreadthFirstPathfinder.search = function (graph, start, goal) {
|
||||||
|
var _this = this;
|
||||||
|
var foundPath = false;
|
||||||
|
var frontier = [];
|
||||||
|
frontier.unshift(start);
|
||||||
|
var cameFrom = new Map();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
var _loop_3 = function () {
|
||||||
|
var current = frontier.shift();
|
||||||
|
if (JSON.stringify(current) == JSON.stringify(goal)) {
|
||||||
|
foundPath = true;
|
||||||
|
return "break";
|
||||||
|
}
|
||||||
|
graph.getNeighbors(current).forEach(function (next) {
|
||||||
|
if (!_this.hasKey(cameFrom, next)) {
|
||||||
|
frontier.unshift(next);
|
||||||
|
cameFrom.set(next, current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
while (frontier.length > 0) {
|
||||||
|
var state_2 = _loop_3();
|
||||||
|
if (state_2 === "break")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return foundPath ? AStarPathfinder.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
};
|
||||||
|
BreadthFirstPathfinder.hasKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var r;
|
||||||
|
while (r = iterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
return BreadthFirstPathfinder;
|
||||||
|
}());
|
||||||
|
var UnweightedGraph = (function () {
|
||||||
|
function UnweightedGraph() {
|
||||||
|
this.edges = new Map();
|
||||||
|
}
|
||||||
|
UnweightedGraph.prototype.addEdgesForNode = function (node, edges) {
|
||||||
|
this.edges.set(node, edges);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
UnweightedGraph.prototype.getNeighbors = function (node) {
|
||||||
|
return this.edges.get(node);
|
||||||
|
};
|
||||||
|
return UnweightedGraph;
|
||||||
|
}());
|
||||||
|
var Point = (function () {
|
||||||
|
function Point(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
return Point;
|
||||||
|
}());
|
||||||
|
var UnweightedGridGraph = (function () {
|
||||||
|
function UnweightedGridGraph(width, height, allowDiagonalSearch) {
|
||||||
|
if (allowDiagonalSearch === void 0) { allowDiagonalSearch = false; }
|
||||||
|
this.walls = [];
|
||||||
|
this._neighbors = new Array(4);
|
||||||
|
this._width = width;
|
||||||
|
this._hegiht = height;
|
||||||
|
this._dirs = allowDiagonalSearch ? UnweightedGridGraph.COMPASS_DIRS : UnweightedGridGraph.CARDINAL_DIRS;
|
||||||
|
}
|
||||||
|
UnweightedGridGraph.prototype.isNodeInBounds = function (node) {
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._hegiht;
|
||||||
|
};
|
||||||
|
UnweightedGridGraph.prototype.isNodePassable = function (node) {
|
||||||
|
return !this.walls.firstOrDefault(function (wall) { return JSON.stringify(wall) == JSON.stringify(node); });
|
||||||
|
};
|
||||||
|
UnweightedGridGraph.prototype.getNeighbors = function (node) {
|
||||||
|
var _this = this;
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
this._dirs.forEach(function (dir) {
|
||||||
|
var next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (_this.isNodeInBounds(next) && _this.isNodePassable(next))
|
||||||
|
_this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
return this._neighbors;
|
||||||
|
};
|
||||||
|
UnweightedGridGraph.prototype.search = function (start, goal) {
|
||||||
|
return BreadthFirstPathfinder.search(this, start, goal);
|
||||||
|
};
|
||||||
|
UnweightedGridGraph.CARDINAL_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, -1)
|
||||||
|
];
|
||||||
|
UnweightedGridGraph.COMPASS_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(1, -1),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(-1, 1),
|
||||||
|
new Point(0, 1),
|
||||||
|
new Point(1, 1),
|
||||||
|
];
|
||||||
|
return UnweightedGridGraph;
|
||||||
|
}());
|
||||||
|
var WeightedGridGraph = (function () {
|
||||||
|
function WeightedGridGraph(width, height, allowDiagonalSearch) {
|
||||||
|
if (allowDiagonalSearch === void 0) { allowDiagonalSearch = false; }
|
||||||
|
this.walls = [];
|
||||||
|
this.weightedNodes = [];
|
||||||
|
this.defaultWeight = 1;
|
||||||
|
this.weightedNodeWeight = 5;
|
||||||
|
this._neighbors = new Array(4);
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
this._dirs = allowDiagonalSearch ? WeightedGridGraph.COMPASS_DIRS : WeightedGridGraph.CARDINAL_DIRS;
|
||||||
|
}
|
||||||
|
WeightedGridGraph.prototype.isNodeInBounds = function (node) {
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||||
|
};
|
||||||
|
WeightedGridGraph.prototype.isNodePassable = function (node) {
|
||||||
|
return !this.walls.firstOrDefault(function (wall) { return JSON.stringify(wall) == JSON.stringify(node); });
|
||||||
|
};
|
||||||
|
WeightedGridGraph.prototype.search = function (start, goal) {
|
||||||
|
return WeightedPathfinder.search(this, start, goal);
|
||||||
|
};
|
||||||
|
WeightedGridGraph.prototype.getNeighbors = function (node) {
|
||||||
|
var _this = this;
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
this._dirs.forEach(function (dir) {
|
||||||
|
var next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (_this.isNodeInBounds(next) && _this.isNodePassable(next))
|
||||||
|
_this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
return this._neighbors;
|
||||||
|
};
|
||||||
|
WeightedGridGraph.prototype.cost = function (from, to) {
|
||||||
|
return this.weightedNodes.find(function (t) { return JSON.stringify(t) == JSON.stringify(to); }) ? this.weightedNodeWeight : this.defaultWeight;
|
||||||
|
};
|
||||||
|
WeightedGridGraph.CARDINAL_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, 1)
|
||||||
|
];
|
||||||
|
WeightedGridGraph.COMPASS_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(1, -1),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(-1, 1),
|
||||||
|
new Point(0, 1),
|
||||||
|
new Point(1, 1),
|
||||||
|
];
|
||||||
|
return WeightedGridGraph;
|
||||||
|
}());
|
||||||
|
var WeightedNode = (function (_super) {
|
||||||
|
__extends(WeightedNode, _super);
|
||||||
|
function WeightedNode(data) {
|
||||||
|
var _this = _super.call(this) || this;
|
||||||
|
_this.data = data;
|
||||||
|
return _this;
|
||||||
|
}
|
||||||
|
return WeightedNode;
|
||||||
|
}(PriorityQueueNode));
|
||||||
|
var WeightedPathfinder = (function () {
|
||||||
|
function WeightedPathfinder() {
|
||||||
|
}
|
||||||
|
WeightedPathfinder.search = function (graph, start, goal) {
|
||||||
|
var _this = this;
|
||||||
|
var foundPath = false;
|
||||||
|
var cameFrom = new Map();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
var costSoFar = new Map();
|
||||||
|
var frontier = new PriorityQueue(1000);
|
||||||
|
frontier.enqueue(new WeightedNode(start), 0);
|
||||||
|
costSoFar.set(start, 0);
|
||||||
|
var _loop_4 = function () {
|
||||||
|
var current = frontier.dequeue();
|
||||||
|
if (JSON.stringify(current.data) == JSON.stringify(goal)) {
|
||||||
|
foundPath = true;
|
||||||
|
return "break";
|
||||||
|
}
|
||||||
|
graph.getNeighbors(current.data).forEach(function (next) {
|
||||||
|
var newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||||
|
if (!_this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)) {
|
||||||
|
costSoFar.set(next, newCost);
|
||||||
|
var priprity = newCost;
|
||||||
|
frontier.enqueue(new WeightedNode(next), priprity);
|
||||||
|
cameFrom.set(next, current.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
while (frontier.count > 0) {
|
||||||
|
var state_3 = _loop_4();
|
||||||
|
if (state_3 === "break")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
};
|
||||||
|
WeightedPathfinder.hasKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var r;
|
||||||
|
while (r = iterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
WeightedPathfinder.getKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var valueIterator = map.values();
|
||||||
|
var r;
|
||||||
|
var v;
|
||||||
|
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return v.value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
WeightedPathfinder.recontructPath = function (cameFrom, start, goal) {
|
||||||
|
var path = [];
|
||||||
|
var current = goal;
|
||||||
|
path.push(goal);
|
||||||
|
while (current != start) {
|
||||||
|
current = this.getKey(cameFrom, current);
|
||||||
|
path.push(current);
|
||||||
|
}
|
||||||
|
path.reverse();
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
return WeightedPathfinder;
|
||||||
|
}());
|
||||||
var Component = (function () {
|
var Component = (function () {
|
||||||
function Component() {
|
function Component() {
|
||||||
this._enabled = true;
|
this._enabled = true;
|
||||||
@@ -1403,6 +1639,8 @@ var Mesh = (function (_super) {
|
|||||||
this._height = max.y - this._topLeftVertPosition.y;
|
this._height = max.y - this._topLeftVertPosition.y;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
Mesh.prototype.render = function () {
|
||||||
|
};
|
||||||
return Mesh;
|
return Mesh;
|
||||||
}(Component));
|
}(Component));
|
||||||
var VertexPosition = (function () {
|
var VertexPosition = (function () {
|
||||||
@@ -2129,13 +2367,6 @@ var Matrix2D = (function () {
|
|||||||
Matrix2D._identity = new Matrix2D(1, 0, 0, 1, 0, 0);
|
Matrix2D._identity = new Matrix2D(1, 0, 0, 1, 0, 0);
|
||||||
return Matrix2D;
|
return Matrix2D;
|
||||||
}());
|
}());
|
||||||
var Point = (function () {
|
|
||||||
function Point(x, y) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
return Point;
|
|
||||||
}());
|
|
||||||
var Rectangle = (function () {
|
var Rectangle = (function () {
|
||||||
function Rectangle(x, y, width, height) {
|
function Rectangle(x, y, width, height) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
|
|||||||
2
demo/libs/framework/framework.min.js
vendored
2
demo/libs/framework/framework.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -4,6 +4,34 @@ class MainScene extends Scene {
|
|||||||
|
|
||||||
this.addEntityProcessor(new SpawnerSystem(new Matcher()));
|
this.addEntityProcessor(new SpawnerSystem(new Matcher()));
|
||||||
this.astarTest();
|
this.astarTest();
|
||||||
|
this.dijkstraTest();
|
||||||
|
this.breadthfirstTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public breadthfirstTest(){
|
||||||
|
let graph = new UnweightedGraph<string>();
|
||||||
|
|
||||||
|
graph.addEdgesForNode("a", ["b"]); // a->b
|
||||||
|
graph.addEdgesForNode("b", ["a", "c", "d"]); // b->a b->c b->d
|
||||||
|
graph.addEdgesForNode("c", ["a"]); // c->a
|
||||||
|
graph.addEdgesForNode("d", ["e", "a"]); // d->e d->a
|
||||||
|
graph.addEdgesForNode("e", ["b"]); // e->b
|
||||||
|
|
||||||
|
// 计算从c到e的路径
|
||||||
|
let path = BreadthFirstPathfinder.search(graph, "c", "e");
|
||||||
|
console.log(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public dijkstraTest(){
|
||||||
|
let graph = new WeightedGridGraph(20, 20);
|
||||||
|
|
||||||
|
graph.weightedNodes.push(new Point(3, 3));
|
||||||
|
graph.weightedNodes.push(new Point(3, 4));
|
||||||
|
graph.weightedNodes.push(new Point(4, 3));
|
||||||
|
graph.weightedNodes.push(new Point(4, 4));
|
||||||
|
|
||||||
|
let path = graph.search(new Point(3, 4), new Point(15, 17));
|
||||||
|
console.log(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public astarTest(){
|
public astarTest(){
|
||||||
|
|||||||
69
source/bin/framework.d.ts
vendored
69
source/bin/framework.d.ts
vendored
@@ -72,6 +72,69 @@ declare class PriorityQueue<T extends PriorityQueueNode> {
|
|||||||
private swap;
|
private swap;
|
||||||
private hasHigherPriority;
|
private hasHigherPriority;
|
||||||
}
|
}
|
||||||
|
declare class BreadthFirstPathfinder {
|
||||||
|
static search<T>(graph: IUnweightedGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
}
|
||||||
|
interface IUnweightedGraph<T> {
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
|
declare class UnweightedGraph<T> implements IUnweightedGraph<T> {
|
||||||
|
edges: Map<T, T[]>;
|
||||||
|
addEdgesForNode(node: T, edges: T[]): this;
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
|
declare class Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
constructor(x: number, y: number);
|
||||||
|
}
|
||||||
|
declare class UnweightedGridGraph implements IUnweightedGraph<Point> {
|
||||||
|
private static readonly CARDINAL_DIRS;
|
||||||
|
private static readonly COMPASS_DIRS;
|
||||||
|
walls: Point[];
|
||||||
|
private _width;
|
||||||
|
private _hegiht;
|
||||||
|
private _dirs;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch?: boolean);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
}
|
||||||
|
interface IWeightedGraph<T> {
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
cost(from: T, to: T): number;
|
||||||
|
}
|
||||||
|
declare class WeightedGridGraph implements IWeightedGraph<Point> {
|
||||||
|
static readonly CARDINAL_DIRS: Point[];
|
||||||
|
private static readonly COMPASS_DIRS;
|
||||||
|
walls: Point[];
|
||||||
|
weightedNodes: Point[];
|
||||||
|
defaultWeight: number;
|
||||||
|
weightedNodeWeight: number;
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
private _dirs;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch?: boolean);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
cost(from: Point, to: Point): number;
|
||||||
|
}
|
||||||
|
declare class WeightedNode<T> extends PriorityQueueNode {
|
||||||
|
data: T;
|
||||||
|
constructor(data: T);
|
||||||
|
}
|
||||||
|
declare class WeightedPathfinder {
|
||||||
|
static search<T>(graph: IWeightedGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
private static getKey;
|
||||||
|
static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[];
|
||||||
|
}
|
||||||
declare abstract class Component {
|
declare abstract class Component {
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
displayRender: egret.DisplayObject;
|
displayRender: egret.DisplayObject;
|
||||||
@@ -266,6 +329,7 @@ declare class Mesh extends Component {
|
|||||||
setVertPosition(positions: Vector2[]): this;
|
setVertPosition(positions: Vector2[]): this;
|
||||||
setTriangles(triangles: number[]): this;
|
setTriangles(triangles: number[]): this;
|
||||||
recalculateBounds(): this;
|
recalculateBounds(): this;
|
||||||
|
render(): void;
|
||||||
}
|
}
|
||||||
declare class VertexPosition {
|
declare class VertexPosition {
|
||||||
position: Vector2;
|
position: Vector2;
|
||||||
@@ -422,11 +486,6 @@ declare class Matrix2D {
|
|||||||
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
||||||
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
||||||
}
|
}
|
||||||
declare class Point {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
constructor(x: number, y: number);
|
|
||||||
}
|
|
||||||
declare class Rectangle {
|
declare class Rectangle {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ var AstarGridGraph = (function () {
|
|||||||
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||||
};
|
};
|
||||||
AstarGridGraph.prototype.isNodePassable = function (node) {
|
AstarGridGraph.prototype.isNodePassable = function (node) {
|
||||||
return !this.walls.contains(node);
|
return !this.walls.firstOrDefault(function (wall) { return JSON.stringify(wall) == JSON.stringify(node); });
|
||||||
};
|
};
|
||||||
AstarGridGraph.prototype.search = function (start, goal) {
|
AstarGridGraph.prototype.search = function (start, goal) {
|
||||||
return AStarPathfinder.search(this, start, goal);
|
return AStarPathfinder.search(this, start, goal);
|
||||||
@@ -494,6 +494,242 @@ var PriorityQueue = (function () {
|
|||||||
};
|
};
|
||||||
return PriorityQueue;
|
return PriorityQueue;
|
||||||
}());
|
}());
|
||||||
|
var BreadthFirstPathfinder = (function () {
|
||||||
|
function BreadthFirstPathfinder() {
|
||||||
|
}
|
||||||
|
BreadthFirstPathfinder.search = function (graph, start, goal) {
|
||||||
|
var _this = this;
|
||||||
|
var foundPath = false;
|
||||||
|
var frontier = [];
|
||||||
|
frontier.unshift(start);
|
||||||
|
var cameFrom = new Map();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
var _loop_3 = function () {
|
||||||
|
var current = frontier.shift();
|
||||||
|
if (JSON.stringify(current) == JSON.stringify(goal)) {
|
||||||
|
foundPath = true;
|
||||||
|
return "break";
|
||||||
|
}
|
||||||
|
graph.getNeighbors(current).forEach(function (next) {
|
||||||
|
if (!_this.hasKey(cameFrom, next)) {
|
||||||
|
frontier.unshift(next);
|
||||||
|
cameFrom.set(next, current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
while (frontier.length > 0) {
|
||||||
|
var state_2 = _loop_3();
|
||||||
|
if (state_2 === "break")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return foundPath ? AStarPathfinder.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
};
|
||||||
|
BreadthFirstPathfinder.hasKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var r;
|
||||||
|
while (r = iterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
return BreadthFirstPathfinder;
|
||||||
|
}());
|
||||||
|
var UnweightedGraph = (function () {
|
||||||
|
function UnweightedGraph() {
|
||||||
|
this.edges = new Map();
|
||||||
|
}
|
||||||
|
UnweightedGraph.prototype.addEdgesForNode = function (node, edges) {
|
||||||
|
this.edges.set(node, edges);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
UnweightedGraph.prototype.getNeighbors = function (node) {
|
||||||
|
return this.edges.get(node);
|
||||||
|
};
|
||||||
|
return UnweightedGraph;
|
||||||
|
}());
|
||||||
|
var Point = (function () {
|
||||||
|
function Point(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
return Point;
|
||||||
|
}());
|
||||||
|
var UnweightedGridGraph = (function () {
|
||||||
|
function UnweightedGridGraph(width, height, allowDiagonalSearch) {
|
||||||
|
if (allowDiagonalSearch === void 0) { allowDiagonalSearch = false; }
|
||||||
|
this.walls = [];
|
||||||
|
this._neighbors = new Array(4);
|
||||||
|
this._width = width;
|
||||||
|
this._hegiht = height;
|
||||||
|
this._dirs = allowDiagonalSearch ? UnweightedGridGraph.COMPASS_DIRS : UnweightedGridGraph.CARDINAL_DIRS;
|
||||||
|
}
|
||||||
|
UnweightedGridGraph.prototype.isNodeInBounds = function (node) {
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._hegiht;
|
||||||
|
};
|
||||||
|
UnweightedGridGraph.prototype.isNodePassable = function (node) {
|
||||||
|
return !this.walls.firstOrDefault(function (wall) { return JSON.stringify(wall) == JSON.stringify(node); });
|
||||||
|
};
|
||||||
|
UnweightedGridGraph.prototype.getNeighbors = function (node) {
|
||||||
|
var _this = this;
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
this._dirs.forEach(function (dir) {
|
||||||
|
var next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (_this.isNodeInBounds(next) && _this.isNodePassable(next))
|
||||||
|
_this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
return this._neighbors;
|
||||||
|
};
|
||||||
|
UnweightedGridGraph.prototype.search = function (start, goal) {
|
||||||
|
return BreadthFirstPathfinder.search(this, start, goal);
|
||||||
|
};
|
||||||
|
UnweightedGridGraph.CARDINAL_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, -1)
|
||||||
|
];
|
||||||
|
UnweightedGridGraph.COMPASS_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(1, -1),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(-1, 1),
|
||||||
|
new Point(0, 1),
|
||||||
|
new Point(1, 1),
|
||||||
|
];
|
||||||
|
return UnweightedGridGraph;
|
||||||
|
}());
|
||||||
|
var WeightedGridGraph = (function () {
|
||||||
|
function WeightedGridGraph(width, height, allowDiagonalSearch) {
|
||||||
|
if (allowDiagonalSearch === void 0) { allowDiagonalSearch = false; }
|
||||||
|
this.walls = [];
|
||||||
|
this.weightedNodes = [];
|
||||||
|
this.defaultWeight = 1;
|
||||||
|
this.weightedNodeWeight = 5;
|
||||||
|
this._neighbors = new Array(4);
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
this._dirs = allowDiagonalSearch ? WeightedGridGraph.COMPASS_DIRS : WeightedGridGraph.CARDINAL_DIRS;
|
||||||
|
}
|
||||||
|
WeightedGridGraph.prototype.isNodeInBounds = function (node) {
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||||
|
};
|
||||||
|
WeightedGridGraph.prototype.isNodePassable = function (node) {
|
||||||
|
return !this.walls.firstOrDefault(function (wall) { return JSON.stringify(wall) == JSON.stringify(node); });
|
||||||
|
};
|
||||||
|
WeightedGridGraph.prototype.search = function (start, goal) {
|
||||||
|
return WeightedPathfinder.search(this, start, goal);
|
||||||
|
};
|
||||||
|
WeightedGridGraph.prototype.getNeighbors = function (node) {
|
||||||
|
var _this = this;
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
this._dirs.forEach(function (dir) {
|
||||||
|
var next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (_this.isNodeInBounds(next) && _this.isNodePassable(next))
|
||||||
|
_this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
return this._neighbors;
|
||||||
|
};
|
||||||
|
WeightedGridGraph.prototype.cost = function (from, to) {
|
||||||
|
return this.weightedNodes.find(function (t) { return JSON.stringify(t) == JSON.stringify(to); }) ? this.weightedNodeWeight : this.defaultWeight;
|
||||||
|
};
|
||||||
|
WeightedGridGraph.CARDINAL_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, 1)
|
||||||
|
];
|
||||||
|
WeightedGridGraph.COMPASS_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(1, -1),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(-1, 1),
|
||||||
|
new Point(0, 1),
|
||||||
|
new Point(1, 1),
|
||||||
|
];
|
||||||
|
return WeightedGridGraph;
|
||||||
|
}());
|
||||||
|
var WeightedNode = (function (_super) {
|
||||||
|
__extends(WeightedNode, _super);
|
||||||
|
function WeightedNode(data) {
|
||||||
|
var _this = _super.call(this) || this;
|
||||||
|
_this.data = data;
|
||||||
|
return _this;
|
||||||
|
}
|
||||||
|
return WeightedNode;
|
||||||
|
}(PriorityQueueNode));
|
||||||
|
var WeightedPathfinder = (function () {
|
||||||
|
function WeightedPathfinder() {
|
||||||
|
}
|
||||||
|
WeightedPathfinder.search = function (graph, start, goal) {
|
||||||
|
var _this = this;
|
||||||
|
var foundPath = false;
|
||||||
|
var cameFrom = new Map();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
var costSoFar = new Map();
|
||||||
|
var frontier = new PriorityQueue(1000);
|
||||||
|
frontier.enqueue(new WeightedNode(start), 0);
|
||||||
|
costSoFar.set(start, 0);
|
||||||
|
var _loop_4 = function () {
|
||||||
|
var current = frontier.dequeue();
|
||||||
|
if (JSON.stringify(current.data) == JSON.stringify(goal)) {
|
||||||
|
foundPath = true;
|
||||||
|
return "break";
|
||||||
|
}
|
||||||
|
graph.getNeighbors(current.data).forEach(function (next) {
|
||||||
|
var newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||||
|
if (!_this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)) {
|
||||||
|
costSoFar.set(next, newCost);
|
||||||
|
var priprity = newCost;
|
||||||
|
frontier.enqueue(new WeightedNode(next), priprity);
|
||||||
|
cameFrom.set(next, current.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
while (frontier.count > 0) {
|
||||||
|
var state_3 = _loop_4();
|
||||||
|
if (state_3 === "break")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
};
|
||||||
|
WeightedPathfinder.hasKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var r;
|
||||||
|
while (r = iterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
WeightedPathfinder.getKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var valueIterator = map.values();
|
||||||
|
var r;
|
||||||
|
var v;
|
||||||
|
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return v.value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
WeightedPathfinder.recontructPath = function (cameFrom, start, goal) {
|
||||||
|
var path = [];
|
||||||
|
var current = goal;
|
||||||
|
path.push(goal);
|
||||||
|
while (current != start) {
|
||||||
|
current = this.getKey(cameFrom, current);
|
||||||
|
path.push(current);
|
||||||
|
}
|
||||||
|
path.reverse();
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
return WeightedPathfinder;
|
||||||
|
}());
|
||||||
var Component = (function () {
|
var Component = (function () {
|
||||||
function Component() {
|
function Component() {
|
||||||
this._enabled = true;
|
this._enabled = true;
|
||||||
@@ -1403,6 +1639,8 @@ var Mesh = (function (_super) {
|
|||||||
this._height = max.y - this._topLeftVertPosition.y;
|
this._height = max.y - this._topLeftVertPosition.y;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
Mesh.prototype.render = function () {
|
||||||
|
};
|
||||||
return Mesh;
|
return Mesh;
|
||||||
}(Component));
|
}(Component));
|
||||||
var VertexPosition = (function () {
|
var VertexPosition = (function () {
|
||||||
@@ -2129,13 +2367,6 @@ var Matrix2D = (function () {
|
|||||||
Matrix2D._identity = new Matrix2D(1, 0, 0, 1, 0, 0);
|
Matrix2D._identity = new Matrix2D(1, 0, 0, 1, 0, 0);
|
||||||
return Matrix2D;
|
return Matrix2D;
|
||||||
}());
|
}());
|
||||||
var Point = (function () {
|
|
||||||
function Point(x, y) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
return Point;
|
|
||||||
}());
|
|
||||||
var Rectangle = (function () {
|
var Rectangle = (function () {
|
||||||
function Rectangle(x, y, width, height) {
|
function Rectangle(x, y, width, height) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
|
|||||||
2
source/bin/framework.min.js
vendored
2
source/bin/framework.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -37,7 +37,7 @@ class AstarGridGraph implements IAstarGraph<Point> {
|
|||||||
* @param node
|
* @param node
|
||||||
*/
|
*/
|
||||||
public isNodePassable(node: Point): boolean {
|
public isNodePassable(node: Point): boolean {
|
||||||
return !this.walls.contains(node);
|
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
public search(start: Point, goal: Point){
|
public search(start: Point, goal: Point){
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 计算路径给定的IUnweightedGraph和开始/目标位置
|
||||||
|
*/
|
||||||
|
class BreadthFirstPathfinder {
|
||||||
|
public static search<T>(graph: IUnweightedGraph<T>, start: T, goal: T): T[]{
|
||||||
|
let foundPath = false;
|
||||||
|
let frontier = [];
|
||||||
|
frontier.unshift(start);
|
||||||
|
|
||||||
|
let cameFrom = new Map<T, T>();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
|
||||||
|
while (frontier.length > 0){
|
||||||
|
let current = frontier.shift();
|
||||||
|
if (JSON.stringify(current) == JSON.stringify(goal)){
|
||||||
|
foundPath = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.getNeighbors(current).forEach(next => {
|
||||||
|
if (!this.hasKey(cameFrom, next)){
|
||||||
|
frontier.unshift(next);
|
||||||
|
cameFrom.set(next, current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundPath ? AStarPathfinder.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static hasKey<T>(map: Map<T, T>, compareKey: T){
|
||||||
|
let iterator = map.keys();
|
||||||
|
let r: IteratorResult<T>;
|
||||||
|
while (r = iterator.next() , !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
interface IUnweightedGraph<T>{
|
||||||
|
/**
|
||||||
|
* getNeighbors方法应该返回从传入的节点可以到达的任何相邻节点。
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
}
|
||||||
16
source/src/AI/Pathfinding/BreadthFirst/UnweightedGraph.ts
Normal file
16
source/src/AI/Pathfinding/BreadthFirst/UnweightedGraph.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* 一个未加权图的基本实现。所有的边都被缓存。这种类型的图最适合于非基于网格的图。
|
||||||
|
* 作为边添加的任何节点都必须在边字典中有一个条目作为键。
|
||||||
|
*/
|
||||||
|
class UnweightedGraph<T> implements IUnweightedGraph<T> {
|
||||||
|
public edges: Map<T, T[]> = new Map<T, T[]>();
|
||||||
|
|
||||||
|
public addEdgesForNode(node: T, edges: T[]){
|
||||||
|
this.edges.set(node, edges);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNeighbors(node: T){
|
||||||
|
return this.edges.get(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
///<reference path="../../../Math/Point.ts" />
|
||||||
|
/**
|
||||||
|
* 基本的未加权网格图形用于BreadthFirstPathfinder
|
||||||
|
*/
|
||||||
|
class UnweightedGridGraph implements IUnweightedGraph<Point> {
|
||||||
|
private static readonly CARDINAL_DIRS: Point[] = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, -1)
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly COMPASS_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(1, -1),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(-1, 1),
|
||||||
|
new Point(0, 1),
|
||||||
|
new Point(1, 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
public walls: Point[] = [];
|
||||||
|
|
||||||
|
private _width: number;
|
||||||
|
private _hegiht: number;
|
||||||
|
|
||||||
|
private _dirs: Point[];
|
||||||
|
private _neighbors: Point[] = new Array(4);
|
||||||
|
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch: boolean = false) {
|
||||||
|
this._width = width;
|
||||||
|
this._hegiht = height;
|
||||||
|
this._dirs = allowDiagonalSearch ? UnweightedGridGraph.COMPASS_DIRS : UnweightedGridGraph.CARDINAL_DIRS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNodeInBounds(node: Point): boolean {
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._hegiht;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNodePassable(node: Point): boolean {
|
||||||
|
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNeighbors(node: Point) {
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
|
||||||
|
this._dirs.forEach(dir => {
|
||||||
|
let next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||||
|
this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public search(start: Point, goal: Point): Point[] {
|
||||||
|
return BreadthFirstPathfinder.search(this, start, goal);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
source/src/AI/Pathfinding/Dijkstra/IWeightedGraph.ts
Normal file
14
source/src/AI/Pathfinding/Dijkstra/IWeightedGraph.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
interface IWeightedGraph<T>{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
getNeighbors(node: T): T[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param from
|
||||||
|
* @param to
|
||||||
|
*/
|
||||||
|
cost(from: T, to: T): number;
|
||||||
|
}
|
||||||
67
source/src/AI/Pathfinding/Dijkstra/WeightedGridGraph.ts
Normal file
67
source/src/AI/Pathfinding/Dijkstra/WeightedGridGraph.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
///<reference path="../../../Math/Point.ts" />
|
||||||
|
/**
|
||||||
|
* 支持一种加权节点的基本网格图
|
||||||
|
*/
|
||||||
|
class WeightedGridGraph implements IWeightedGraph<Point> {
|
||||||
|
public static readonly CARDINAL_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, 1)
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly COMPASS_DIRS = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(1, -1),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(-1, 1),
|
||||||
|
new Point(0, 1),
|
||||||
|
new Point(1, 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
public walls: Point[] = [];
|
||||||
|
public weightedNodes: Point[] = [];
|
||||||
|
public defaultWeight = 1;
|
||||||
|
public weightedNodeWeight = 5;
|
||||||
|
|
||||||
|
private _width: number;
|
||||||
|
private _height: number;
|
||||||
|
private _dirs: Point[];
|
||||||
|
private _neighbors: Point[] = new Array(4);
|
||||||
|
|
||||||
|
constructor(width: number, height: number, allowDiagonalSearch: boolean = false){
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
this._dirs = allowDiagonalSearch ? WeightedGridGraph.COMPASS_DIRS : WeightedGridGraph.CARDINAL_DIRS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNodeInBounds(node: Point){
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNodePassable(node: Point): boolean {
|
||||||
|
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
public search(start: Point, goal: Point){
|
||||||
|
return WeightedPathfinder.search(this, start, goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNeighbors(node: Point): Point[]{
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
|
||||||
|
this._dirs.forEach(dir => {
|
||||||
|
let next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||||
|
this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public cost(from: Point, to: Point): number{
|
||||||
|
return this.weightedNodes.find(t => JSON.stringify(t) == JSON.stringify(to)) ? this.weightedNodeWeight : this.defaultWeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
83
source/src/AI/Pathfinding/Dijkstra/WeightedPathfinder.ts
Normal file
83
source/src/AI/Pathfinding/Dijkstra/WeightedPathfinder.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
class WeightedNode<T> extends PriorityQueueNode {
|
||||||
|
public data: T;
|
||||||
|
|
||||||
|
constructor(data: T){
|
||||||
|
super();
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WeightedPathfinder {
|
||||||
|
public static search<T>(graph: IWeightedGraph<T>, start: T, goal: T){
|
||||||
|
let foundPath = false;
|
||||||
|
|
||||||
|
let cameFrom = new Map<T, T>();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
|
||||||
|
let costSoFar = new Map<T, number>();
|
||||||
|
let frontier = new PriorityQueue<WeightedNode<T>>(1000);
|
||||||
|
frontier.enqueue(new WeightedNode<T>(start), 0);
|
||||||
|
|
||||||
|
costSoFar.set(start, 0);
|
||||||
|
|
||||||
|
while (frontier.count > 0){
|
||||||
|
let current = frontier.dequeue();
|
||||||
|
|
||||||
|
if (JSON.stringify(current.data) == JSON.stringify(goal)){
|
||||||
|
foundPath = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.getNeighbors(current.data).forEach(next => {
|
||||||
|
let newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||||
|
if (!this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)){
|
||||||
|
costSoFar.set(next, newCost);
|
||||||
|
let priprity = newCost;
|
||||||
|
frontier.enqueue(new WeightedNode<T>(next), priprity);
|
||||||
|
cameFrom.set(next, current.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static hasKey<T>(map: Map<T, number>, compareKey: T){
|
||||||
|
let iterator = map.keys();
|
||||||
|
let r: IteratorResult<T>;
|
||||||
|
while (r = iterator.next() , !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getKey<T>(map: Map<T, T>, compareKey: T){
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,9 @@ class Mesh extends Component {
|
|||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public render(){
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VertexPosition{
|
class VertexPosition{
|
||||||
|
|||||||
Reference in New Issue
Block a user