chore: pathfinding replacement

This commit is contained in:
Marcin Czerniak 2021-04-19 13:30:57 +02:00
parent 37c068e91b
commit 5653fa6b02
6 changed files with 131 additions and 51 deletions

View File

@ -20,6 +20,7 @@
<script src="modules/floor.js"></script> <script src="modules/floor.js"></script>
<script src="modules/grid.js"></script> <script src="modules/grid.js"></script>
<script src="modules/pathfinding.js"></script>
<script src="modules/agentController.js"></script> <script src="modules/agentController.js"></script>
<script src="modules/agent.js"></script> <script src="modules/agent.js"></script>
<script src="modules/products.js"></script> <script src="modules/products.js"></script>

View File

@ -8,4 +8,6 @@ window.addEventListener('DOMContentLoaded', () => document.fonts.ready.then(() =
const grid = new Grid(gridCanvas); const grid = new Grid(gridCanvas);
const agent = new Agent(agentCanvas); const agent = new Agent(agentCanvas);
const products = new Products(productsCanvas); const products = new Products(productsCanvas);
window.products = products;
})); }));

View File

@ -4,15 +4,22 @@ class Agent extends AgentController {
const cycle = async () => { const cycle = async () => {
await this.takeFromStore(Product.RANDOM_FROM_REGISTRY(), Math.floor(Math.random() * 40) + 10); // await this.takeFromStore(Product.RANDOM_FROM_REGISTRY(), Math.floor(Math.random() * 40) + 10);
const randomPlace = Grid.instance.randomPlace(); // const randomPlace = Grid.instance.randomPlace();
await this.deliver(randomPlace.x, randomPlace.y); // await this.deliver(randomPlace.x, randomPlace.y);
const jobRequest = window.products.getJobRequest();
if (jobRequest) {
console.log(jobRequest);
const requiredAmount = 50 - jobRequest.amount;
await this.takeFromStore(jobRequest.product, requiredAmount);
await this.deliver(jobRequest.x, jobRequest.y);
}
await waitFor(2000); await waitFor(2000);
cycle(); cycle();
}; };
cycle(); setTimeout(() => cycle(), 2000);
} }
@ -32,6 +39,7 @@ class Agent extends AgentController {
*/ */
async deliver(x, y) { async deliver(x, y) {
await this.moveTo(x, y); await this.moveTo(x, y);
window.products.exchange(x, y, this.ownedProduct, 50);
this.ownedProductAmount = 0; this.ownedProductAmount = 0;
this.ownedProduct = Product.REGISTRY.empty; this.ownedProduct = Product.REGISTRY.empty;
} }
@ -41,7 +49,8 @@ class Agent extends AgentController {
* @param {*} product * @param {*} product
*/ */
async takeFromStore(product, amount) { async takeFromStore(product, amount) {
await this.moveTo(6, 8); await this.moveTo(9, 8);
await waitFor(1000);
this.ownedProductAmount = amount; this.ownedProductAmount = amount;
this.ownedProduct = product; this.ownedProduct = product;
} }

View File

@ -48,10 +48,11 @@ class AgentController {
let nearestPoint = this.currentPath[this.currentPathIndex]; let nearestPoint = this.currentPath[this.currentPathIndex];
if (nearestPoint) { if (nearestPoint) {
const requiredRotation = this.findRequiredRotation(nearestPoint); if (nearestPoint.action == PATHFINDING_ACTION.MOVE) {
await this.rotate({ z: requiredRotation }); await this.moveInLine(nearestPoint.state.position);
if (!nearestPoint.options || !nearestPoint.options.isShelf) { }
await this.moveInLine(nearestPoint); if (nearestPoint.action == PATHFINDING_ACTION.ROTATE) {
await this.rotate({ z: nearestPoint.state.rotation });
} }
nearestPoint = this.currentPath[++this.currentPathIndex]; nearestPoint = this.currentPath[++this.currentPathIndex];
@ -134,9 +135,6 @@ class AgentController {
*/ */
rotate(rotation) { rotate(rotation) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// console.log(this.rotation); // 270
// console.log(rotation); // 0
if (this.rotation.z == rotation.z) { if (this.rotation.z == rotation.z) {
resolve(); resolve();
} }
@ -172,7 +170,6 @@ class AgentController {
moveTo(x, y) { moveTo(x, y) {
this.currentPath = this.findPathTo(x, y, this.position.x, this.position.y); this.currentPath = this.findPathTo(x, y, this.position.x, this.position.y);
console.log(this.currentPath);
return this.updateAgentPosition(); return this.updateAgentPosition();
} }
@ -181,8 +178,8 @@ class AgentController {
this.ctx.beginPath(); this.ctx.beginPath();
this.ctx.strokeWidth = 5; this.ctx.strokeWidth = 5;
this.ctx.strokeStyle = 'red'; this.ctx.strokeStyle = 'red';
this.ctx.moveTo(path[i].x * 100 + 50, path[i].y * 100 + 50); this.ctx.moveTo(path[i].state.position.x * 100 + 50, path[i].state.position.y * 100 + 50);
this.ctx.lineTo(path[i + 1].x * 100 + 50, path[i + 1].y * 100 + 50); this.ctx.lineTo(path[i + 1].state.position.x * 100 + 50, path[i + 1].state.position.y * 100 + 50);
this.ctx.stroke(); this.ctx.stroke();
} }
} }
@ -230,44 +227,13 @@ class AgentController {
} }
findPathTo(dx, dy, sx, sy) { findPathTo(dx, dy, sx, sy) {
const grid = JSON.parse(JSON.stringify(Grid.instance.grid)); if (Grid.instance.getGrid()[dx][dy] == GRID_FIELD_TYPE.SHELF || Grid.instance.getGrid()[dx][dy] == GRID_FIELD_TYPE.STORAGE) {
const queue = []; if (Grid.instance.getGrid()[dx][dy - 1] == GRID_FIELD_TYPE.PATH) {
const lastPoint = []; return Pathfinding.search({ position: { x: sx, y: sy }, rotation: this.rotation.z }, { position: { x: dx, y: dy - 1 }, rotation: 180 });
if (grid[dx][dy] === GRID_FIELD_TYPE.SHELF) {
if(grid[dx][dy + 1] === GRID_FIELD_TYPE.PATH) {
lastPoint.push({ x: dx, y: dy, options: { isShelf: true } });
dy++;
} else if(grid[dx][dy - 1] === GRID_FIELD_TYPE.PATH) {
lastPoint.push({ x: dx, y: dy, options: { isShelf: true } });
dy--;
} }
return Pathfinding.search({ position: { x: sx, y: sy }, rotation: this.rotation.z }, { position: { x: dx, y: dy + 1 }, rotation: 0 });
} }
return Pathfinding.search({ position: { x: sx, y: sy }, rotation: this.rotation.z }, { position: { x: dx, y: dy }, rotation: this.rotation.z });
queue.push({ x: sx, y: sy, path: [] });
while(queue.length) {
const { x, y, path } = queue.shift();
path.push({ x, y });
if (x === dx && y === dy) {
return [...path, ...lastPoint];
}
if (grid[x][y - 1] === GRID_FIELD_TYPE.PATH) {
queue.push({ x, y: y - 1, path: [...path] });
}
if (grid[x][y + 1] === GRID_FIELD_TYPE.PATH) {
queue.push({ x, y: y + 1, path: [...path] });
}
if (grid[x - 1] && grid[x - 1][y] === GRID_FIELD_TYPE.PATH) {
queue.push({ x: x - 1, y, path: [...path] });
}
if (grid[x + 1] && grid[x + 1][y] === GRID_FIELD_TYPE.PATH) {
queue.push({ x: x + 1, y, path: [...path] });
}
grid[x][y] = -1;
}
} }
clearCanvas() { clearCanvas() {

100
src/modules/pathfinding.js Normal file
View File

@ -0,0 +1,100 @@
/**
* @typedef IPosition
* @property {number} x
* @property {number} y
*
* @typedef {Object} IState
* @property {number} rotation
* @property {IPosition} position
*/
const PATHFINDING_ACTION = {
ROTATE: 0,
MOVE: 1,
}
class Pathfinding {
static search(state, expectedState) {
return Pathfinding.graphSearch({ state }, { state: expectedState })
}
static graphSearch(node, expectedNode, fringe = [], explored = []) {
fringe.push(node);
while(true) {
if (fringe.length == 0) {
return false;
}
const elem = fringe.shift();
if (Pathfinding.reached(elem.state, expectedNode.state)) {
return Pathfinding.buildPath(node, elem);
}
explored.push(elem);
for (const successor of this.successor(elem)) {
if (explored.every(n => !Pathfinding.reached(n.state, successor.state))) {
fringe.push(successor);
}
}
}
}
static successor(node) {
const list = [];
const grid = Grid.instance.getGrid();
list.push({
state: { ...node.state, rotation: (node.state.rotation + 270) % 360 },
action: PATHFINDING_ACTION.ROTATE,
parent: node
});
list.push({
state: { ...node.state, rotation: (node.state.rotation + 90) % 360 },
action: PATHFINDING_ACTION.ROTATE,
parent: node
});
const direction = Pathfinding.getVectorFromRotation(node.state.rotation);
const prediction = { x: node.state.position.x + direction.x, y: node.state.position.y + direction.y }
if (grid[prediction.x] && grid[prediction.x][prediction.y] == GRID_FIELD_TYPE.PATH) {
list.push({
state: { ...node.state, position: prediction },
action: PATHFINDING_ACTION.MOVE,
parent: node
});
}
return list;
}
static buildPath(node, expectedNode) {
const result = [];
let currentElement = expectedNode;
while(!Pathfinding.reached(currentElement.state, node.state)) {
result.unshift(currentElement);
currentElement = currentElement.parent;
}
return result;
}
static reached(state, expectedState) {
return state.rotation == expectedState.rotation && state.position.x == expectedState.position.x && state.position.y == expectedState.position.y;
}
static getVectorFromRotation(rotation) {
switch (rotation) {
case 0: return { x: 0, y: -1 };
case 90: return { x: 1, y: 0 };
case 180: return { x: 0, y: 1 };
case 270: return { x: -1, y: 0 };
}
}
}

View File

@ -130,6 +130,8 @@ class Products {
const amount = this.gridProductsAmount[x][y] const amount = this.gridProductsAmount[x][y]
this.gridProducts[x][y] = incomingProduct this.gridProducts[x][y] = incomingProduct
this.gridProductsAmount[x][y] = incomingAmount this.gridProductsAmount[x][y] = incomingAmount
this.clearCanvas();
this.drawProducts();
return { product, amount } return { product, amount }
} }