From fe69b98a716520776b7f0821685d568c0fa253b4 Mon Sep 17 00:00:00 2001 From: Marcin Czerniak Date: Tue, 16 Mar 2021 23:13:58 +0100 Subject: [PATCH] feat: agent --- src/index.html | 2 + src/main.js | 1 + src/modules/agent.js | 13 +++ src/modules/agentController.js | 146 +++++++++++++++++++++++++++++++++ src/modules/grid.js | 15 +++- 5 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 src/modules/agent.js create mode 100644 src/modules/agentController.js diff --git a/src/index.html b/src/index.html index 2742d86..8585e1f 100644 --- a/src/index.html +++ b/src/index.html @@ -13,6 +13,8 @@ + + diff --git a/src/main.js b/src/main.js index 532bfa5..18eccc7 100644 --- a/src/main.js +++ b/src/main.js @@ -6,4 +6,5 @@ window.addEventListener('DOMContentLoaded', () => { const floor = new Floor(floorCanvas); const grid = new Grid(gridCanvas); + const agent = new Agent(agentCanvas); }); \ No newline at end of file diff --git a/src/modules/agent.js b/src/modules/agent.js new file mode 100644 index 0000000..e2b5be2 --- /dev/null +++ b/src/modules/agent.js @@ -0,0 +1,13 @@ +class Agent extends AgentController { + constructor(canvas) { + super(canvas); + + let { x, y } = Grid.instance.randomPlace(); + this.moveTo(x, y); + + this.onDestinationReached(() => { + let { x, y } = Grid.instance.randomPlace(); + this.moveTo(x, y); + }) + } +} \ No newline at end of file diff --git a/src/modules/agentController.js b/src/modules/agentController.js new file mode 100644 index 0000000..a6a0501 --- /dev/null +++ b/src/modules/agentController.js @@ -0,0 +1,146 @@ +class AgentController { + constructor(canvas) { + this.ctx = canvas.getContext('2d'); + this.events = { + 'ON_DESTINATION_REACHED': [], + }; + + this.setCanvasSize(canvas); + + this.position = { x: 6, y: 0 }; + + this.update(); + } + + setCanvasSize (canvas) { + canvas.width = 2000; + canvas.height = 900; + } + + update() { + this.clearCanvas(); + if (this.currentPath) { + this.drawPath(this.currentPath); + this.updateAgentPosition(); + } + this.drawAgent(); + + requestAnimationFrame(this.update.bind(this)); + } + + updateAgentPosition() { + const speed = 30; + if (!this.currentPathIndex) { + this.currentPathIndex = 0; + } + + let nearestPoint = this.currentPath[this.currentPathIndex]; + + if (nearestPoint) { + this.position = { + x: this.position.x + (Math.sign(nearestPoint.x - this.position.x)/speed), + y: this.position.y + (Math.sign(nearestPoint.y - this.position.y)/speed) + } + if (Math.abs(nearestPoint.x - this.position.x) < 0.001 && Math.abs(nearestPoint.y - this.position.y) < 0.001) { + nearestPoint = this.currentPath[++this.currentPathIndex]; + if (!nearestPoint) { + this.position = { + x: Math.round(this.position.x), + y: Math.round(this.position.y) + } + this.currentPath = []; + this.currentPathIndex = 0; + this.events.ON_DESTINATION_REACHED.forEach(func => func()); + } + } + } + } + + moveTo(x, y) { + this.currentPath = this.findPathTo(x, y, this.position.x, this.position.y); + } + + drawPath(path) { + for(let i = 0; i < path.length - 1; i++) { + 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.stroke(); + } + } + + drawAgent() { + const {x, y} = this.position; + const offset = 5; + this.ctx.clearRect((x * 100) + (offset), (y * 100) + (offset), 100 - (offset * 2), 100 - (offset * 2)); + this.roundRect((x * 100) + (offset), (y * 100) + (offset), 100 - (offset * 2), 100 - (offset * 2)); + } + + 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--; + } + } + + 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; + } + } + + onDestinationReached(func) { + this.events.ON_DESTINATION_REACHED.push(func); + } + + clearCanvas() { + this.ctx.clearRect(0, 0, 2000, 900); + } + + roundRect(x, y, w, h, r = 20) { + this.ctx.lineWidth = 5; + this.ctx.strokeStyle = 'red'; + this.ctx.beginPath(); + this.ctx.moveTo(x + r, y); + this.ctx.lineTo(x + w - r, y); + this.ctx.quadraticCurveTo(x + w, y, x + w, y + r); + this.ctx.lineTo(x + w, y + h - r); + this.ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); + this.ctx.lineTo(x + r, y + h); + this.ctx.quadraticCurveTo(x, y + h, x, y + h - r); + this.ctx.lineTo(x, y + r); + this.ctx.quadraticCurveTo(x, y, x + r, y); + this.ctx.closePath(); + this.ctx.stroke(); + } +} \ No newline at end of file diff --git a/src/modules/grid.js b/src/modules/grid.js index f08dfdb..f15ea2a 100644 --- a/src/modules/grid.js +++ b/src/modules/grid.js @@ -5,6 +5,8 @@ const GRID_FIELD_TYPE = Object.freeze({ }); class Grid { + static instance; + constructor(canvas) { this.grid = [[]]; this.ctx; @@ -15,6 +17,8 @@ class Grid { this.processGrid(); this.drawShelfs(); this.drawStorages(); + + Grid.instance = this; } getGrid () { @@ -39,7 +43,6 @@ class Grid { for (let i = 0; i < 20; i++) { this.grid[i] = [...this.grid[i], lastLine[i]]; } - console.log(this.grid); } drawShelfs () { @@ -52,6 +55,16 @@ class Grid { } } + randomPlace() { + const x = Math.floor(Math.random() * this.grid.length); + const y = Math.floor(Math.random() * this.grid[0].length); + const rand = this.grid[x][y]; + if (rand === GRID_FIELD_TYPE.SHELF || rand === GRID_FIELD_TYPE.PATH) { + return { x, y }; + } + return this.randomPlace(); + } + drawStorages () { let isFirstOfType = true; let isLastOfType = false;