chore: pathfinding replacement
This commit is contained in:
parent
37c068e91b
commit
5653fa6b02
@ -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>
|
||||||
|
@ -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;
|
||||||
}));
|
}));
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
100
src/modules/pathfinding.js
Normal 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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user