Skip to content

Commit

Permalink
08.01.2019
Browse files Browse the repository at this point in the history
General Work
- Reformat code
- Better error handling
  • Loading branch information
Suficio committed Jan 8, 2019
1 parent 93719e9 commit f9c975e
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 103 deletions.
58 changes: 34 additions & 24 deletions Pathfinders/ASTAR.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,8 @@ function State(p)
this.p = p;
this.g = Number.POSITIVE_INFINITY;
this.f = Number.POSITIVE_INFINITY;
this.cF = null;
}

function ASTARReturnState(MainPromise)
{
const ReturnState = this;
this.on = function(Callback)
{
MainPromise.then(function() {Callback(ReturnState);});
};

MainPromise.then(function(IntermediateObject)
{
ReturnState.ENUMStatus = IntermediateObject.ENUMStatus;

let State = IntermediateObject.State;
const Path = [State.p];
while (State.cF)
{
State = State.cF;
Path.push(State.p);
}
ReturnState.path = Path;
}).catch(function() {return;});
this.cF;
}

module.exports = function(bot, sp, ep)
Expand All @@ -44,6 +22,39 @@ module.exports = function(bot, sp, ep)
// current implementation, this could have been due to an expenisve computation of the hash, however easier to compute hashes collided too frequently.
// Additionally nested arrays allow the AI to traverse the entirety of the world instead of being limited by integer limits.

function ASTARReturnState(MainPromise)
{
const ReturnState = this;
this.on = function(Callback)
{
MainPromise.then(function(IntermediateObject)
{
ReturnState.ENUMStatus = IntermediateObject.ENUMStatus;

if (IntermediateObject.ENUMStatus === bot.pathfinder.ENUMStatus.Incomplete)
{
ReturnState.ClosestPoint = IntermediateObject.State.p;
console.warn(
'WARNING Pathfinder: Did not find path in allowed MAX_EXPANSIONS,',
'returned path to closest valid end point'
);
}

// Builds path
let State = IntermediateObject.State;
const Path = [State.p];
while (State.cF)
{
State = State.cF;
Path.push(State.p);
}
ReturnState.path = Path;

Callback(ReturnState);
}).catch(function(e) {console.error('ERROR Pathfinder:', e);});
};
}

// Closed list functions
const C = [];
C.push = function(s)
Expand Down Expand Up @@ -136,7 +147,6 @@ module.exports = function(bot, sp, ep)
if (current.f - current.g < closest.f - closest.g) closest = current;
};

console.log('WARNING Pathfinder: Did not find path in allowed MAX_EXPANSIONS, returned closest path');
return resolve({ENUMStatus: bot.pathfinder.ENUMStatus.Incomplete, State: closest});
});

Expand Down
27 changes: 15 additions & 12 deletions Pathfinders/DLITE/DLITE.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,31 @@ module.exports = function(bot, sp, ep)
// The advantage of using D* Lite is it precomputes the global state between the start and end point, this allows for convenient changes of costs
// at runtime, which can be used for entity avoidance.

const ReturnState = new DLITEReturnState(bot, sp, ep, UpdateVertex, ComputeShortestPath);
const ReturnState = new DLITEReturnState(bot, sp, ep);
const R = ReturnState;

function CalculateKey(s)
{
return [Math.min(s.g, s.rhs) + bot.pathfinder.HEURISTIC(R.S.start.p, s.p) + R.km, Math.min(s.g, s.rhs)];
}

Object.defineProperty(R, 'UpdateVertex', {value: UpdateVertex, enumerable: false});
function UpdateVertex(u)
{
const exists = this.U.check(u); // Integer index
const exists = R.U.check(u); // Integer index
const equals = floatEqual(u.g, u.rhs);

if (!equals && exists !== undefined) this.U.update(exists, CalculateKey(u));
else if (!equals && exists === undefined) this.U.insert(u, CalculateKey(u));
else if (equals && exists !== undefined) this.U.remove(exists);
if (!equals && exists !== undefined) R.U.update(exists, CalculateKey(u));
else if (!equals && exists === undefined) R.U.insert(u, CalculateKey(u));
else if (equals && exists !== undefined) R.U.remove(exists);
}

Object.defineProperty(R, 'ComputeShortestPath', {value: ComputeShortestPath, enumerable: false});
function ComputeShortestPath()
{
const R = this;
const CSPPromise = new Promise(function(resolve)
{
let closest = R.S.goal;
const closest = R.S.goal;
for (let i = 0; i < bot.pathfinder.MAX_EXPANSIONS && R.U.size !== 0 &&
(CompareKeys(R.U.peek().k, CalculateKey(R.S.start)) || R.S.start.rhs > R.S.start.g); i++)
{
Expand Down Expand Up @@ -85,20 +86,21 @@ module.exports = function(bot, sp, ep)
R.UpdateVertex(s);
}
}
// console.log(u);

// Storest closest element
if (fastDistance(closest.p, R.S.start.p) > fastDistance(R.U.peek().p, R.S.start.p)) closest = R.U.peek();
// if (fastDistance(closest.p, R.S.start.p) > fastDistance(R.U.peek().p, R.S.start.p)) closest = R.U.peek();
}
if (R.S.start.rhs === Number.POSITIVE_INFINITY)
resolve({ENUMStatus: bot.pathfinder.ENUMStatus.Incomplete, State: closest});
else
resolve({ENUMStatus: bot.pathfinder.ENUMStatus.Complete, State: closest});
resolve({ENUMStatus: bot.pathfinder.ENUMStatus.Complete});
});

