class Algorithm{ constructor(){ this.startNode = game.startNode; this.targetNode = game.targetNode; this.grid = game.grid; this.startDate = new Date(); this.timeDiff = 0; } get running(){ return !this.hasSucceeded && !this.hasFailed; } display(){ push(); scale(1 / this.grid.nodeSize.x, 1 / this.grid.nodeSize.y); fill(0); textSize(20); textAlign(CENTER, TOP); text('Time: ' + this.timeDiff / 1000 + 's', this.grid.size / 2, -this.grid.nodeSize.y / 2 - 50); pop(); } update(){ this.timeDiff = new Date().getTime() - this.startDate.getTime(); } success(){ console.log('Success!'); this.hasSucceeded = true; } failed(){ console.log('Failed!'); this.hasFailed = true; } } function startAlgorithm(){ switch ($('#algorithm_type').val()){ case 'astar': game.start(new AStar()); break; case 'dijkstra': game.start(new Dijkstra()); break; case 'dstar': game.start(new DStar()); break; } } class DStar extends Algorithm{ constructor() { super(); this.openList = [] } } class AStar extends Algorithm{ constructor(){ super(); this.startNode.f = 0; this.startNode.g = 0; this.openList = [this.startNode]; this.closedList = []; } get minFNodeIndex(){ let f = this.openList[0].f, index = 0; this.openList.forEach((n, i) => { if (n.f < f){ f = n.f; index = i; } }); return index; } update(){ if (!this.running){ return; } super.update(); if (!this.openList.length){ this.failed(); } else { let index = this.minFNodeIndex; let currentNode = this.openList[index]; this.openList.splice(index, 1); if (currentNode === this.targetNode){ this.success(); } else { this.closedList.push(currentNode); currentNode.successors.forEach(successor => { if (this.closedList.find(n => n === successor)){ return; } let provisionalG = currentNode.g + currentNode.pos.dist(successor.pos); if (this.openList.find(n => n === successor) && provisionalG >= successor.g){ return; } successor.predecessor = currentNode successor.g = provisionalG; successor.f = provisionalG + successor.distanceToTarget; if (!this.openList.find(n => n === successor)){ this.openList.push(successor); } }); } } } display(appearance, isInformationVisible, pathSmoothness){ super.display(); if (isInformationVisible){ if (appearance === 'Circles'){ let r = 0.5; fill(200); this.openList.forEach(n => { ellipse(n.pos.x, n.pos.y, r * 2); }); fill(100); this.closedList.forEach(n => { if (n === this.startNode) return; ellipse(n.pos.x, n.pos.y, r * 2); }); } if (appearance === 'Rectangles'){ fill(200); this.openList.forEach(n => { rect(n.pos.x - 0.5, n.pos.y - 0.5, 1, 1); }); fill(100); this.closedList.forEach(n => { if (n === this.startNode) return; rect(n.pos.x - 0.5, n.pos.y - 0.5, 1, 1); }); } } noFill(); stroke(50, 255, 50); strokeWeight(0.2); let node; if (!this.openList.length){ node = this.closedList[this.closedList.length - 1]; } else { node = this.openList[this.minFNodeIndex]; } if (this.hasSucceeded){ node = this.targetNode; } if (node){ if (pathSmoothness){ beginShape(); vertex(node.pos.x, node.pos.y); while (node.predecessor){ node = node.predecessor; if (!node.predecessor){ quadraticVertex(node.pos.x, node.pos.y, node.pos.x, node.pos.y); endShape(); } else { let cx = (node.pos.x + node.predecessor.pos.x) / 2; let cy = (node.pos.y + node.predecessor.pos.y) / 2; quadraticVertex(node.pos.x, node.pos.y, cx, cy); } } } else { while (node.predecessor){ line(node.pos.x, node.pos.y, node.predecessor.pos.x, node.predecessor.pos.y); node = node.predecessor; } } } } } class Dijkstra extends Algorithm{ constructor(){ super(); this.nodes = this.grid.allowedNodes.slice(); this.nodes.forEach(n => n.distance = Infinity); this.startNode.distance = 0; } get minDistanceNodeIndex(){ let index = 0, distance = this.nodes[0].distance; this.nodes.forEach((n, i) => { if (n.distance < distance){ index = i; distance = n.distance; } }); return index; } update(){ if (!this.running){ return; } super.update(); let index = this.minDistanceNodeIndex; let currentNode = this.nodes[index]; this.nodes.splice(index, 1); if (currentNode === this.targetNode){ this.success(); } currentNode.successors.forEach(s => { if (this.nodes.find(n => n === s)){ let alternativeDistance = currentNode.distance + currentNode.pos.dist(s.pos); if (alternativeDistance < s.distance){ s.distance = alternativeDistance; s.predecessor = currentNode; } } }) } display(appearance, isInformationVisible, pathSmoothness){ super.display(); if (isInformationVisible){ if (appearance === 'Circles'){ let r = 0.5; fill(100); this.nodes.forEach(n => { if (n === this.startNode || n === this.targetNode) return; ellipse(n.pos.x, n.pos.y, r * 2); }); } if (appearance === 'Rectangles'){ fill(100); this.nodes.forEach(n => { if (n === this.startNode || n === this.targetNode) return; rect(n.pos.x - 0.5, n.pos.y - 0.5, 1, 1); }); } } noFill(); stroke(50, 255, 50); strokeWeight(0.2); let node = this.nodes[this.minDistanceNodeIndex]; if (this.hasSucceeded){ node = this.targetNode; } if (node){ if (pathSmoothness){ beginShape(); vertex(node.pos.x, node.pos.y); while (node.predecessor){ node = node.predecessor; if (!node.predecessor){ quadraticVertex(node.pos.x, node.pos.y, node.pos.x, node.pos.y); endShape(); } else { let cx = (node.pos.x + node.predecessor.pos.x) / 2; let cy = (node.pos.y + node.predecessor.pos.y) / 2; quadraticVertex(node.pos.x, node.pos.y, cx, cy); } } } else { while (node.predecessor){ line(node.pos.x, node.pos.y, node.predecessor.pos.x, node.predecessor.pos.y); node = node.predecessor; } } } } }