From 5653fa6b0244914be5592922d295a608781a4aee Mon Sep 17 00:00:00 2001 From: Marcin Czerniak Date: Mon, 19 Apr 2021 13:30:57 +0200 Subject: [PATCH] chore: pathfinding replacement --- src/index.html | 1 + src/main.js | 2 + src/modules/agent.js | 19 +++++-- src/modules/agentController.js | 58 ++++--------------- src/modules/pathfinding.js | 100 +++++++++++++++++++++++++++++++++ src/modules/products.js | 2 + 6 files changed, 131 insertions(+), 51 deletions(-) create mode 100644 src/modules/pathfinding.js diff --git a/src/index.html b/src/index.html index 2546a6c..81adcff 100644 --- a/src/index.html +++ b/src/index.html @@ -20,6 +20,7 @@ + diff --git a/src/main.js b/src/main.js index b253951..32cd266 100644 --- a/src/main.js +++ b/src/main.js @@ -8,4 +8,6 @@ window.addEventListener('DOMContentLoaded', () => document.fonts.ready.then(() = const grid = new Grid(gridCanvas); const agent = new Agent(agentCanvas); const products = new Products(productsCanvas); + + window.products = products; })); \ No newline at end of file diff --git a/src/modules/agent.js b/src/modules/agent.js index 3cb730e..75aa379 100644 --- a/src/modules/agent.js +++ b/src/modules/agent.js @@ -4,15 +4,22 @@ class Agent extends AgentController { const cycle = async () => { - await this.takeFromStore(Product.RANDOM_FROM_REGISTRY(), Math.floor(Math.random() * 40) + 10); - const randomPlace = Grid.instance.randomPlace(); - await this.deliver(randomPlace.x, randomPlace.y); + // await this.takeFromStore(Product.RANDOM_FROM_REGISTRY(), Math.floor(Math.random() * 40) + 10); + // const randomPlace = Grid.instance.randomPlace(); + // 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); cycle(); }; - cycle(); + setTimeout(() => cycle(), 2000); } @@ -32,6 +39,7 @@ class Agent extends AgentController { */ async deliver(x, y) { await this.moveTo(x, y); + window.products.exchange(x, y, this.ownedProduct, 50); this.ownedProductAmount = 0; this.ownedProduct = Product.REGISTRY.empty; } @@ -41,7 +49,8 @@ class Agent extends AgentController { * @param {*} product */ async takeFromStore(product, amount) { - await this.moveTo(6, 8); + await this.moveTo(9, 8); + await waitFor(1000); this.ownedProductAmount = amount; this.ownedProduct = product; } diff --git a/src/modules/agentController.js b/src/modules/agentController.js index 8502906..d9dd7d6 100644 --- a/src/modules/agentController.js +++ b/src/modules/agentController.js @@ -48,10 +48,11 @@ class AgentController { let nearestPoint = this.currentPath[this.currentPathIndex]; if (nearestPoint) { - const requiredRotation = this.findRequiredRotation(nearestPoint); - await this.rotate({ z: requiredRotation }); - if (!nearestPoint.options || !nearestPoint.options.isShelf) { - await this.moveInLine(nearestPoint); + if (nearestPoint.action == PATHFINDING_ACTION.MOVE) { + await this.moveInLine(nearestPoint.state.position); + } + if (nearestPoint.action == PATHFINDING_ACTION.ROTATE) { + await this.rotate({ z: nearestPoint.state.rotation }); } nearestPoint = this.currentPath[++this.currentPathIndex]; @@ -134,9 +135,6 @@ class AgentController { */ rotate(rotation) { return new Promise((resolve, reject) => { - // console.log(this.rotation); // 270 - // console.log(rotation); // 0 - if (this.rotation.z == rotation.z) { resolve(); } @@ -172,7 +170,6 @@ class AgentController { moveTo(x, y) { this.currentPath = this.findPathTo(x, y, this.position.x, this.position.y); - console.log(this.currentPath); return this.updateAgentPosition(); } @@ -181,8 +178,8 @@ class AgentController { this.ctx.beginPath(); this.ctx.strokeWidth = 5; this.ctx.strokeStyle = 'red'; - this.ctx.moveTo(path[i].x * 100 + 50, path[i].y * 100 + 50); - this.ctx.lineTo(path[i + 1].x * 100 + 50, path[i + 1].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].state.position.x * 100 + 50, path[i + 1].state.position.y * 100 + 50); this.ctx.stroke(); } } @@ -230,44 +227,13 @@ class AgentController { } findPathTo(dx, dy, sx, sy) { - const grid = JSON.parse(JSON.stringify(Grid.instance.grid)); - const queue = []; - const lastPoint = []; - - 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--; + if (Grid.instance.getGrid()[dx][dy] == GRID_FIELD_TYPE.SHELF || Grid.instance.getGrid()[dx][dy] == GRID_FIELD_TYPE.STORAGE) { + if (Grid.instance.getGrid()[dx][dy - 1] == GRID_FIELD_TYPE.PATH) { + return Pathfinding.search({ position: { x: sx, y: sy }, rotation: this.rotation.z }, { position: { x: dx, y: dy - 1 }, rotation: 180 }); } + return Pathfinding.search({ position: { x: sx, y: sy }, rotation: this.rotation.z }, { position: { x: dx, y: dy + 1 }, rotation: 0 }); } - - 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; - } + return Pathfinding.search({ position: { x: sx, y: sy }, rotation: this.rotation.z }, { position: { x: dx, y: dy }, rotation: this.rotation.z }); } clearCanvas() { diff --git a/src/modules/pathfinding.js b/src/modules/pathfinding.js new file mode 100644 index 0000000..2a1f5b1 --- /dev/null +++ b/src/modules/pathfinding.js @@ -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 }; + } + } +} \ No newline at end of file diff --git a/src/modules/products.js b/src/modules/products.js index 07e18d9..8af2572 100644 --- a/src/modules/products.js +++ b/src/modules/products.js @@ -130,6 +130,8 @@ class Products { const amount = this.gridProductsAmount[x][y] this.gridProducts[x][y] = incomingProduct this.gridProductsAmount[x][y] = incomingAmount + this.clearCanvas(); + this.drawProducts(); return { product, amount } }