return CSPPromise;
}

R.Initialize();

return ReturnState;
};

Expand All @@ -113,11 +115,12 @@ function floatEqual(f1, f2)
return Math.abs(f1 - f2) < Number.EPSILON;
}

function fastDistance(p1, p2)
/* function fastDistance(p1, p2)
{
// Just avoids using square root as we know that if p1^2 > p2^2 => p1 > p2
const dx = p1.x - p2.x;
const dy = p1.y - p2.y;
const dz = p1.z - p2.z;
return dx * dx + dy * dy + dz * dz;
}
}*/
72 changes: 33 additions & 39 deletions Pathfinders/DLITE/DLITEReturnState.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
const Heap = require('fastpriorityqueue');

module.exports = function DLITEReturnState(bot, sp, ep, UpdateVertex, ComputeShortestPath)
module.exports = function DLITEReturnState(bot, sp, ep)
{
// Global state functions
const S = [];
Expand Down Expand Up @@ -58,7 +58,8 @@ module.exports = function DLITEReturnState(bot, sp, ep, UpdateVertex, ComputeSho
Object.defineProperty(this, 'U', {value: U, enumerable: false});
Object.defineProperty(this, 'S', {value: S, enumerable: false});

const State = function(p)
Object.defineProperty(this, 'State', {value: State, enumerable: false});
function State(p)
{
if (S.check(p))
return S[p.x >>> 0][p.y >>> 0][p.z >>> 0];
Expand All @@ -74,13 +75,11 @@ module.exports = function DLITEReturnState(bot, sp, ep, UpdateVertex, ComputeSho
}
};

Object.defineProperty(this, 'State', {value: State, enumerable: false});

// Path functions

const R = this;
this.path = {};

Object.defineProperty(this.path, '_peek', {value: _peek, enumerable: false});
function _peek()
{
// First part of Main() in D*Lite
Expand All @@ -90,10 +89,11 @@ module.exports = function DLITEReturnState(bot, sp, ep, UpdateVertex, ComputeSho
let minCost = Number.POSITIVE_INFINITY;

// Get successors according to stored state.
const successors = bot.pathfinder.getSuccessors(R.S.start.p);
const successors = bot.pathfinder.getSuccessors(R.S.start.p, true);
for (let n = 0, len = successors.length; n < len; n++)
{
const sp = new R.State(successors[n]);
console.log(sp);
const cost = bot.pathfinder.COST(R.S.start.p, sp.p) + sp.g;
if (minCost > cost)
{
Expand All @@ -104,8 +104,6 @@ module.exports = function DLITEReturnState(bot, sp, ep, UpdateVertex, ComputeSho

return minState;
}
Object.defineProperty(this.path, '_peek', {value: _peek, enumerable: false});

this.path.peek = function()
{
const temp = this._peek();
Expand All @@ -124,42 +122,38 @@ module.exports = function DLITEReturnState(bot, sp, ep, UpdateVertex, ComputeSho
else return undefined;
};

// Algorithm
Initialize.call(this, bot, sp, ep, UpdateVertex, ComputeShortestPath);
};

function Initialize(bot, sp, ep, UpdateVertex, ComputeShortestPath)
{
Object.defineProperty(this, 'km', {value: 0, enumerable: false});
Object.defineProperty(this, 'UpdateVertex', {value: UpdateVertex, enumerable: false});
Object.defineProperty(this, 'ComputeShortestPath', {value: ComputeShortestPath, enumerable: false});
this.Initialize = function()
{
Object.defineProperty(this, 'km', {value: 0, enumerable: false});

this.S.start = new this.State(sp);
this.S.goal = new this.State(ep);
this.S.goal.rhs = 0;
this.S.start = new this.State(sp);
this.S.goal = new this.State(ep);
this.S.goal.rhs = 0;

this.S.last = this.S.start;
this.S.last = this.S.start;

this.U.insert(this.S.goal, [bot.pathfinder.HEURISTIC(this.S.start.p, this.S.goal.p), 0]);
this.U.insert(this.S.goal, [bot.pathfinder.HEURISTIC(this.S.start.p, this.S.goal.p), 0]);

const R = this;
this.on = function(Callback)
{
// ComputeShortestPath has to be run initially
const MainPromise = this.ComputeShortestPath();
MainPromise.then(function(ReturnState)
const MainPromise = R.ComputeShortestPath();
this.on = function(Callback)
{
R.ENUMStatus = ReturnState.ENUMStatus;
R.ClosestPoint = ReturnState.State.p;
if (ReturnState.ENUMStatus === bot.pathfinder.ENUMStatus.Incomplete)
MainPromise.then(function(IntermediateObject)
{
console.warn(
'WARNING Pathfinder: Did not find path in allowed MAX_EXPANSIONS, returned closest valid start point',
'Use another algorithm to reach the valid start point before attempting D*Lite'
);
}
});
MainPromise.then(function() {Callback(R);});
R.ENUMStatus = IntermediateObject.ENUMStatus;

if (IntermediateObject.State)
R.ClosestPoint = IntermediateObject.State.p;
if (IntermediateObject.ENUMStatus === bot.pathfinder.ENUMStatus.Incomplete)
{
console.warn(
'WARNING Pathfinder: Did not find path in allowed MAX_EXPANSIONS,',
'returned path to closest valid end point'
);
}

Callback(R);
}).catch(function(e) {console.error('ERROR Pathfinder:', e);});
};
};
};

Expand All @@ -172,4 +166,4 @@ function floatEqual(f1, f2)
{
if (f1 === Number.POSITIVE_INFINITY && f2 === Number.POSITIVE_INFINITY) return true;
return Math.abs(f1 - f2) < Number.EPSILON;
}
}
26 changes: 15 additions & 11 deletions Pathfinders/DLITE/UDLITE.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ module.exports = function(bot, sp, ep)
// The advantage of using D* Lite is it precomputes the global state between the start and end point, this
// allows for convenient changes of costs at runtime, which can be used for entity avoidance.

const ReturnState = new DLITEReturnState(bot, sp, ep, UpdateVertex, ComputeShortestPath);
const ReturnState = new DLITEReturnState(bot, sp, ep);
const R = ReturnState;

function CalculateKey(s)
{
return [Math.min(s.g, s.rhs) + bot.pathfinder.HEURISTIC(R.S.start.p, s.p) + R.km, Math.min(s.g, s.rhs)];
}

Object.defineProperty(R, 'UpdateVertex', {value: UpdateVertex, enumerable: false});
function UpdateVertex(u)
{
if (u !== this.S.goal)
if (u !== R.S.goal)
{
u.rhs = Number.POSITIVE_INFINITY;

Expand All @@ -34,17 +35,17 @@ module.exports = function(bot, sp, ep)
}
}

const exists = this.U.check(u);
if (exists) this.U.remove(exists);
if (!floatEqual(u.g, u.rhs)) this.U.insert(u, CalculateKey(u));
const exists = R.U.check(u);
if (exists) R.U.remove(exists);
if (!floatEqual(u.g, u.rhs)) R.U.insert(u, CalculateKey(u));
}

Object.defineProperty(R, 'ComputeShortestPath', {value: ComputeShortestPath, enumerable: false});
function ComputeShortestPath()
{
const R = this;
const CSPPromise = new Promise(function(resolve)
{
let closest = R.S.goal;
const closest = R.S.goal;

for (let i = 0; i < bot.pathfinder.MAX_EXPANSIONS && R.U.size !== 0 &&
(CompareKeys(R.U.peek().k, CalculateKey(R.S.start)) || !floatEqual(R.S.start.rhs, R.S.start.g)); i++)
Expand Down Expand Up @@ -80,17 +81,19 @@ module.exports = function(bot, sp, ep)
}

// Storest closest element
if (fastDistance(closest.p, R.S.start.p) > fastDistance(u.p, R.S.start.p)) closest = u;
// if (fastDistance(closest.p, R.S.start.p) > fastDistance(u.p, R.S.start.p)) closest = u;
}
if (R.S.start.g === Number.POSITIVE_INFINITY)
resolve({ENUMStatus: bot.pathfinder.ENUMStatus.Incomplete, State: closest});
else
resolve({ENUMStatus: bot.pathfinder.ENUMStatus.Complete, State: closest});
resolve({ENUMStatus: bot.pathfinder.ENUMStatus.Complete});
});

return CSPPromise;
}

R.Initialize();

return ReturnState;
};

Expand All @@ -105,11 +108,12 @@ function floatEqual(f1, f2)
return Math.abs(f1 - f2) < Number.EPSILON;
}

function fastDistance(p1, p2)
/* function fastDistance(p1, p2)
{
// Just avoids using square root as we know that if p1^2 > p2^2 => p1 > p2
const dx = p1.x - p2.x;
const dy = p1.y - p2.y;
const dz = p1.z - p2.z;
return dx * dx + dy * dy + dz * dz;
}
}*/
Loading

0 comments on commit f9c975e

Please sign in to comment.