Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallel Paths #76

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 9 additions & 6 deletions README.md
Expand Up @@ -11,8 +11,8 @@
* **Phaser:** see [Phaser Plugin](https://github.com/appsbu-de/phaser_plugin_pathfinding)
* **Bower:** `bower install easystarjs`

## Description
easystar.js is an asynchronous A* pathfinding API written in Javascript for use in your HTML5 games and interactive projects. The goal of this project is to make it easy and fast to implement performance conscious pathfinding.
## Description
easystar.js is an asynchronous A\* pathfinding API written in Javascript for use in your HTML5 games and interactive projects. The goal of this project is to make it easy and fast to implement performance conscious pathfinding.

## Features

Expand Down Expand Up @@ -73,6 +73,9 @@ var instanceId = easystar.findPath(startX, startY, endX, endY, callback);
// ...
easystar.cancelPath(instanceId);
```
```javascript
easystar.enableParallelCompute();
```

## Usage

Expand All @@ -96,7 +99,7 @@ var grid = [[0,0,1,0,0],
```

Set our grid.
```javascript
```javascript
easystar.setGrid(grid);
```
Set tiles which are "walkable".
Expand All @@ -115,14 +118,14 @@ easystar.findPath(0, 0, 4, 0, function( path ) {
});
```

EasyStar will not yet start calculating my path.
EasyStar will not yet start calculating my path.

In order for EasyStar to actually start calculating, I must call the calculate() method.

You should call `easystar.calculate()` on a ticker, or setInterval.

If you have a large grid, then it is possible that these calculations could slow down the browser.
For this reason, it might be a good idea to give EasyStar a smaller `iterationsPerCalculation` value via
If you have a large grid, then it is possible that these calculations could slow down the browser.
For this reason, it might be a good idea to give EasyStar a smaller `iterationsPerCalculation` value via

```javascript
easystar.setIterationsPerCalculation(1000);
Expand Down
14 changes: 14 additions & 0 deletions index.d.ts
Expand Up @@ -41,6 +41,20 @@ export class js {
*/
disableDiagonals(): void

/**
* Enables parallel path computing.
*
* If multiple calls to .findPath() are made, this
* will distribute the load by running iterationsPerCalculation before
* moving onto the next path in the queue.
**/
enableParallelCompute(): void

/**
* Disables parallel path computing.
*/
disableParallelCompute(): void

/**
* Sets the collision grid that EasyStar uses.
*
Expand Down
47 changes: 43 additions & 4 deletions src/easystar.js
Expand Up @@ -31,9 +31,11 @@ EasyStar.js = function() {
var iterationsSoFar;
var instances = {};
var instanceQueue = [];
var instanceQueueIndex = 0;
var iterationsPerCalculation = Number.MAX_VALUE;
var acceptableTiles;
var diagonalsEnabled = false;
var parallelEnabled = false;

/**
* Sets the collision grid that EasyStar uses.
Expand Down Expand Up @@ -67,6 +69,24 @@ EasyStar.js = function() {
syncEnabled = false;
};

/**
* Enables parallel path computing.
*
* If multiple calls to .findPath() are made, this
* will distribute the load by running iterationsPerCalculation before
* moving onto the next path in the queue.
**/
this.enableParallelCompute = function() {
parallelEnabled = true;
};

/**
* Disables parallel path computing.
**/
this.disableParallelCompute = function() {
parallelEnabled = false;
};

/**
* Enable diagonal pathfinding.
*/
Expand Down Expand Up @@ -305,6 +325,7 @@ EasyStar.js = function() {
var instanceId = nextInstanceId ++;
instances[instanceId] = instance;
instanceQueue.push(instanceId);

return instanceId;
};

Expand Down Expand Up @@ -334,6 +355,7 @@ EasyStar.js = function() {
if (instanceQueue.length === 0 || collisionGrid === undefined || acceptableTiles === undefined) {
return;
}

for (iterationsSoFar = 0; iterationsSoFar < iterationsPerCalculation; iterationsSoFar++) {
if (instanceQueue.length === 0) {
return;
Expand All @@ -344,19 +366,21 @@ EasyStar.js = function() {
iterationsSoFar = 0;
}

var instanceId = instanceQueue[0];
var instanceId = instanceQueue[instanceQueueIndex];
var instance = instances[instanceId];
if (typeof instance == 'undefined') {
// This instance was cancelled
instanceQueue.shift();
instanceQueue.splice(instanceQueueIndex, 1);
updateQueueIndex();
continue;
}

// Couldn't find a path.
if (instance.openList.size() === 0) {
instance.callback(null);
delete instances[instanceId];
instanceQueue.shift();
instanceQueue.splice(instanceQueueIndex, 1);
updateQueueIndex();
continue;
}

Expand All @@ -375,7 +399,8 @@ EasyStar.js = function() {
var ip = path;
instance.callback(ip);
delete instances[instanceId];
instanceQueue.shift();
instanceQueue.splice(instanceQueueIndex, 1);
updateQueueIndex();
continue;
}

Expand Down Expand Up @@ -441,6 +466,8 @@ EasyStar.js = function() {
}

}

updateQueueIndex();
};

// Private methods follow
Expand Down Expand Up @@ -544,6 +571,18 @@ EasyStar.js = function() {
return (dx + dy);
}
};

var updateQueueIndex = function () {
if (!parallelEnabled || !instanceQueue.length) {
return;
}

instanceQueueIndex--;

if (instanceQueueIndex < 0) {
instanceQueueIndex = instanceQueue.length - 1;
}
};
}

EasyStar.TOP = 'TOP'
Expand Down
80 changes: 80 additions & 0 deletions test/easystartest.js
Expand Up @@ -346,4 +346,84 @@ describe("EasyStar.js", function() {

easyStar.calculate();
})

it("It should handle computing paths in serial", function (done) {
var easyStar = new EasyStar.js();
var map = [[1,1,0,1,1],
[1,1,0,1,1],
[1,1,0,1,1],
[1,1,1,1,1],
[1,1,1,1,1]];

easyStar.setGrid(map);
easyStar.setAcceptableTiles([1]);
easyStar.setIterationsPerCalculation(2);
easyStar.disableParallelCompute();

var iteration = 0;
// iteration each path was found on
var firstIteration = -1;
var secondIteration = -1;
var maxIterations = 8;

easyStar.findPath(1, 2, 3, 2, () => {
firstIteration = iteration;
});

easyStar.findPath(1, 2, 3, 2, () => {
secondIteration = iteration;
expect(firstIteration).toBeLessThan(secondIteration);
done();
});

function iterate() {
if (iteration < maxIterations) {
easyStar.calculate();
iteration += 1;
setTimeout(iterate, 1);
}
}

iterate();
})

it("It should handle computing paths in parallel", function (done) {
var easyStar = new EasyStar.js();
var map = [[1,1,0,1,1],
[1,1,0,1,1],
[1,1,0,1,1],
[1,1,1,1,1],
[1,1,1,1,1]];

easyStar.setGrid(map);
easyStar.setAcceptableTiles([1]);
easyStar.setIterationsPerCalculation(2);
easyStar.enableParallelCompute();

var iteration = 0;
// iteration each path was found on
var firstIteration = -1;
var secondIteration = -1;
var maxIterations = 8;

easyStar.findPath(1, 2, 3, 2, () => {
firstIteration = iteration;
});

easyStar.findPath(1, 2, 3, 2, () => {
secondIteration = iteration;
expect(firstIteration).toEqual(secondIteration);
done();
});

function iterate() {
if (iteration < maxIterations) {
easyStar.calculate();
iteration += 1;
setTimeout(iterate, 1);
}
}

iterate();
})
});