├── .gitignore ├── README.md ├── index.html ├── package.json ├── public ├── browser │ ├── animations │ │ ├── launchAnimations.js │ │ ├── launchInstantAnimations.js │ │ └── mazeGenerationAnimations.js │ ├── board.js │ ├── bundle.js │ ├── getDistance.js │ ├── mazeAlgorithms │ │ ├── otherMaze.js │ │ ├── otherOtherMaze.js │ │ ├── recursiveDivisionMaze.js │ │ ├── simpleDemonstration.js │ │ ├── stairDemonstration.js │ │ └── weightsDemonstration.js │ ├── node.js │ └── pathfindingAlgorithms │ │ ├── astar.js │ │ ├── bidirectional.js │ │ ├── testAlgorithm.js │ │ ├── unweightedSearchAlgorithm.js │ │ └── weightedSearchAlgorithm.js └── styling │ ├── algorithms.png │ ├── bomb.png │ ├── c_icon.png │ ├── car-down.png │ ├── car-left.png │ ├── car-right.png │ ├── car-up.png │ ├── circle.svg │ ├── cssBasic.css │ ├── cssPokemon.css │ ├── diamond.svg │ ├── dragging.gif │ ├── fonts │ ├── glyphicons │ │ ├── flat-ui-icons-regular.eot │ │ ├── flat-ui-icons-regular.svg │ │ ├── flat-ui-icons-regular.ttf │ │ ├── flat-ui-icons-regular.woff │ │ └── selection.json │ └── lato │ │ ├── lato-black.eot │ │ ├── lato-black.svg │ │ ├── lato-black.ttf │ │ ├── lato-black.woff │ │ ├── lato-bold.eot │ │ ├── lato-bold.svg │ │ ├── lato-bold.ttf │ │ ├── lato-bold.woff │ │ ├── lato-bolditalic.eot │ │ ├── lato-bolditalic.svg │ │ ├── lato-bolditalic.ttf │ │ ├── lato-bolditalic.woff │ │ ├── lato-italic.eot │ │ ├── lato-italic.svg │ │ ├── lato-italic.ttf │ │ ├── lato-italic.woff │ │ ├── lato-light.eot │ │ ├── lato-light.svg │ │ ├── lato-light.ttf │ │ ├── lato-light.woff │ │ ├── lato-regular.eot │ │ ├── lato-regular.svg │ │ ├── lato-regular.ttf │ │ └── lato-regular.woff │ ├── navbar.png │ ├── path.png │ ├── pokemonweight.png │ ├── spaceshiptwo-down.svg │ ├── spaceshiptwo-left.svg │ ├── spaceshiptwo-right.svg │ ├── spaceshiptwo-up.svg │ ├── triangletwo-down.svg │ ├── triangletwo-left.svg │ ├── triangletwo-right.svg │ ├── triangletwo-up.svg │ ├── walls.gif │ ├── wallsandweights.png │ └── weight.svg └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | stuff.js 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pathfinding Visualizer 2 | 3 | Welcome to Pathfinding Visualizer! I built this application because I was fascinated by pathfinding algorithms, and I wanted to visualize them in action. I hope that you enjoy playing around with this visualization tool just as much as I enjoyed building it. You can access it here (use Google Chrome!): https://clementmihailescu.github.io/Pathfinding-Visualizer/ 4 | 5 | ## Meet the Algorithms 6 | 7 | This application supports the following algorithms: 8 | 9 | **Dijkstra's Algorithm** (weighted): the father of pathfinding algorithms; guarantees the shortest path 10 | 11 | **A* Search** (weighted): arguably the best pathfinding algorithm; uses heuristics to guarantee the shortest path much faster than Dijkstra's Algorithm 12 | 13 | **Greedy Best-first Search** (weighted): a faster, more heuristic-heavy version of A*; does not guarantee the shortest path 14 | 15 | **Swarm Algorithm** (weighted): a mixture of Dijkstra's Algorithm and A*; does not guarantee the shortest-path 16 | 17 | **Convergent Swarm Algorithm** (weighted): the faster, more heuristic-heavy version of Swarm; does not guarantee the shortest path 18 | 19 | **Bidirectional Swarm Algorithm** (weighted): Swarm from both sides; does not guarantee the shortest path 20 | 21 | **Breath-first Search** (unweighted): a great algorithm; guarantees the shortest path 22 | 23 | **Depth-first Search** (unweighted): a very bad algorithm for pathfinding; does not guarantee the shortest path 24 | 25 | On top of the pathfinding algorithms listed above, I implemented a **Recursive Division** Maze Generation algorithm. 26 | 27 | ## More about the Swarm Algorithm 28 | 29 | The Swarm Algorithm is an algorithm that I - at least presumably so (I was unable to find anything close to it online) - co-developed with a good friend and colleague, Hussein Farah. The algorithm is essentially a mixture of Dijkstra's Algorithm and A* Search; more precisely, while it converges to the target node like A* , it still explores quite a few neighboring nodes surrounding the start node like Dijkstra's. The algorithm differentiates itself from A* through its use of heuristics: it continually updates nodes' distance from the start node while taking into account their estimated distance from the target node. This effectively "balances" the difference in total distance between nodes closer to the start node and nodes closer to the target node, which results in the triangle-like shape of the Swarm Algorithm. We named the algorithm "Swarm" because one of its potential applications could be seen in a video-game where a character must keep track of a boss with high priority (the target node), all the while keeping tracking of neighboring enemies that might be swarming nearby. 30 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Pathfinding Visualizer 4 | 5 | 6 | 7 | 8 | 9 | 60 |
61 |

Welcome to Pathfinding Visualizer!

62 |
This short tutorial will walk you through all of the features of this application.
63 |

If you want to dive right in, feel free to press the "Skip Tutorial" button below. Otherwise, press "Next"!

64 |
1/9
65 | 66 | 67 | 68 | 69 |
70 |
71 |
72 | 90 |
91 |
Pick an algorithm and visualize it!
92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pathfinding_algorithms", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "watch": "watchify /home/clement/Documents/Fullstack_Academy/Projects/Pathfinding_Algorithms/public/browser/board.js -o /home/clement/Documents/Fullstack_Academy/Projects/Pathfinding_Algorithms/public/browser/bundle.js" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "express": "^4.14.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /public/browser/animations/launchAnimations.js: -------------------------------------------------------------------------------- 1 | const weightedSearchAlgorithm = require("../pathfindingAlgorithms/weightedSearchAlgorithm"); 2 | const unweightedSearchAlgorithm = require("../pathfindingAlgorithms/unweightedSearchAlgorithm"); 3 | 4 | function launchAnimations(board, success, type, object, algorithm, heuristic) { 5 | let nodes = object ? board.objectNodesToAnimate.slice(0) : board.nodesToAnimate.slice(0); 6 | let speed = board.speed === "fast" ? 7 | 0 : board.speed === "average" ? 8 | 100 : 500; 9 | let shortestNodes; 10 | function timeout(index) { 11 | setTimeout(function () { 12 | if (index === nodes.length) { 13 | if (object) { 14 | board.objectNodesToAnimate = []; 15 | if (success) { 16 | board.addShortestPath(board.object, board.start, "object"); 17 | board.clearNodeStatuses(); 18 | let newSuccess; 19 | if (board.currentAlgorithm === "bidirectional") { 20 | 21 | } else { 22 | if (type === "weighted") { 23 | newSuccess = weightedSearchAlgorithm(board.nodes, board.object, board.target, board.nodesToAnimate, board.boardArray, algorithm, heuristic); 24 | } else { 25 | newSuccess = unweightedSearchAlgorithm(board.nodes, board.object, board.target, board.nodesToAnimate, board.boardArray, algorithm); 26 | } 27 | } 28 | document.getElementById(board.object).className = "visitedObjectNode"; 29 | launchAnimations(board, newSuccess, type); 30 | return; 31 | } else { 32 | console.log("Failure."); 33 | board.reset(); 34 | board.toggleButtons(); 35 | return; 36 | } 37 | } else { 38 | board.nodesToAnimate = []; 39 | if (success) { 40 | if (document.getElementById(board.target).className !== "visitedTargetNodeBlue") { 41 | document.getElementById(board.target).className = "visitedTargetNodeBlue"; 42 | } 43 | if (board.isObject) { 44 | board.addShortestPath(board.target, board.object); 45 | board.drawShortestPathTimeout(board.target, board.object, type, "object"); 46 | board.objectShortestPathNodesToAnimate = []; 47 | board.shortestPathNodesToAnimate = []; 48 | board.reset("objectNotTransparent"); 49 | } else { 50 | board.drawShortestPathTimeout(board.target, board.start, type); 51 | board.objectShortestPathNodesToAnimate = []; 52 | board.shortestPathNodesToAnimate = []; 53 | board.reset(); 54 | } 55 | shortestNodes = board.objectShortestPathNodesToAnimate.concat(board.shortestPathNodesToAnimate); 56 | return; 57 | } else { 58 | console.log("Failure."); 59 | board.reset(); 60 | board.toggleButtons(); 61 | return; 62 | } 63 | } 64 | } else if (index === 0) { 65 | if (object) { 66 | document.getElementById(board.start).className = "visitedStartNodePurple"; 67 | } else { 68 | if (document.getElementById(board.start).className !== "visitedStartNodePurple") { 69 | document.getElementById(board.start).className = "visitedStartNodeBlue"; 70 | } 71 | } 72 | if (board.currentAlgorithm === "bidirectional") { 73 | document.getElementById(board.target).className = "visitedTargetNodeBlue"; 74 | } 75 | change(nodes[index]) 76 | } else if (index === nodes.length - 1 && board.currentAlgorithm === "bidirectional") { 77 | change(nodes[index], nodes[index - 1], "bidirectional"); 78 | } else { 79 | change(nodes[index], nodes[index - 1]); 80 | } 81 | timeout(index + 1); 82 | }, speed); 83 | } 84 | 85 | function change(currentNode, previousNode, bidirectional) { 86 | let currentHTMLNode = document.getElementById(currentNode.id); 87 | let relevantClassNames = ["start", "target", "object", "visitedStartNodeBlue", "visitedStartNodePurple", "visitedObjectNode", "visitedTargetNodePurple", "visitedTargetNodeBlue"]; 88 | if (!relevantClassNames.includes(currentHTMLNode.className)) { 89 | currentHTMLNode.className = !bidirectional ? 90 | "current" : currentNode.weight === 15 ? 91 | "visited weight" : "visited"; 92 | } 93 | if (currentHTMLNode.className === "visitedStartNodePurple" && !object) { 94 | currentHTMLNode.className = "visitedStartNodeBlue"; 95 | } 96 | if (currentHTMLNode.className === "target" && object) { 97 | currentHTMLNode.className = "visitedTargetNodePurple"; 98 | } 99 | if (previousNode) { 100 | let previousHTMLNode = document.getElementById(previousNode.id); 101 | if (!relevantClassNames.includes(previousHTMLNode.className)) { 102 | if (object) { 103 | previousHTMLNode.className = previousNode.weight === 15 ? "visitedobject weight" : "visitedobject"; 104 | } else { 105 | previousHTMLNode.className = previousNode.weight === 15 ? "visited weight" : "visited"; 106 | } 107 | } 108 | } 109 | } 110 | 111 | function shortestPathTimeout(index) { 112 | setTimeout(function () { 113 | if (index === shortestNodes.length){ 114 | board.reset(); 115 | if (object) { 116 | shortestPathChange(board.nodes[board.target], shortestNodes[index - 1]); 117 | board.objectShortestPathNodesToAnimate = []; 118 | board.shortestPathNodesToAnimate = []; 119 | board.clearNodeStatuses(); 120 | let newSuccess; 121 | if (type === "weighted") { 122 | newSuccess = weightedSearchAlgorithm(board.nodes, board.object, board.target, board.nodesToAnimate, board.boardArray, algorithm); 123 | } else { 124 | newSuccess = unweightedSearchAlgorithm(board.nodes, board.object, board.target, board.nodesToAnimate, board.boardArray, algorithm); 125 | } 126 | launchAnimations(board, newSuccess, type); 127 | return; 128 | } else { 129 | shortestPathChange(board.nodes[board.target], shortestNodes[index - 1]); 130 | board.objectShortestPathNodesToAnimate = []; 131 | board.shortestPathNodesToAnimate = []; 132 | return; 133 | } 134 | } else if (index === 0) { 135 | shortestPathChange(shortestNodes[index]) 136 | } else { 137 | shortestPathChange(shortestNodes[index], shortestNodes[index - 1]); 138 | } 139 | shortestPathTimeout(index + 1); 140 | }, 40); 141 | } 142 | 143 | function shortestPathChange(currentNode, previousNode) { 144 | let currentHTMLNode = document.getElementById(currentNode.id); 145 | if (type === "unweighted") { 146 | currentHTMLNode.className = "shortest-path-unweighted"; 147 | } else { 148 | if (currentNode.direction === "up") { 149 | currentHTMLNode.className = "shortest-path-up"; 150 | } else if (currentNode.direction === "down") { 151 | currentHTMLNode.className = "shortest-path-down"; 152 | } else if (currentNode.direction === "right") { 153 | currentHTMLNode.className = "shortest-path-right"; 154 | } else if (currentNode.direction === "left") { 155 | currentHTMLNode.className = "shortest-path-left"; 156 | } else if (currentNode.direction = "down-right") { 157 | currentHTMLNode.className = "wall" 158 | } 159 | } 160 | if (previousNode) { 161 | let previousHTMLNode = document.getElementById(previousNode.id); 162 | previousHTMLNode.className = "shortest-path"; 163 | } else { 164 | let element = document.getElementById(board.start); 165 | element.className = "shortest-path"; 166 | element.removeAttribute("style"); 167 | } 168 | } 169 | 170 | timeout(0); 171 | 172 | }; 173 | 174 | module.exports = launchAnimations; 175 | -------------------------------------------------------------------------------- /public/browser/animations/launchInstantAnimations.js: -------------------------------------------------------------------------------- 1 | const weightedSearchAlgorithm = require("../pathfindingAlgorithms/weightedSearchAlgorithm"); 2 | const unweightedSearchAlgorithm = require("../pathfindingAlgorithms/unweightedSearchAlgorithm"); 3 | 4 | function launchInstantAnimations(board, success, type, object, algorithm, heuristic) { 5 | let nodes = object ? board.objectNodesToAnimate.slice(0) : board.nodesToAnimate.slice(0); 6 | let shortestNodes; 7 | for (let i = 0; i < nodes.length; i++) { 8 | if (i === 0) { 9 | change(nodes[i]); 10 | } else { 11 | change(nodes[i], nodes[i - 1]); 12 | } 13 | } 14 | if (object) { 15 | board.objectNodesToAnimate = []; 16 | if (success) { 17 | board.drawShortestPath(board.object, board.start, "object"); 18 | board.clearNodeStatuses(); 19 | let newSuccess; 20 | if (type === "weighted") { 21 | newSuccess = weightedSearchAlgorithm(board.nodes, board.object, board.target, board.nodesToAnimate, board.boardArray, algorithm, heuristic); 22 | } else { 23 | newSuccess = unweightedSearchAlgorithm(board.nodes, board.object, board.target, board.nodesToAnimate, board.boardArray, algorithm); 24 | } 25 | launchInstantAnimations(board, newSuccess, type); 26 | shortestNodes = board.objectShortestPathNodesToAnimate.concat(board.shortestPathNodesToAnimate); 27 | } else { 28 | console.log("Failure."); 29 | board.reset(); 30 | return; 31 | } 32 | } else { 33 | board.nodesToAnimate = []; 34 | if (success) { 35 | if (board.isObject) { 36 | board.drawShortestPath(board.target, board.object); 37 | } else { 38 | board.drawShortestPath(board.target, board.start); 39 | } 40 | shortestNodes = board.objectShortestPathNodesToAnimate.concat(board.shortestPathNodesToAnimate); 41 | } else { 42 | console.log("Failure"); 43 | board.reset(); 44 | return; 45 | } 46 | } 47 | 48 | let j; 49 | for (j = 0; j < shortestNodes.length; j++) { 50 | if (j === 0) { 51 | shortestPathChange(shortestNodes[j]); 52 | } else { 53 | shortestPathChange(shortestNodes[j], shortestNodes[j - 1]); 54 | } 55 | } 56 | board.reset(); 57 | if (object) { 58 | shortestPathChange(board.nodes[board.target], shortestNodes[j - 1]); 59 | board.objectShortestPathNodesToAnimate = []; 60 | board.shortestPathNodesToAnimate = []; 61 | board.clearNodeStatuses(); 62 | let newSuccess; 63 | if (type === "weighted") { 64 | newSuccess = weightedSearchAlgorithm(board.nodes, board.object, board.target, board.nodesToAnimate, board.boardArray, algorithm); 65 | } else { 66 | newSuccess = unweightedSearchAlgorithm(board.nodes, board.object, board.target, board.nodesToAnimate, board.boardArray, algorithm); 67 | } 68 | launchInstantAnimations(board, newSuccess, type); 69 | } else { 70 | shortestPathChange(board.nodes[board.target], shortestNodes[j - 1]); 71 | board.objectShortestPathNodesToAnimate = []; 72 | board.shortestPathNodesToAnimate = []; 73 | } 74 | 75 | function change(currentNode, previousNode) { 76 | let currentHTMLNode = document.getElementById(currentNode.id); 77 | let relevantClassNames = ["start", "shortest-path", "instantshortest-path", "instantshortest-path weight"]; 78 | if (previousNode) { 79 | let previousHTMLNode = document.getElementById(previousNode.id); 80 | if (!relevantClassNames.includes(previousHTMLNode.className)) { 81 | if (object) { 82 | previousHTMLNode.className = previousNode.weight === 15 ? "instantvisitedobject weight" : "instantvisitedobject"; 83 | } else { 84 | previousHTMLNode.className = previousNode.weight === 15 ? "instantvisited weight" : "instantvisited"; 85 | } 86 | } 87 | } 88 | } 89 | 90 | function shortestPathChange(currentNode, previousNode) { 91 | let currentHTMLNode = document.getElementById(currentNode.id); 92 | if (type === "unweighted") { 93 | currentHTMLNode.className = "shortest-path-unweighted"; 94 | } else { 95 | if (currentNode.direction === "up") { 96 | currentHTMLNode.className = "shortest-path-up"; 97 | } else if (currentNode.direction === "down") { 98 | currentHTMLNode.className = "shortest-path-down"; 99 | } else if (currentNode.direction === "right") { 100 | currentHTMLNode.className = "shortest-path-right"; 101 | } else if (currentNode.direction === "left") { 102 | currentHTMLNode.className = "shortest-path-left"; 103 | } 104 | } 105 | if (previousNode) { 106 | let previousHTMLNode = document.getElementById(previousNode.id); 107 | previousHTMLNode.className = previousNode.weight === 15 ? "instantshortest-path weight" : "instantshortest-path"; 108 | } else { 109 | let element = document.getElementById(board.start); 110 | element.className = "startTransparent"; 111 | } 112 | } 113 | 114 | }; 115 | 116 | module.exports = launchInstantAnimations; 117 | -------------------------------------------------------------------------------- /public/browser/animations/mazeGenerationAnimations.js: -------------------------------------------------------------------------------- 1 | function mazeGenerationAnimations(board) { 2 | let nodes = board.wallsToAnimate.slice(0); 3 | let speed = board.speed === "fast" ? 4 | 5 : board.speed === "average" ? 5 | 25 : 75; 6 | function timeout(index) { 7 | setTimeout(function () { 8 | if (index === nodes.length){ 9 | board.wallsToAnimate = []; 10 | board.toggleButtons(); 11 | return; 12 | } 13 | nodes[index].className = board.nodes[nodes[index].id].weight === 15 ? "unvisited weight" : "wall"; 14 | timeout(index + 1); 15 | }, speed); 16 | } 17 | 18 | timeout(0); 19 | }; 20 | 21 | module.exports = mazeGenerationAnimations; 22 | -------------------------------------------------------------------------------- /public/browser/getDistance.js: -------------------------------------------------------------------------------- 1 | function getDistance(nodeOne, nodeTwo) { 2 | let currentCoordinates = nodeOne.id.split("-"); 3 | let targetCoordinates = nodeTwo.id.split("-"); 4 | let x1 = parseInt(currentCoordinates[0]); 5 | let y1 = parseInt(currentCoordinates[1]); 6 | let x2 = parseInt(targetCoordinates[0]); 7 | let y2 = parseInt(targetCoordinates[1]); 8 | if (x2 < x1) { 9 | if (nodeOne.direction === "up") { 10 | return [1, ["f"], "up"]; 11 | } else if (nodeOne.direction === "right") { 12 | return [2, ["l", "f"], "up"]; 13 | } else if (nodeOne.direction === "left") { 14 | return [2, ["r", "f"], "up"]; 15 | } else if (nodeOne.direction === "down") { 16 | return [3, ["r", "r", "f"], "up"]; 17 | } 18 | } else if (x2 > x1) { 19 | if (nodeOne.direction === "up") { 20 | return [3, ["r", "r", "f"], "down"]; 21 | } else if (nodeOne.direction === "right") { 22 | return [2, ["r", "f"], "down"]; 23 | } else if (nodeOne.direction === "left") { 24 | return [2, ["l", "f"], "down"]; 25 | } else if (nodeOne.direction === "down") { 26 | return [1, ["f"], "down"]; 27 | } 28 | } 29 | if (y2 < y1) { 30 | if (nodeOne.direction === "up") { 31 | return [2, ["l", "f"], "left"]; 32 | } else if (nodeOne.direction === "right") { 33 | return [3, ["l", "l", "f"], "left"]; 34 | } else if (nodeOne.direction === "left") { 35 | return [1, ["f"], "left"]; 36 | } else if (nodeOne.direction === "down") { 37 | return [2, ["r", "f"], "left"]; 38 | } 39 | } else if (y2 > y1) { 40 | if (nodeOne.direction === "up") { 41 | return [2, ["r", "f"], "right"]; 42 | } else if (nodeOne.direction === "right") { 43 | return [1, ["f"], "right"]; 44 | } else if (nodeOne.direction === "left") { 45 | return [3, ["r", "r", "f"], "right"]; 46 | } else if (nodeOne.direction === "down") { 47 | return [2, ["l", "f"], "right"]; 48 | } 49 | } 50 | } 51 | 52 | module.exports = getDistance; 53 | -------------------------------------------------------------------------------- /public/browser/mazeAlgorithms/otherMaze.js: -------------------------------------------------------------------------------- 1 | function recursiveDivisionMaze(board, rowStart, rowEnd, colStart, colEnd, orientation, surroundingWalls) { 2 | if (rowEnd < rowStart || colEnd < colStart) { 3 | return; 4 | } 5 | if (!surroundingWalls) { 6 | let relevantIds = [board.start, board.target]; 7 | if (board.object) relevantIds.push(board.object); 8 | Object.keys(board.nodes).forEach(node => { 9 | if (!relevantIds.includes(node)) { 10 | let r = parseInt(node.split("-")[0]); 11 | let c = parseInt(node.split("-")[1]); 12 | if (r === 0 || c === 0 || r === board.height - 1 || c === board.width - 1) { 13 | let currentHTMLNode = document.getElementById(node); 14 | board.wallsToAnimate.push(currentHTMLNode); 15 | board.nodes[node].status = "wall"; 16 | } 17 | } 18 | }); 19 | surroundingWalls = true; 20 | } 21 | if (orientation === "horizontal") { 22 | let possibleRows = []; 23 | for (let number = rowStart; number <= rowEnd; number += 2) { 24 | possibleRows.push(number); 25 | } 26 | let possibleCols = []; 27 | for (let number = colStart - 1; number <= colEnd + 1; number += 2) { 28 | possibleCols.push(number); 29 | } 30 | let randomRowIndex = Math.floor(Math.random() * possibleRows.length); 31 | let randomColIndex = Math.floor(Math.random() * possibleCols.length); 32 | let currentRow = possibleRows[randomRowIndex]; 33 | let colRandom = possibleCols[randomColIndex]; 34 | Object.keys(board.nodes).forEach(node => { 35 | let r = parseInt(node.split("-")[0]); 36 | let c = parseInt(node.split("-")[1]); 37 | if (r === currentRow && c !== colRandom && c >= colStart - 1 && c <= colEnd + 1) { 38 | let currentHTMLNode = document.getElementById(node); 39 | if (currentHTMLNode.className !== "start" && currentHTMLNode.className !== "target" && currentHTMLNode.className !== "object") { 40 | board.wallsToAnimate.push(currentHTMLNode); 41 | board.nodes[node].status = "wall"; 42 | } 43 | } 44 | }); 45 | if (currentRow - 2 - rowStart > colEnd - colStart) { 46 | recursiveDivisionMaze(board, rowStart, currentRow - 2, colStart, colEnd, orientation, surroundingWalls); 47 | } else { 48 | recursiveDivisionMaze(board, rowStart, currentRow - 2, colStart, colEnd, "vertical", surroundingWalls); 49 | } 50 | if (rowEnd - (currentRow + 2) > colEnd - colStart) { 51 | recursiveDivisionMaze(board, currentRow + 2, rowEnd, colStart, colEnd, "vertical", surroundingWalls); 52 | } else { 53 | recursiveDivisionMaze(board, currentRow + 2, rowEnd, colStart, colEnd, "vertical", surroundingWalls); 54 | } 55 | } else { 56 | let possibleCols = []; 57 | for (let number = colStart; number <= colEnd; number += 2) { 58 | possibleCols.push(number); 59 | } 60 | let possibleRows = []; 61 | for (let number = rowStart - 1; number <= rowEnd + 1; number += 2) { 62 | possibleRows.push(number); 63 | } 64 | let randomColIndex = Math.floor(Math.random() * possibleCols.length); 65 | let randomRowIndex = Math.floor(Math.random() * possibleRows.length); 66 | let currentCol = possibleCols[randomColIndex]; 67 | let rowRandom = possibleRows[randomRowIndex]; 68 | Object.keys(board.nodes).forEach(node => { 69 | let r = parseInt(node.split("-")[0]); 70 | let c = parseInt(node.split("-")[1]); 71 | if (c === currentCol && r !== rowRandom && r >= rowStart - 1 && r <= rowEnd + 1) { 72 | let currentHTMLNode = document.getElementById(node); 73 | if (currentHTMLNode.className !== "start" && currentHTMLNode.className !== "target" && currentHTMLNode.className !== "object") { 74 | board.wallsToAnimate.push(currentHTMLNode); 75 | board.nodes[node].status = "wall"; 76 | } 77 | } 78 | }); 79 | if (rowEnd - rowStart > currentCol - 2 - colStart) { 80 | recursiveDivisionMaze(board, rowStart, rowEnd, colStart, currentCol - 2, "vertical", surroundingWalls); 81 | } else { 82 | recursiveDivisionMaze(board, rowStart, rowEnd, colStart, currentCol - 2, orientation, surroundingWalls); 83 | } 84 | if (rowEnd - rowStart > colEnd - (currentCol + 2)) { 85 | recursiveDivisionMaze(board, rowStart, rowEnd, currentCol + 2, colEnd, "horizontal", surroundingWalls); 86 | } else { 87 | recursiveDivisionMaze(board, rowStart, rowEnd, currentCol + 2, colEnd, orientation, surroundingWalls); 88 | } 89 | } 90 | }; 91 | 92 | module.exports = recursiveDivisionMaze; 93 | -------------------------------------------------------------------------------- /public/browser/mazeAlgorithms/otherOtherMaze.js: -------------------------------------------------------------------------------- 1 | function recursiveDivisionMaze(board, rowStart, rowEnd, colStart, colEnd, orientation, surroundingWalls) { 2 | if (rowEnd < rowStart || colEnd < colStart) { 3 | return; 4 | } 5 | if (!surroundingWalls) { 6 | let relevantIds = [board.start, board.target]; 7 | if (board.object) relevantIds.push(board.object); 8 | Object.keys(board.nodes).forEach(node => { 9 | if (!relevantIds.includes(node)) { 10 | let r = parseInt(node.split("-")[0]); 11 | let c = parseInt(node.split("-")[1]); 12 | if (r === 0 || c === 0 || r === board.height - 1 || c === board.width - 1) { 13 | let currentHTMLNode = document.getElementById(node); 14 | board.wallsToAnimate.push(currentHTMLNode); 15 | board.nodes[node].status = "wall"; 16 | } 17 | } 18 | }); 19 | surroundingWalls = true; 20 | } 21 | if (orientation === "horizontal") { 22 | let possibleRows = []; 23 | for (let number = rowStart; number <= rowEnd; number += 2) { 24 | possibleRows.push(number); 25 | } 26 | let possibleCols = []; 27 | for (let number = colStart - 1; number <= colEnd + 1; number += 2) { 28 | possibleCols.push(number); 29 | } 30 | let randomRowIndex = Math.floor(Math.random() * possibleRows.length); 31 | let randomColIndex = Math.floor(Math.random() * possibleCols.length); 32 | let currentRow = possibleRows[randomRowIndex]; 33 | let colRandom = possibleCols[randomColIndex]; 34 | Object.keys(board.nodes).forEach(node => { 35 | let r = parseInt(node.split("-")[0]); 36 | let c = parseInt(node.split("-")[1]); 37 | if (r === currentRow && c !== colRandom && c >= colStart - 1 && c <= colEnd + 1) { 38 | let currentHTMLNode = document.getElementById(node); 39 | if (currentHTMLNode.className !== "start" && currentHTMLNode.className !== "target" && currentHTMLNode.className !== "object") { 40 | board.wallsToAnimate.push(currentHTMLNode); 41 | board.nodes[node].status = "wall"; 42 | } 43 | } 44 | }); 45 | if (currentRow - 2 - rowStart > colEnd - colStart) { 46 | recursiveDivisionMaze(board, rowStart, currentRow - 2, colStart, colEnd, orientation, surroundingWalls); 47 | } else { 48 | recursiveDivisionMaze(board, rowStart, currentRow - 2, colStart, colEnd, "horizontal", surroundingWalls); 49 | } 50 | if (rowEnd - (currentRow + 2) > colEnd - colStart) { 51 | recursiveDivisionMaze(board, currentRow + 2, rowEnd, colStart, colEnd, orientation, surroundingWalls); 52 | } else { 53 | recursiveDivisionMaze(board, currentRow + 2, rowEnd, colStart, colEnd, "vertical", surroundingWalls); 54 | } 55 | } else { 56 | let possibleCols = []; 57 | for (let number = colStart; number <= colEnd; number += 2) { 58 | possibleCols.push(number); 59 | } 60 | let possibleRows = []; 61 | for (let number = rowStart - 1; number <= rowEnd + 1; number += 2) { 62 | possibleRows.push(number); 63 | } 64 | let randomColIndex = Math.floor(Math.random() * possibleCols.length); 65 | let randomRowIndex = Math.floor(Math.random() * possibleRows.length); 66 | let currentCol = possibleCols[randomColIndex]; 67 | let rowRandom = possibleRows[randomRowIndex]; 68 | Object.keys(board.nodes).forEach(node => { 69 | let r = parseInt(node.split("-")[0]); 70 | let c = parseInt(node.split("-")[1]); 71 | if (c === currentCol && r !== rowRandom && r >= rowStart - 1 && r <= rowEnd + 1) { 72 | let currentHTMLNode = document.getElementById(node); 73 | if (currentHTMLNode.className !== "start" && currentHTMLNode.className !== "target" && currentHTMLNode.className !== "object") { 74 | board.wallsToAnimate.push(currentHTMLNode); 75 | board.nodes[node].status = "wall"; 76 | } 77 | } 78 | }); 79 | if (rowEnd - rowStart > currentCol - 2 - colStart) { 80 | recursiveDivisionMaze(board, rowStart, rowEnd, colStart, currentCol - 2, "horizontal", surroundingWalls); 81 | } else { 82 | recursiveDivisionMaze(board, rowStart, rowEnd, colStart, currentCol - 2, "horizontal", surroundingWalls); 83 | } 84 | if (rowEnd - rowStart > colEnd - (currentCol + 2)) { 85 | recursiveDivisionMaze(board, rowStart, rowEnd, currentCol + 2, colEnd, "horizontal", surroundingWalls); 86 | } else { 87 | recursiveDivisionMaze(board, rowStart, rowEnd, currentCol + 2, colEnd, orientation, surroundingWalls); 88 | } 89 | } 90 | }; 91 | 92 | module.exports = recursiveDivisionMaze; 93 | -------------------------------------------------------------------------------- /public/browser/mazeAlgorithms/recursiveDivisionMaze.js: -------------------------------------------------------------------------------- 1 | function recursiveDivisionMaze(board, rowStart, rowEnd, colStart, colEnd, orientation, surroundingWalls, type) { 2 | if (rowEnd < rowStart || colEnd < colStart) { 3 | return; 4 | } 5 | if (!surroundingWalls) { 6 | let relevantIds = [board.start, board.target]; 7 | if (board.object) relevantIds.push(board.object); 8 | Object.keys(board.nodes).forEach(node => { 9 | if (!relevantIds.includes(node)) { 10 | let r = parseInt(node.split("-")[0]); 11 | let c = parseInt(node.split("-")[1]); 12 | if (r === 0 || c === 0 || r === board.height - 1 || c === board.width - 1) { 13 | let currentHTMLNode = document.getElementById(node); 14 | board.wallsToAnimate.push(currentHTMLNode); 15 | if (type === "wall") { 16 | board.nodes[node].status = "wall"; 17 | board.nodes[node].weight = 0; 18 | } else if (type === "weight") { 19 | board.nodes[node].status = "unvisited"; 20 | board.nodes[node].weight = 15; 21 | } 22 | } 23 | } 24 | }); 25 | surroundingWalls = true; 26 | } 27 | if (orientation === "horizontal") { 28 | let possibleRows = []; 29 | for (let number = rowStart; number <= rowEnd; number += 2) { 30 | possibleRows.push(number); 31 | } 32 | let possibleCols = []; 33 | for (let number = colStart - 1; number <= colEnd + 1; number += 2) { 34 | possibleCols.push(number); 35 | } 36 | let randomRowIndex = Math.floor(Math.random() * possibleRows.length); 37 | let randomColIndex = Math.floor(Math.random() * possibleCols.length); 38 | let currentRow = possibleRows[randomRowIndex]; 39 | let colRandom = possibleCols[randomColIndex]; 40 | Object.keys(board.nodes).forEach(node => { 41 | let r = parseInt(node.split("-")[0]); 42 | let c = parseInt(node.split("-")[1]); 43 | if (r === currentRow && c !== colRandom && c >= colStart - 1 && c <= colEnd + 1) { 44 | let currentHTMLNode = document.getElementById(node); 45 | if (currentHTMLNode.className !== "start" && currentHTMLNode.className !== "target" && currentHTMLNode.className !== "object") { 46 | board.wallsToAnimate.push(currentHTMLNode); 47 | if (type === "wall") { 48 | board.nodes[node].status = "wall"; 49 | board.nodes[node].weight = 0; 50 | } else if (type === "weight") { 51 | board.nodes[node].status = "unvisited"; 52 | board.nodes[node].weight = 15; 53 | } } 54 | } 55 | }); 56 | if (currentRow - 2 - rowStart > colEnd - colStart) { 57 | recursiveDivisionMaze(board, rowStart, currentRow - 2, colStart, colEnd, orientation, surroundingWalls, type); 58 | } else { 59 | recursiveDivisionMaze(board, rowStart, currentRow - 2, colStart, colEnd, "vertical", surroundingWalls, type); 60 | } 61 | if (rowEnd - (currentRow + 2) > colEnd - colStart) { 62 | recursiveDivisionMaze(board, currentRow + 2, rowEnd, colStart, colEnd, orientation, surroundingWalls, type); 63 | } else { 64 | recursiveDivisionMaze(board, currentRow + 2, rowEnd, colStart, colEnd, "vertical", surroundingWalls, type); 65 | } 66 | } else { 67 | let possibleCols = []; 68 | for (let number = colStart; number <= colEnd; number += 2) { 69 | possibleCols.push(number); 70 | } 71 | let possibleRows = []; 72 | for (let number = rowStart - 1; number <= rowEnd + 1; number += 2) { 73 | possibleRows.push(number); 74 | } 75 | let randomColIndex = Math.floor(Math.random() * possibleCols.length); 76 | let randomRowIndex = Math.floor(Math.random() * possibleRows.length); 77 | let currentCol = possibleCols[randomColIndex]; 78 | let rowRandom = possibleRows[randomRowIndex]; 79 | Object.keys(board.nodes).forEach(node => { 80 | let r = parseInt(node.split("-")[0]); 81 | let c = parseInt(node.split("-")[1]); 82 | if (c === currentCol && r !== rowRandom && r >= rowStart - 1 && r <= rowEnd + 1) { 83 | let currentHTMLNode = document.getElementById(node); 84 | if (currentHTMLNode.className !== "start" && currentHTMLNode.className !== "target" && currentHTMLNode.className !== "object") { 85 | board.wallsToAnimate.push(currentHTMLNode); 86 | if (type === "wall") { 87 | board.nodes[node].status = "wall"; 88 | board.nodes[node].weight = 0; 89 | } else if (type === "weight") { 90 | board.nodes[node].status = "unvisited"; 91 | board.nodes[node].weight = 15; 92 | } } 93 | } 94 | }); 95 | if (rowEnd - rowStart > currentCol - 2 - colStart) { 96 | recursiveDivisionMaze(board, rowStart, rowEnd, colStart, currentCol - 2, "horizontal", surroundingWalls, type); 97 | } else { 98 | recursiveDivisionMaze(board, rowStart, rowEnd, colStart, currentCol - 2, orientation, surroundingWalls, type); 99 | } 100 | if (rowEnd - rowStart > colEnd - (currentCol + 2)) { 101 | recursiveDivisionMaze(board, rowStart, rowEnd, currentCol + 2, colEnd, "horizontal", surroundingWalls, type); 102 | } else { 103 | recursiveDivisionMaze(board, rowStart, rowEnd, currentCol + 2, colEnd, orientation, surroundingWalls, type); 104 | } 105 | } 106 | }; 107 | 108 | module.exports = recursiveDivisionMaze; 109 | -------------------------------------------------------------------------------- /public/browser/mazeAlgorithms/simpleDemonstration.js: -------------------------------------------------------------------------------- 1 | function simpleDemonstration(board) { 2 | let currentIdY = board.width - 10; 3 | for (let counter = 0; counter < 7; counter++) { 4 | let currentIdXOne = Math.floor(board.height / 2) - counter; 5 | let currentIdXTwo = Math.floor(board.height / 2) + counter; 6 | let currentIdOne = `${currentIdXOne}-${currentIdY}`; 7 | let currentIdTwo = `${currentIdXTwo}-${currentIdY}`; 8 | let currentElementOne = document.getElementById(currentIdOne); 9 | let currentElementTwo = document.getElementById(currentIdTwo); 10 | board.wallsToAnimate.push(currentElementOne); 11 | board.wallsToAnimate.push(currentElementTwo); 12 | let currentNodeOne = board.nodes[currentIdOne]; 13 | let currentNodeTwo = board.nodes[currentIdTwo]; 14 | currentNodeOne.status = "wall"; 15 | currentNodeOne.weight = 0; 16 | currentNodeTwo.status = "wall"; 17 | currentNodeTwo.weight = 0; 18 | } 19 | } 20 | 21 | module.exports = simpleDemonstration; 22 | -------------------------------------------------------------------------------- /public/browser/mazeAlgorithms/stairDemonstration.js: -------------------------------------------------------------------------------- 1 | function stairDemonstration(board) { 2 | let currentIdX = board.height - 1; 3 | let currentIdY = 0; 4 | let relevantStatuses = ["start", "target", "object"]; 5 | while (currentIdX > 0 && currentIdY < board.width) { 6 | let currentId = `${currentIdX}-${currentIdY}`; 7 | let currentNode = board.nodes[currentId]; 8 | let currentHTMLNode = document.getElementById(currentId); 9 | if (!relevantStatuses.includes(currentNode.status)) { 10 | currentNode.status = "wall"; 11 | board.wallsToAnimate.push(currentHTMLNode); 12 | } 13 | currentIdX--; 14 | currentIdY++; 15 | } 16 | while (currentIdX < board.height - 2 && currentIdY < board.width) { 17 | let currentId = `${currentIdX}-${currentIdY}`; 18 | let currentNode = board.nodes[currentId]; 19 | let currentHTMLNode = document.getElementById(currentId); 20 | if (!relevantStatuses.includes(currentNode.status)) { 21 | currentNode.status = "wall"; 22 | board.wallsToAnimate.push(currentHTMLNode); 23 | } 24 | currentIdX++; 25 | currentIdY++; 26 | } 27 | while (currentIdX > 0 && currentIdY < board.width - 1) { 28 | let currentId = `${currentIdX}-${currentIdY}`; 29 | let currentNode = board.nodes[currentId]; 30 | let currentHTMLNode = document.getElementById(currentId); 31 | if (!relevantStatuses.includes(currentNode.status)) { 32 | currentNode.status = "wall"; 33 | board.wallsToAnimate.push(currentHTMLNode); 34 | } 35 | currentIdX--; 36 | currentIdY++; 37 | } 38 | } 39 | 40 | module.exports = stairDemonstration; 41 | -------------------------------------------------------------------------------- /public/browser/mazeAlgorithms/weightsDemonstration.js: -------------------------------------------------------------------------------- 1 | function weightsDemonstration(board) { 2 | let currentIdX = board.height - 1; 3 | let currentIdY = 35; 4 | while (currentIdX > 5) { 5 | let currentId = `${currentIdX}-${currentIdY}`; 6 | let currentElement = document.getElementById(currentId); 7 | board.wallsToAnimate.push(currentElement); 8 | let currentNode = board.nodes[currentId]; 9 | currentNode.status = "wall"; 10 | currentNode.weight = 0; 11 | currentIdX--; 12 | } 13 | for (let currentIdX = board.height - 2; currentIdX > board.height - 11; currentIdX--) { 14 | for (let currentIdY = 1; currentIdY < 35; currentIdY++) { 15 | let currentId = `${currentIdX}-${currentIdY}`; 16 | let currentElement = document.getElementById(currentId); 17 | board.wallsToAnimate.push(currentElement); 18 | let currentNode = board.nodes[currentId]; 19 | if (currentIdX === board.height - 2 && currentIdY < 35 && currentIdY > 26) { 20 | currentNode.status = "wall"; 21 | currentNode.weight = 0; 22 | } else { 23 | currentNode.status = "unvisited"; 24 | currentNode.weight = 15; 25 | } 26 | } 27 | } 28 | } 29 | 30 | module.exports = weightsDemonstration; 31 | -------------------------------------------------------------------------------- /public/browser/node.js: -------------------------------------------------------------------------------- 1 | function Node(id, status) { 2 | this.id = id; 3 | this.status = status; 4 | this.previousNode = null; 5 | this.path = null; 6 | this.direction = null; 7 | this.storedDirection = null; 8 | this.distance = Infinity; 9 | this.totalDistance = Infinity; 10 | this.heuristicDistance = null; 11 | this.weight = 0; 12 | this.relatesToObject = false; 13 | this.overwriteObjectRelation = false; 14 | 15 | this.otherid = id; 16 | this.otherstatus = status; 17 | this.otherpreviousNode = null; 18 | this.otherpath = null; 19 | this.otherdirection = null; 20 | this.otherstoredDirection = null; 21 | this.otherdistance = Infinity; 22 | this.otherweight = 0; 23 | this.otherrelatesToObject = false; 24 | this.otheroverwriteObjectRelation = false; 25 | } 26 | 27 | module.exports = Node; 28 | -------------------------------------------------------------------------------- /public/browser/pathfindingAlgorithms/astar.js: -------------------------------------------------------------------------------- 1 | function astar(nodes, start, target, nodesToAnimate, boardArray, name, heuristic) { 2 | if (!start || !target || start === target) { 3 | return false; 4 | } 5 | nodes[start].distance = 0; 6 | nodes[start].totalDistance = 0; 7 | nodes[start].direction = "up"; 8 | let unvisitedNodes = Object.keys(nodes); 9 | while (unvisitedNodes.length) { 10 | let currentNode = closestNode(nodes, unvisitedNodes); 11 | while (currentNode.status === "wall" && unvisitedNodes.length) { 12 | currentNode = closestNode(nodes, unvisitedNodes) 13 | } 14 | if (currentNode.distance === Infinity) return false; 15 | nodesToAnimate.push(currentNode); 16 | currentNode.status = "visited"; 17 | if (currentNode.id === target) { 18 | return "success!"; 19 | } 20 | updateNeighbors(nodes, currentNode, boardArray, target, name, start, heuristic); 21 | } 22 | } 23 | 24 | function closestNode(nodes, unvisitedNodes) { 25 | let currentClosest, index; 26 | for (let i = 0; i < unvisitedNodes.length; i++) { 27 | if (!currentClosest || currentClosest.totalDistance > nodes[unvisitedNodes[i]].totalDistance) { 28 | currentClosest = nodes[unvisitedNodes[i]]; 29 | index = i; 30 | } else if (currentClosest.totalDistance === nodes[unvisitedNodes[i]].totalDistance) { 31 | if (currentClosest.heuristicDistance > nodes[unvisitedNodes[i]].heuristicDistance) { 32 | currentClosest = nodes[unvisitedNodes[i]]; 33 | index = i; 34 | } 35 | } 36 | } 37 | unvisitedNodes.splice(index, 1); 38 | return currentClosest; 39 | } 40 | 41 | function updateNeighbors(nodes, node, boardArray, target, name, start, heuristic) { 42 | let neighbors = getNeighbors(node.id, nodes, boardArray); 43 | for (let neighbor of neighbors) { 44 | if (target) { 45 | updateNode(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic, boardArray); 46 | } else { 47 | updateNode(node, nodes[neighbor]); 48 | } 49 | } 50 | } 51 | 52 | function updateNode(currentNode, targetNode, actualTargetNode, name, nodes, actualStartNode, heuristic, boardArray) { 53 | let distance = getDistance(currentNode, targetNode); 54 | if (!targetNode.heuristicDistance) targetNode.heuristicDistance = manhattanDistance(targetNode, actualTargetNode); 55 | let distanceToCompare = currentNode.distance + targetNode.weight + distance[0]; 56 | if (distanceToCompare < targetNode.distance) { 57 | targetNode.distance = distanceToCompare; 58 | targetNode.totalDistance = targetNode.distance + targetNode.heuristicDistance; 59 | targetNode.previousNode = currentNode.id; 60 | targetNode.path = distance[1]; 61 | targetNode.direction = distance[2]; 62 | } 63 | } 64 | 65 | function getNeighbors(id, nodes, boardArray) { 66 | let coordinates = id.split("-"); 67 | let x = parseInt(coordinates[0]); 68 | let y = parseInt(coordinates[1]); 69 | let neighbors = []; 70 | let potentialNeighbor; 71 | if (boardArray[x - 1] && boardArray[x - 1][y]) { 72 | potentialNeighbor = `${(x - 1).toString()}-${y.toString()}` 73 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 74 | } 75 | if (boardArray[x + 1] && boardArray[x + 1][y]) { 76 | potentialNeighbor = `${(x + 1).toString()}-${y.toString()}` 77 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 78 | } 79 | if (boardArray[x][y - 1]) { 80 | potentialNeighbor = `${x.toString()}-${(y - 1).toString()}` 81 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 82 | } 83 | if (boardArray[x][y + 1]) { 84 | potentialNeighbor = `${x.toString()}-${(y + 1).toString()}` 85 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 86 | } 87 | // if (boardArray[x - 1] && boardArray[x - 1][y - 1]) { 88 | // potentialNeighbor = `${(x - 1).toString()}-${(y - 1).toString()}` 89 | // let potentialWallOne = `${(x - 1).toString()}-${y.toString()}` 90 | // let potentialWallTwo = `${x.toString()}-${(y - 1).toString()}` 91 | // if (nodes[potentialNeighbor].status !== "wall" && !(nodes[potentialWallOne].status === "wall" && nodes[potentialWallTwo].status === "wall")) neighbors.push(potentialNeighbor); 92 | // } 93 | // if (boardArray[x + 1] && boardArray[x + 1][y - 1]) { 94 | // potentialNeighbor = `${(x + 1).toString()}-${(y - 1).toString()}` 95 | // let potentialWallOne = `${(x + 1).toString()}-${y.toString()}` 96 | // let potentialWallTwo = `${x.toString()}-${(y - 1).toString()}` 97 | // if (nodes[potentialNeighbor].status !== "wall" && !(nodes[potentialWallOne].status === "wall" && nodes[potentialWallTwo].status === "wall")) neighbors.push(potentialNeighbor); 98 | // } 99 | // if (boardArray[x - 1] && boardArray[x - 1][y + 1]) { 100 | // potentialNeighbor = `${(x - 1).toString()}-${(y + 1).toString()}` 101 | // let potentialWallOne = `${(x - 1).toString()}-${y.toString()}` 102 | // let potentialWallTwo = `${x.toString()}-${(y + 1).toString()}` 103 | // if (nodes[potentialNeighbor].status !== "wall" && !(nodes[potentialWallOne].status === "wall" && nodes[potentialWallTwo].status === "wall")) neighbors.push(potentialNeighbor); 104 | // } 105 | // if (boardArray[x + 1] && boardArray[x + 1][y + 1]) { 106 | // potentialNeighbor = `${(x + 1).toString()}-${(y + 1).toString()}` 107 | // let potentialWallOne = `${(x + 1).toString()}-${y.toString()}` 108 | // let potentialWallTwo = `${x.toString()}-${(y + 1).toString()}` 109 | // if (nodes[potentialNeighbor].status !== "wall" && !(nodes[potentialWallOne].status === "wall" && nodes[potentialWallTwo].status === "wall")) neighbors.push(potentialNeighbor); 110 | // } 111 | return neighbors; 112 | } 113 | 114 | 115 | function getDistance(nodeOne, nodeTwo) { 116 | let currentCoordinates = nodeOne.id.split("-"); 117 | let targetCoordinates = nodeTwo.id.split("-"); 118 | let x1 = parseInt(currentCoordinates[0]); 119 | let y1 = parseInt(currentCoordinates[1]); 120 | let x2 = parseInt(targetCoordinates[0]); 121 | let y2 = parseInt(targetCoordinates[1]); 122 | if (x2 < x1 && y1 === y2) { 123 | if (nodeOne.direction === "up") { 124 | return [1, ["f"], "up"]; 125 | } else if (nodeOne.direction === "right") { 126 | return [2, ["l", "f"], "up"]; 127 | } else if (nodeOne.direction === "left") { 128 | return [2, ["r", "f"], "up"]; 129 | } else if (nodeOne.direction === "down") { 130 | return [3, ["r", "r", "f"], "up"]; 131 | } else if (nodeOne.direction === "up-right") { 132 | return [1.5, null, "up"]; 133 | } else if (nodeOne.direction === "down-right") { 134 | return [2.5, null, "up"]; 135 | } else if (nodeOne.direction === "up-left") { 136 | return [1.5, null, "up"]; 137 | } else if (nodeOne.direction === "down-left") { 138 | return [2.5, null, "up"]; 139 | } 140 | } else if (x2 > x1 && y1 === y2) { 141 | if (nodeOne.direction === "up") { 142 | return [3, ["r", "r", "f"], "down"]; 143 | } else if (nodeOne.direction === "right") { 144 | return [2, ["r", "f"], "down"]; 145 | } else if (nodeOne.direction === "left") { 146 | return [2, ["l", "f"], "down"]; 147 | } else if (nodeOne.direction === "down") { 148 | return [1, ["f"], "down"]; 149 | } else if (nodeOne.direction === "up-right") { 150 | return [2.5, null, "down"]; 151 | } else if (nodeOne.direction === "down-right") { 152 | return [1.5, null, "down"]; 153 | } else if (nodeOne.direction === "up-left") { 154 | return [2.5, null, "down"]; 155 | } else if (nodeOne.direction === "down-left") { 156 | return [1.5, null, "down"]; 157 | } 158 | } 159 | if (y2 < y1 && x1 === x2) { 160 | if (nodeOne.direction === "up") { 161 | return [2, ["l", "f"], "left"]; 162 | } else if (nodeOne.direction === "right") { 163 | return [3, ["l", "l", "f"], "left"]; 164 | } else if (nodeOne.direction === "left") { 165 | return [1, ["f"], "left"]; 166 | } else if (nodeOne.direction === "down") { 167 | return [2, ["r", "f"], "left"]; 168 | } else if (nodeOne.direction === "up-right") { 169 | return [2.5, null, "left"]; 170 | } else if (nodeOne.direction === "down-right") { 171 | return [2.5, null, "left"]; 172 | } else if (nodeOne.direction === "up-left") { 173 | return [1.5, null, "left"]; 174 | } else if (nodeOne.direction === "down-left") { 175 | return [1.5, null, "left"]; 176 | } 177 | } else if (y2 > y1 && x1 === x2) { 178 | if (nodeOne.direction === "up") { 179 | return [2, ["r", "f"], "right"]; 180 | } else if (nodeOne.direction === "right") { 181 | return [1, ["f"], "right"]; 182 | } else if (nodeOne.direction === "left") { 183 | return [3, ["r", "r", "f"], "right"]; 184 | } else if (nodeOne.direction === "down") { 185 | return [2, ["l", "f"], "right"]; 186 | } else if (nodeOne.direction === "up-right") { 187 | return [1.5, null, "right"]; 188 | } else if (nodeOne.direction === "down-right") { 189 | return [1.5, null, "right"]; 190 | } else if (nodeOne.direction === "up-left") { 191 | return [2.5, null, "right"]; 192 | } else if (nodeOne.direction === "down-left") { 193 | return [2.5, null, "right"]; 194 | } 195 | } /*else if (x2 < x1 && y2 < y1) { 196 | if (nodeOne.direction === "up") { 197 | return [1.5, ["f"], "up-left"]; 198 | } else if (nodeOne.direction === "right") { 199 | return [2.5, ["l", "f"], "up-left"]; 200 | } else if (nodeOne.direction === "left") { 201 | return [1.5, ["r", "f"], "up-left"]; 202 | } else if (nodeOne.direction === "down") { 203 | return [2.5, ["r", "r", "f"], "up-left"]; 204 | } else if (nodeOne.direction === "up-right") { 205 | return [2, null, "up-left"]; 206 | } else if (nodeOne.direction === "down-right") { 207 | return [3, null, "up-left"]; 208 | } else if (nodeOne.direction === "up-left") { 209 | return [1, null, "up-left"]; 210 | } else if (nodeOne.direction === "down-left") { 211 | return [2, null, "up-left"]; 212 | } 213 | } else if (x2 < x1 && y2 > y1) { 214 | if (nodeOne.direction === "up") { 215 | return [1.5, ["f"], "up-right"]; 216 | } else if (nodeOne.direction === "right") { 217 | return [1.5, ["l", "f"], "up-right"]; 218 | } else if (nodeOne.direction === "left") { 219 | return [2.5, ["r", "f"], "up-right"]; 220 | } else if (nodeOne.direction === "down") { 221 | return [2.5, ["r", "r", "f"], "up-right"]; 222 | } else if (nodeOne.direction === "up-right") { 223 | return [1, null, "up-right"]; 224 | } else if (nodeOne.direction === "down-right") { 225 | return [2, null, "up-right"]; 226 | } else if (nodeOne.direction === "up-left") { 227 | return [2, null, "up-right"]; 228 | } else if (nodeOne.direction === "down-left") { 229 | return [3, null, "up-right"]; 230 | } 231 | } else if (x2 > x1 && y2 > y1) { 232 | if (nodeOne.direction === "up") { 233 | return [2.5, ["f"], "down-right"]; 234 | } else if (nodeOne.direction === "right") { 235 | return [1.5, ["l", "f"], "down-right"]; 236 | } else if (nodeOne.direction === "left") { 237 | return [2.5, ["r", "f"], "down-right"]; 238 | } else if (nodeOne.direction === "down") { 239 | return [1.5, ["r", "r", "f"], "down-right"]; 240 | } else if (nodeOne.direction === "up-right") { 241 | return [2, null, "down-right"]; 242 | } else if (nodeOne.direction === "down-right") { 243 | return [1, null, "down-right"]; 244 | } else if (nodeOne.direction === "up-left") { 245 | return [3, null, "down-right"]; 246 | } else if (nodeOne.direction === "down-left") { 247 | return [2, null, "down-right"]; 248 | } 249 | } else if (x2 > x1 && y2 < y1) { 250 | if (nodeOne.direction === "up") { 251 | return [2.5, ["f"], "down-left"]; 252 | } else if (nodeOne.direction === "right") { 253 | return [2.5, ["l", "f"], "down-left"]; 254 | } else if (nodeOne.direction === "left") { 255 | return [1.5, ["r", "f"], "down-left"]; 256 | } else if (nodeOne.direction === "down") { 257 | return [1.5, ["r", "r", "f"], "down-left"]; 258 | } else if (nodeOne.direction === "up-right") { 259 | return [3, null, "down-left"]; 260 | } else if (nodeOne.direction === "down-right") { 261 | return [2, null, "down-left"]; 262 | } else if (nodeOne.direction === "up-left") { 263 | return [2, null, "down-left"]; 264 | } else if (nodeOne.direction === "down-left") { 265 | return [1, null, "down-left"]; 266 | } 267 | }*/ 268 | } 269 | 270 | function manhattanDistance(nodeOne, nodeTwo) { 271 | let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele)); 272 | let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele)); 273 | let xOne = nodeOneCoordinates[0]; 274 | let xTwo = nodeTwoCoordinates[0]; 275 | let yOne = nodeOneCoordinates[1]; 276 | let yTwo = nodeTwoCoordinates[1]; 277 | 278 | let xChange = Math.abs(xOne - xTwo); 279 | let yChange = Math.abs(yOne - yTwo); 280 | 281 | return (xChange + yChange); 282 | } 283 | 284 | 285 | 286 | module.exports = astar; 287 | -------------------------------------------------------------------------------- /public/browser/pathfindingAlgorithms/bidirectional.js: -------------------------------------------------------------------------------- 1 | const astar = require("./astar"); 2 | 3 | function bidirectional(nodes, start, target, nodesToAnimate, boardArray, name, heuristic, board) { 4 | if (name === "astar") return astar(nodes, start, target, nodesToAnimate, boardArray, name) 5 | if (!start || !target || start === target) { 6 | return false; 7 | } 8 | nodes[start].distance = 0; 9 | nodes[start].direction = "right"; 10 | nodes[target].otherdistance = 0; 11 | nodes[target].otherdirection = "left"; 12 | let visitedNodes = {}; 13 | let unvisitedNodesOne = Object.keys(nodes); 14 | let unvisitedNodesTwo = Object.keys(nodes); 15 | while (unvisitedNodesOne.length && unvisitedNodesTwo.length) { 16 | let currentNode = closestNode(nodes, unvisitedNodesOne); 17 | let secondCurrentNode = closestNodeTwo(nodes, unvisitedNodesTwo); 18 | while ((currentNode.status === "wall" || secondCurrentNode.status === "wall") && unvisitedNodesOne.length && unvisitedNodesTwo.length) { 19 | if (currentNode.status === "wall") currentNode = closestNode(nodes, unvisitedNodesOne); 20 | if (secondCurrentNode.status === "wall") secondCurrentNode = closestNodeTwo(nodes, unvisitedNodesTwo); 21 | } 22 | if (currentNode.distance === Infinity || secondCurrentNode.otherdistance === Infinity) { 23 | return false; 24 | } 25 | nodesToAnimate.push(currentNode); 26 | nodesToAnimate.push(secondCurrentNode); 27 | currentNode.status = "visited"; 28 | secondCurrentNode.status = "visited"; 29 | if (visitedNodes[currentNode.id]) { 30 | board.middleNode = currentNode.id; 31 | return "success"; 32 | } else if (visitedNodes[secondCurrentNode.id]) { 33 | board.middleNode = secondCurrentNode.id; 34 | return "success"; 35 | } else if (currentNode === secondCurrentNode) { 36 | board.middleNode = secondCurrentNode.id; 37 | return "success"; 38 | } 39 | visitedNodes[currentNode.id] = true; 40 | visitedNodes[secondCurrentNode.id] = true; 41 | updateNeighbors(nodes, currentNode, boardArray, target, name, start, heuristic); 42 | updateNeighborsTwo(nodes, secondCurrentNode, boardArray, start, name, target, heuristic); 43 | } 44 | } 45 | 46 | function closestNode(nodes, unvisitedNodes) { 47 | let currentClosest, index; 48 | for (let i = 0; i < unvisitedNodes.length; i++) { 49 | if (!currentClosest || currentClosest.distance > nodes[unvisitedNodes[i]].distance) { 50 | currentClosest = nodes[unvisitedNodes[i]]; 51 | index = i; 52 | } 53 | } 54 | unvisitedNodes.splice(index, 1); 55 | return currentClosest; 56 | } 57 | 58 | function closestNodeTwo(nodes, unvisitedNodes) { 59 | let currentClosest, index; 60 | for (let i = 0; i < unvisitedNodes.length; i++) { 61 | if (!currentClosest || currentClosest.otherdistance > nodes[unvisitedNodes[i]].otherdistance) { 62 | currentClosest = nodes[unvisitedNodes[i]]; 63 | index = i; 64 | } 65 | } 66 | unvisitedNodes.splice(index, 1); 67 | return currentClosest; 68 | } 69 | 70 | function updateNeighbors(nodes, node, boardArray, target, name, start, heuristic) { 71 | let neighbors = getNeighbors(node.id, nodes, boardArray); 72 | for (let neighbor of neighbors) { 73 | updateNode(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic, boardArray); 74 | } 75 | } 76 | 77 | function updateNeighborsTwo(nodes, node, boardArray, target, name, start, heuristic) { 78 | let neighbors = getNeighbors(node.id, nodes, boardArray); 79 | for (let neighbor of neighbors) { 80 | updateNodeTwo(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic, boardArray); 81 | } 82 | } 83 | 84 | function updateNode(currentNode, targetNode, actualTargetNode, name, nodes, actualStartNode, heuristic, boardArray) { 85 | let distance = getDistance(currentNode, targetNode); 86 | let weight = targetNode.weight === 15 ? 15 : 1; 87 | let distanceToCompare = currentNode.distance + (weight + distance[0]) * manhattanDistance(targetNode, actualTargetNode); 88 | if (distanceToCompare < targetNode.distance) { 89 | targetNode.distance = distanceToCompare; 90 | targetNode.previousNode = currentNode.id; 91 | targetNode.path = distance[1]; 92 | targetNode.direction = distance[2]; 93 | } 94 | } 95 | 96 | function updateNodeTwo(currentNode, targetNode, actualTargetNode, name, nodes, actualStartNode, heuristic, boardArray) { 97 | let distance = getDistanceTwo(currentNode, targetNode); 98 | let weight = targetNode.weight === 15 ? 15 : 1; 99 | let distanceToCompare = currentNode.otherdistance + (weight + distance[0]) * manhattanDistance(targetNode, actualTargetNode); 100 | if (distanceToCompare < targetNode.otherdistance) { 101 | targetNode.otherdistance = distanceToCompare; 102 | targetNode.otherpreviousNode = currentNode.id; 103 | targetNode.path = distance[1]; 104 | targetNode.otherdirection = distance[2]; 105 | } 106 | } 107 | 108 | function getNeighbors(id, nodes, boardArray) { 109 | let coordinates = id.split("-"); 110 | let x = parseInt(coordinates[0]); 111 | let y = parseInt(coordinates[1]); 112 | let neighbors = []; 113 | let potentialNeighbor; 114 | if (boardArray[x - 1] && boardArray[x - 1][y]) { 115 | potentialNeighbor = `${(x - 1).toString()}-${y.toString()}` 116 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 117 | } 118 | if (boardArray[x + 1] && boardArray[x + 1][y]) { 119 | potentialNeighbor = `${(x + 1).toString()}-${y.toString()}` 120 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 121 | } 122 | if (boardArray[x][y - 1]) { 123 | potentialNeighbor = `${x.toString()}-${(y - 1).toString()}` 124 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 125 | } 126 | if (boardArray[x][y + 1]) { 127 | potentialNeighbor = `${x.toString()}-${(y + 1).toString()}` 128 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 129 | } 130 | return neighbors; 131 | } 132 | 133 | function getDistance(nodeOne, nodeTwo) { 134 | let currentCoordinates = nodeOne.id.split("-"); 135 | let targetCoordinates = nodeTwo.id.split("-"); 136 | let x1 = parseInt(currentCoordinates[0]); 137 | let y1 = parseInt(currentCoordinates[1]); 138 | let x2 = parseInt(targetCoordinates[0]); 139 | let y2 = parseInt(targetCoordinates[1]); 140 | if (x2 < x1) { 141 | if (nodeOne.direction === "up") { 142 | return [1, ["f"], "up"]; 143 | } else if (nodeOne.direction === "right") { 144 | return [2, ["l", "f"], "up"]; 145 | } else if (nodeOne.direction === "left") { 146 | return [2, ["r", "f"], "up"]; 147 | } else if (nodeOne.direction === "down") { 148 | return [3, ["r", "r", "f"], "up"]; 149 | } 150 | } else if (x2 > x1) { 151 | if (nodeOne.direction === "up") { 152 | return [3, ["r", "r", "f"], "down"]; 153 | } else if (nodeOne.direction === "right") { 154 | return [2, ["r", "f"], "down"]; 155 | } else if (nodeOne.direction === "left") { 156 | return [2, ["l", "f"], "down"]; 157 | } else if (nodeOne.direction === "down") { 158 | return [1, ["f"], "down"]; 159 | } 160 | } 161 | if (y2 < y1) { 162 | if (nodeOne.direction === "up") { 163 | return [2, ["l", "f"], "left"]; 164 | } else if (nodeOne.direction === "right") { 165 | return [3, ["l", "l", "f"], "left"]; 166 | } else if (nodeOne.direction === "left") { 167 | return [1, ["f"], "left"]; 168 | } else if (nodeOne.direction === "down") { 169 | return [2, ["r", "f"], "left"]; 170 | } 171 | } else if (y2 > y1) { 172 | if (nodeOne.direction === "up") { 173 | return [2, ["r", "f"], "right"]; 174 | } else if (nodeOne.direction === "right") { 175 | return [1, ["f"], "right"]; 176 | } else if (nodeOne.direction === "left") { 177 | return [3, ["r", "r", "f"], "right"]; 178 | } else if (nodeOne.direction === "down") { 179 | return [2, ["l", "f"], "right"]; 180 | } 181 | } 182 | } 183 | 184 | function getDistanceTwo(nodeOne, nodeTwo) { 185 | let currentCoordinates = nodeOne.id.split("-"); 186 | let targetCoordinates = nodeTwo.id.split("-"); 187 | let x1 = parseInt(currentCoordinates[0]); 188 | let y1 = parseInt(currentCoordinates[1]); 189 | let x2 = parseInt(targetCoordinates[0]); 190 | let y2 = parseInt(targetCoordinates[1]); 191 | if (x2 < x1) { 192 | if (nodeOne.otherdirection === "up") { 193 | return [1, ["f"], "up"]; 194 | } else if (nodeOne.otherdirection === "right") { 195 | return [2, ["l", "f"], "up"]; 196 | } else if (nodeOne.otherdirection === "left") { 197 | return [2, ["r", "f"], "up"]; 198 | } else if (nodeOne.otherdirection === "down") { 199 | return [3, ["r", "r", "f"], "up"]; 200 | } 201 | } else if (x2 > x1) { 202 | if (nodeOne.otherdirection === "up") { 203 | return [3, ["r", "r", "f"], "down"]; 204 | } else if (nodeOne.otherdirection === "right") { 205 | return [2, ["r", "f"], "down"]; 206 | } else if (nodeOne.otherdirection === "left") { 207 | return [2, ["l", "f"], "down"]; 208 | } else if (nodeOne.otherdirection === "down") { 209 | return [1, ["f"], "down"]; 210 | } 211 | } 212 | if (y2 < y1) { 213 | if (nodeOne.otherdirection === "up") { 214 | return [2, ["l", "f"], "left"]; 215 | } else if (nodeOne.otherdirection === "right") { 216 | return [3, ["l", "l", "f"], "left"]; 217 | } else if (nodeOne.otherdirection === "left") { 218 | return [1, ["f"], "left"]; 219 | } else if (nodeOne.otherdirection === "down") { 220 | return [2, ["r", "f"], "left"]; 221 | } 222 | } else if (y2 > y1) { 223 | if (nodeOne.otherdirection === "up") { 224 | return [2, ["r", "f"], "right"]; 225 | } else if (nodeOne.otherdirection === "right") { 226 | return [1, ["f"], "right"]; 227 | } else if (nodeOne.otherdirection === "left") { 228 | return [3, ["r", "r", "f"], "right"]; 229 | } else if (nodeOne.otherdirection === "down") { 230 | return [2, ["l", "f"], "right"]; 231 | } 232 | } 233 | } 234 | 235 | function manhattanDistance(nodeOne, nodeTwo) { 236 | let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele)); 237 | let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele)); 238 | let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]); 239 | let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]); 240 | return (xChange + yChange); 241 | } 242 | 243 | function weightedManhattanDistance(nodeOne, nodeTwo, nodes) { 244 | let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele)); 245 | let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele)); 246 | let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]); 247 | let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]); 248 | 249 | if (nodeOneCoordinates[0] < nodeTwoCoordinates[0] && nodeOneCoordinates[1] < nodeTwoCoordinates[1]) { 250 | 251 | let additionalxChange = 0, 252 | additionalyChange = 0; 253 | for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++) { 254 | let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`; 255 | let currentNode = nodes[currentId]; 256 | additionalxChange += currentNode.weight; 257 | } 258 | for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++) { 259 | let currentId = `${nodeTwoCoordinates[0]}-${currenty}`; 260 | let currentNode = nodes[currentId]; 261 | additionalyChange += currentNode.weight; 262 | } 263 | 264 | let otherAdditionalxChange = 0, 265 | otherAdditionalyChange = 0; 266 | for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++) { 267 | let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`; 268 | let currentNode = nodes[currentId]; 269 | additionalyChange += currentNode.weight; 270 | } 271 | for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++) { 272 | let currentId = `${currentx}-${nodeTwoCoordinates[1]}`; 273 | let currentNode = nodes[currentId]; 274 | additionalxChange += currentNode.weight; 275 | } 276 | 277 | if (additionalxChange + additionalyChange < otherAdditionalxChange + otherAdditionalyChange) { 278 | xChange += additionalxChange; 279 | yChange += additionalyChange; 280 | } else { 281 | xChange += otherAdditionalxChange; 282 | yChange += otherAdditionalyChange; 283 | } 284 | } else if (nodeOneCoordinates[0] < nodeTwoCoordinates[0] && nodeOneCoordinates[1] >= nodeTwoCoordinates[1]) { 285 | let additionalxChange = 0, 286 | additionalyChange = 0; 287 | for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++) { 288 | let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`; 289 | let currentNode = nodes[currentId]; 290 | additionalxChange += currentNode.weight; 291 | } 292 | for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--) { 293 | let currentId = `${nodeTwoCoordinates[0]}-${currenty}`; 294 | let currentNode = nodes[currentId]; 295 | additionalyChange += currentNode.weight; 296 | } 297 | 298 | let otherAdditionalxChange = 0, 299 | otherAdditionalyChange = 0; 300 | for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--) { 301 | let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`; 302 | let currentNode = nodes[currentId]; 303 | additionalyChange += currentNode.weight; 304 | } 305 | for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++) { 306 | let currentId = `${currentx}-${nodeTwoCoordinates[1]}`; 307 | let currentNode = nodes[currentId]; 308 | additionalxChange += currentNode.weight; 309 | } 310 | 311 | if (additionalxChange + additionalyChange < otherAdditionalxChange + otherAdditionalyChange) { 312 | xChange += additionalxChange; 313 | yChange += additionalyChange; 314 | } else { 315 | xChange += otherAdditionalxChange; 316 | yChange += otherAdditionalyChange; 317 | } 318 | } else if (nodeOneCoordinates[0] >= nodeTwoCoordinates[0] && nodeOneCoordinates[1] < nodeTwoCoordinates[1]) { 319 | let additionalxChange = 0, 320 | additionalyChange = 0; 321 | for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--) { 322 | let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`; 323 | let currentNode = nodes[currentId]; 324 | additionalxChange += currentNode.weight; 325 | } 326 | for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++) { 327 | let currentId = `${nodeTwoCoordinates[0]}-${currenty}`; 328 | let currentNode = nodes[currentId]; 329 | additionalyChange += currentNode.weight; 330 | } 331 | 332 | let otherAdditionalxChange = 0, 333 | otherAdditionalyChange = 0; 334 | for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++) { 335 | let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`; 336 | let currentNode = nodes[currentId]; 337 | additionalyChange += currentNode.weight; 338 | } 339 | for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--) { 340 | let currentId = `${currentx}-${nodeTwoCoordinates[1]}`; 341 | let currentNode = nodes[currentId]; 342 | additionalxChange += currentNode.weight; 343 | } 344 | 345 | if (additionalxChange + additionalyChange < otherAdditionalxChange + otherAdditionalyChange) { 346 | xChange += additionalxChange; 347 | yChange += additionalyChange; 348 | } else { 349 | xChange += otherAdditionalxChange; 350 | yChange += otherAdditionalyChange; 351 | } 352 | } else if (nodeOneCoordinates[0] >= nodeTwoCoordinates[0] && nodeOneCoordinates[1] >= nodeTwoCoordinates[1]) { 353 | let additionalxChange = 0, 354 | additionalyChange = 0; 355 | for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--) { 356 | let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`; 357 | let currentNode = nodes[currentId]; 358 | additionalxChange += currentNode.weight; 359 | } 360 | for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--) { 361 | let currentId = `${nodeTwoCoordinates[0]}-${currenty}`; 362 | let currentNode = nodes[currentId]; 363 | additionalyChange += currentNode.weight; 364 | } 365 | 366 | let otherAdditionalxChange = 0, 367 | otherAdditionalyChange = 0; 368 | for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--) { 369 | let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`; 370 | let currentNode = nodes[currentId]; 371 | additionalyChange += currentNode.weight; 372 | } 373 | for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--) { 374 | let currentId = `${currentx}-${nodeTwoCoordinates[1]}`; 375 | let currentNode = nodes[currentId]; 376 | additionalxChange += currentNode.weight; 377 | } 378 | 379 | if (additionalxChange + additionalyChange < otherAdditionalxChange + otherAdditionalyChange) { 380 | xChange += additionalxChange; 381 | yChange += additionalyChange; 382 | } else { 383 | xChange += otherAdditionalxChange; 384 | yChange += otherAdditionalyChange; 385 | } 386 | } 387 | 388 | 389 | return xChange + yChange; 390 | 391 | 392 | } 393 | 394 | module.exports = bidirectional; 395 | -------------------------------------------------------------------------------- /public/browser/pathfindingAlgorithms/testAlgorithm.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function test(nodes, start, target, nodesToAnimate, boardArray, name, heuristic) { 4 | if (!start || !target || start === target) { 5 | return false; 6 | } 7 | nodes[start].distance = 0; 8 | nodes[start].direction = "up"; 9 | let unvisitedNodes = Object.keys(nodes); 10 | while (unvisitedNodes.length) { 11 | let currentNode = closestNode(nodes, unvisitedNodes); 12 | while (currentNode.status === "wall" && unvisitedNodes.length) { 13 | currentNode = closestNode(nodes, unvisitedNodes) 14 | } 15 | if (currentNode.distance === Infinity) return false; 16 | currentNode.status = "visited"; 17 | if (currentNode.id === target) { 18 | while (currentNode.id !== start) { 19 | nodesToAnimate.unshift(currentNode); 20 | currentNode = nodes[currentNode.previousNode]; 21 | } 22 | return "success!"; 23 | } 24 | if (name === "astar" || name === "greedy") { 25 | updateNeighbors(nodes, currentNode, boardArray, target, name, start, heuristic); 26 | } else if (name === "dijkstra") { 27 | updateNeighbors(nodes, currentNode, boardArray); 28 | } 29 | } 30 | } 31 | 32 | function closestNode(nodes, unvisitedNodes) { 33 | let currentClosest, index; 34 | for (let i = 0; i < unvisitedNodes.length; i++) { 35 | if (!currentClosest || currentClosest.distance > nodes[unvisitedNodes[i]].distance) { 36 | currentClosest = nodes[unvisitedNodes[i]]; 37 | index = i; 38 | } 39 | } 40 | unvisitedNodes.splice(index, 1); 41 | return currentClosest; 42 | } 43 | 44 | function updateNeighbors(nodes, node, boardArray, target, name, start, heuristic) { 45 | let neighbors = getNeighbors(node.id, nodes, boardArray); 46 | for (let neighbor of neighbors) { 47 | if (target) { 48 | updateNode(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic, boardArray); 49 | } else { 50 | updateNode(node, nodes[neighbor]); 51 | } 52 | } 53 | } 54 | 55 | function averageNumberOfNodesBetween(currentNode) { 56 | let num = 0; 57 | while (currentNode.previousNode) { 58 | num++; 59 | currentNode = currentNode.previousNode; 60 | } 61 | return num; 62 | } 63 | 64 | 65 | function updateNode(currentNode, targetNode, actualTargetNode, name, nodes, actualStartNode, heuristic, boardArray) { 66 | let distance = getDistance(currentNode, targetNode); 67 | let distanceToCompare; 68 | if (actualTargetNode && name === "astar") { 69 | if (heuristic === "manhattanDistance") { 70 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0] + manhattanDistance(targetNode, actualTargetNode); 71 | } else if (heuristic === "poweredManhattanDistance") { 72 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0] + Math.pow(manhattanDistance(targetNode, actualTargetNode), 3); 73 | } else if (heuristic === "extraPoweredManhattanDistance") { 74 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0] + Math.pow(manhattanDistance(targetNode, actualTargetNode), 5); 75 | } 76 | let startNodeManhattanDistance = manhattanDistance(actualStartNode, actualTargetNode); 77 | } else if (actualTargetNode && name === "greedy") { 78 | distanceToCompare = targetNode.weight + distance[0] + manhattanDistance(targetNode, actualTargetNode); 79 | } else { 80 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0]; 81 | } 82 | if (distanceToCompare < targetNode.distance) { 83 | targetNode.distance = distanceToCompare; 84 | targetNode.previousNode = currentNode.id; 85 | targetNode.path = distance[1]; 86 | targetNode.direction = distance[2]; 87 | } 88 | } 89 | 90 | function getNeighbors(id, nodes, boardArray) { 91 | let coordinates = id.split("-"); 92 | let x = parseInt(coordinates[0]); 93 | let y = parseInt(coordinates[1]); 94 | let neighbors = []; 95 | let potentialNeighbor; 96 | if (boardArray[x - 1] && boardArray[x - 1][y]) { 97 | potentialNeighbor = `${(x - 1).toString()}-${y.toString()}` 98 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 99 | } 100 | if (boardArray[x + 1] && boardArray[x + 1][y]) { 101 | potentialNeighbor = `${(x + 1).toString()}-${y.toString()}` 102 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 103 | } 104 | if (boardArray[x][y - 1]) { 105 | potentialNeighbor = `${x.toString()}-${(y - 1).toString()}` 106 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 107 | } 108 | if (boardArray[x][y + 1]) { 109 | potentialNeighbor = `${x.toString()}-${(y + 1).toString()}` 110 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 111 | } 112 | return neighbors; 113 | } 114 | 115 | 116 | function getDistance(nodeOne, nodeTwo) { 117 | let currentCoordinates = nodeOne.id.split("-"); 118 | let targetCoordinates = nodeTwo.id.split("-"); 119 | let x1 = parseInt(currentCoordinates[0]); 120 | let y1 = parseInt(currentCoordinates[1]); 121 | let x2 = parseInt(targetCoordinates[0]); 122 | let y2 = parseInt(targetCoordinates[1]); 123 | if (x2 < x1) { 124 | if (nodeOne.direction === "up") { 125 | return [1, ["f"], "up"]; 126 | } else if (nodeOne.direction === "right") { 127 | return [2, ["l", "f"], "up"]; 128 | } else if (nodeOne.direction === "left") { 129 | return [2, ["r", "f"], "up"]; 130 | } else if (nodeOne.direction === "down") { 131 | return [3, ["r", "r", "f"], "up"]; 132 | } 133 | } else if (x2 > x1) { 134 | if (nodeOne.direction === "up") { 135 | return [3, ["r", "r", "f"], "down"]; 136 | } else if (nodeOne.direction === "right") { 137 | return [2, ["r", "f"], "down"]; 138 | } else if (nodeOne.direction === "left") { 139 | return [2, ["l", "f"], "down"]; 140 | } else if (nodeOne.direction === "down") { 141 | return [1, ["f"], "down"]; 142 | } 143 | } 144 | if (y2 < y1) { 145 | if (nodeOne.direction === "up") { 146 | return [2, ["l", "f"], "left"]; 147 | } else if (nodeOne.direction === "right") { 148 | return [3, ["l", "l", "f"], "left"]; 149 | } else if (nodeOne.direction === "left") { 150 | return [1, ["f"], "left"]; 151 | } else if (nodeOne.direction === "down") { 152 | return [2, ["r", "f"], "left"]; 153 | } 154 | } else if (y2 > y1) { 155 | if (nodeOne.direction === "up") { 156 | return [2, ["r", "f"], "right"]; 157 | } else if (nodeOne.direction === "right") { 158 | return [1, ["f"], "right"]; 159 | } else if (nodeOne.direction === "left") { 160 | return [3, ["r", "r", "f"], "right"]; 161 | } else if (nodeOne.direction === "down") { 162 | return [2, ["l", "f"], "right"]; 163 | } 164 | } 165 | } 166 | 167 | function manhattanDistance(nodeOne, nodeTwo) { 168 | let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele)); 169 | let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele)); 170 | let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]); 171 | let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]); 172 | return (xChange + yChange); 173 | } 174 | 175 | module.exports = test; 176 | -------------------------------------------------------------------------------- /public/browser/pathfindingAlgorithms/unweightedSearchAlgorithm.js: -------------------------------------------------------------------------------- 1 | function unweightedSearchAlgorithm(nodes, start, target, nodesToAnimate, boardArray, name) { 2 | if (!start || !target || start === target) { 3 | return false; 4 | } 5 | let structure = [nodes[start]]; 6 | let exploredNodes = {start: true}; 7 | while (structure.length) { 8 | let currentNode = name === "bfs" ? structure.shift() : structure.pop(); 9 | nodesToAnimate.push(currentNode); 10 | if (name === "dfs") exploredNodes[currentNode.id] = true; 11 | currentNode.status = "visited"; 12 | if (currentNode.id === target) { 13 | return "success"; 14 | } 15 | let currentNeighbors = getNeighbors(currentNode.id, nodes, boardArray, name); 16 | currentNeighbors.forEach(neighbor => { 17 | if (!exploredNodes[neighbor]) { 18 | if (name === "bfs") exploredNodes[neighbor] = true; 19 | nodes[neighbor].previousNode = currentNode.id; 20 | structure.push(nodes[neighbor]); 21 | } 22 | }); 23 | } 24 | return false; 25 | } 26 | 27 | function getNeighbors(id, nodes, boardArray, name) { 28 | let coordinates = id.split("-"); 29 | let x = parseInt(coordinates[0]); 30 | let y = parseInt(coordinates[1]); 31 | let neighbors = []; 32 | let potentialNeighbor; 33 | if (boardArray[x - 1] && boardArray[x - 1][y]) { 34 | potentialNeighbor = `${(x - 1).toString()}-${y.toString()}` 35 | if (nodes[potentialNeighbor].status !== "wall") { 36 | if (name === "bfs") { 37 | neighbors.push(potentialNeighbor); 38 | } else { 39 | neighbors.unshift(potentialNeighbor); 40 | } 41 | } 42 | } 43 | if (boardArray[x][y + 1]) { 44 | potentialNeighbor = `${x.toString()}-${(y + 1).toString()}` 45 | if (nodes[potentialNeighbor].status !== "wall") { 46 | if (name === "bfs") { 47 | neighbors.push(potentialNeighbor); 48 | } else { 49 | neighbors.unshift(potentialNeighbor); 50 | } 51 | } 52 | } 53 | if (boardArray[x + 1] && boardArray[x + 1][y]) { 54 | potentialNeighbor = `${(x + 1).toString()}-${y.toString()}` 55 | if (nodes[potentialNeighbor].status !== "wall") { 56 | if (name === "bfs") { 57 | neighbors.push(potentialNeighbor); 58 | } else { 59 | neighbors.unshift(potentialNeighbor); 60 | } 61 | } 62 | } 63 | if (boardArray[x][y - 1]) { 64 | potentialNeighbor = `${x.toString()}-${(y - 1).toString()}` 65 | if (nodes[potentialNeighbor].status !== "wall") { 66 | if (name === "bfs") { 67 | neighbors.push(potentialNeighbor); 68 | } else { 69 | neighbors.unshift(potentialNeighbor); 70 | } 71 | } 72 | } 73 | return neighbors; 74 | } 75 | 76 | module.exports = unweightedSearchAlgorithm; 77 | -------------------------------------------------------------------------------- /public/browser/pathfindingAlgorithms/weightedSearchAlgorithm.js: -------------------------------------------------------------------------------- 1 | const astar = require("./astar"); 2 | 3 | function weightedSearchAlgorithm(nodes, start, target, nodesToAnimate, boardArray, name, heuristic) { 4 | if (name === "astar") return astar(nodes, start, target, nodesToAnimate, boardArray, name) 5 | if (!start || !target || start === target) { 6 | return false; 7 | } 8 | nodes[start].distance = 0; 9 | nodes[start].direction = "right"; 10 | let unvisitedNodes = Object.keys(nodes); 11 | while (unvisitedNodes.length) { 12 | let currentNode = closestNode(nodes, unvisitedNodes); 13 | while (currentNode.status === "wall" && unvisitedNodes.length) { 14 | currentNode = closestNode(nodes, unvisitedNodes) 15 | } 16 | if (currentNode.distance === Infinity) { 17 | return false; 18 | } 19 | nodesToAnimate.push(currentNode); 20 | currentNode.status = "visited"; 21 | if (currentNode.id === target) return "success!"; 22 | if (name === "CLA" || name === "greedy") { 23 | updateNeighbors(nodes, currentNode, boardArray, target, name, start, heuristic); 24 | } else if (name === "dijkstra") { 25 | updateNeighbors(nodes, currentNode, boardArray); 26 | } 27 | } 28 | } 29 | 30 | function closestNode(nodes, unvisitedNodes) { 31 | let currentClosest, index; 32 | for (let i = 0; i < unvisitedNodes.length; i++) { 33 | if (!currentClosest || currentClosest.distance > nodes[unvisitedNodes[i]].distance) { 34 | currentClosest = nodes[unvisitedNodes[i]]; 35 | index = i; 36 | } 37 | } 38 | unvisitedNodes.splice(index, 1); 39 | return currentClosest; 40 | } 41 | 42 | function updateNeighbors(nodes, node, boardArray, target, name, start, heuristic) { 43 | let neighbors = getNeighbors(node.id, nodes, boardArray); 44 | for (let neighbor of neighbors) { 45 | if (target) { 46 | updateNode(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic, boardArray); 47 | } else { 48 | updateNode(node, nodes[neighbor]); 49 | } 50 | } 51 | } 52 | 53 | function averageNumberOfNodesBetween(currentNode) { 54 | let num = 0; 55 | while (currentNode.previousNode) { 56 | num++; 57 | currentNode = currentNode.previousNode; 58 | } 59 | return num; 60 | } 61 | 62 | 63 | function updateNode(currentNode, targetNode, actualTargetNode, name, nodes, actualStartNode, heuristic, boardArray) { 64 | let distance = getDistance(currentNode, targetNode); 65 | let distanceToCompare; 66 | if (actualTargetNode && name === "CLA") { 67 | let weight = targetNode.weight === 15 ? 15 : 1; 68 | if (heuristic === "manhattanDistance") { 69 | distanceToCompare = currentNode.distance + (distance[0] + weight) * manhattanDistance(targetNode, actualTargetNode); 70 | } else if (heuristic === "poweredManhattanDistance") { 71 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0] + Math.pow(manhattanDistance(targetNode, actualTargetNode), 2); 72 | } else if (heuristic === "extraPoweredManhattanDistance") { 73 | distanceToCompare = currentNode.distance + (distance[0] + weight) * Math.pow(manhattanDistance(targetNode, actualTargetNode), 7); 74 | } 75 | let startNodeManhattanDistance = manhattanDistance(actualStartNode, actualTargetNode); 76 | } else if (actualTargetNode && name === "greedy") { 77 | distanceToCompare = targetNode.weight + distance[0] + manhattanDistance(targetNode, actualTargetNode); 78 | } else { 79 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0]; 80 | } 81 | if (distanceToCompare < targetNode.distance) { 82 | targetNode.distance = distanceToCompare; 83 | targetNode.previousNode = currentNode.id; 84 | targetNode.path = distance[1]; 85 | targetNode.direction = distance[2]; 86 | } 87 | } 88 | 89 | function getNeighbors(id, nodes, boardArray) { 90 | let coordinates = id.split("-"); 91 | let x = parseInt(coordinates[0]); 92 | let y = parseInt(coordinates[1]); 93 | let neighbors = []; 94 | let potentialNeighbor; 95 | if (boardArray[x - 1] && boardArray[x - 1][y]) { 96 | potentialNeighbor = `${(x - 1).toString()}-${y.toString()}` 97 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 98 | } 99 | if (boardArray[x + 1] && boardArray[x + 1][y]) { 100 | potentialNeighbor = `${(x + 1).toString()}-${y.toString()}` 101 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 102 | } 103 | if (boardArray[x][y - 1]) { 104 | potentialNeighbor = `${x.toString()}-${(y - 1).toString()}` 105 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 106 | } 107 | if (boardArray[x][y + 1]) { 108 | potentialNeighbor = `${x.toString()}-${(y + 1).toString()}` 109 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor); 110 | } 111 | return neighbors; 112 | } 113 | 114 | 115 | function getDistance(nodeOne, nodeTwo) { 116 | let currentCoordinates = nodeOne.id.split("-"); 117 | let targetCoordinates = nodeTwo.id.split("-"); 118 | let x1 = parseInt(currentCoordinates[0]); 119 | let y1 = parseInt(currentCoordinates[1]); 120 | let x2 = parseInt(targetCoordinates[0]); 121 | let y2 = parseInt(targetCoordinates[1]); 122 | if (x2 < x1) { 123 | if (nodeOne.direction === "up") { 124 | return [1, ["f"], "up"]; 125 | } else if (nodeOne.direction === "right") { 126 | return [2, ["l", "f"], "up"]; 127 | } else if (nodeOne.direction === "left") { 128 | return [2, ["r", "f"], "up"]; 129 | } else if (nodeOne.direction === "down") { 130 | return [3, ["r", "r", "f"], "up"]; 131 | } 132 | } else if (x2 > x1) { 133 | if (nodeOne.direction === "up") { 134 | return [3, ["r", "r", "f"], "down"]; 135 | } else if (nodeOne.direction === "right") { 136 | return [2, ["r", "f"], "down"]; 137 | } else if (nodeOne.direction === "left") { 138 | return [2, ["l", "f"], "down"]; 139 | } else if (nodeOne.direction === "down") { 140 | return [1, ["f"], "down"]; 141 | } 142 | } 143 | if (y2 < y1) { 144 | if (nodeOne.direction === "up") { 145 | return [2, ["l", "f"], "left"]; 146 | } else if (nodeOne.direction === "right") { 147 | return [3, ["l", "l", "f"], "left"]; 148 | } else if (nodeOne.direction === "left") { 149 | return [1, ["f"], "left"]; 150 | } else if (nodeOne.direction === "down") { 151 | return [2, ["r", "f"], "left"]; 152 | } 153 | } else if (y2 > y1) { 154 | if (nodeOne.direction === "up") { 155 | return [2, ["r", "f"], "right"]; 156 | } else if (nodeOne.direction === "right") { 157 | return [1, ["f"], "right"]; 158 | } else if (nodeOne.direction === "left") { 159 | return [3, ["r", "r", "f"], "right"]; 160 | } else if (nodeOne.direction === "down") { 161 | return [2, ["l", "f"], "right"]; 162 | } 163 | } 164 | } 165 | 166 | function manhattanDistance(nodeOne, nodeTwo) { 167 | let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele)); 168 | let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele)); 169 | let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]); 170 | let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]); 171 | return (xChange + yChange); 172 | } 173 | 174 | function weightedManhattanDistance(nodeOne, nodeTwo, nodes) { 175 | let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele)); 176 | let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele)); 177 | let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]); 178 | let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]); 179 | 180 | if (nodeOneCoordinates[0] < nodeTwoCoordinates[0] && nodeOneCoordinates[1] < nodeTwoCoordinates[1]) { 181 | let additionalxChange = 0, 182 | additionalyChange = 0; 183 | for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++) { 184 | let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`; 185 | let currentNode = nodes[currentId]; 186 | additionalxChange += currentNode.weight; 187 | } 188 | for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++) { 189 | let currentId = `${nodeTwoCoordinates[0]}-${currenty}`; 190 | let currentNode = nodes[currentId]; 191 | additionalyChange += currentNode.weight; 192 | } 193 | 194 | let otherAdditionalxChange = 0, 195 | otherAdditionalyChange = 0; 196 | for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++) { 197 | let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`; 198 | let currentNode = nodes[currentId]; 199 | additionalyChange += currentNode.weight; 200 | } 201 | for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++) { 202 | let currentId = `${currentx}-${nodeTwoCoordinates[1]}`; 203 | let currentNode = nodes[currentId]; 204 | additionalxChange += currentNode.weight; 205 | } 206 | 207 | if (additionalxChange + additionalyChange < otherAdditionalxChange + otherAdditionalyChange) { 208 | xChange += additionalxChange; 209 | yChange += additionalyChange; 210 | } else { 211 | xChange += otherAdditionalxChange; 212 | yChange += otherAdditionalyChange; 213 | } 214 | } else if (nodeOneCoordinates[0] < nodeTwoCoordinates[0] && nodeOneCoordinates[1] >= nodeTwoCoordinates[1]) { 215 | let additionalxChange = 0, 216 | additionalyChange = 0; 217 | for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++) { 218 | let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`; 219 | let currentNode = nodes[currentId]; 220 | additionalxChange += currentNode.weight; 221 | } 222 | for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--) { 223 | let currentId = `${nodeTwoCoordinates[0]}-${currenty}`; 224 | let currentNode = nodes[currentId]; 225 | additionalyChange += currentNode.weight; 226 | } 227 | 228 | let otherAdditionalxChange = 0, 229 | otherAdditionalyChange = 0; 230 | for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--) { 231 | let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`; 232 | let currentNode = nodes[currentId]; 233 | additionalyChange += currentNode.weight; 234 | } 235 | for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++) { 236 | let currentId = `${currentx}-${nodeTwoCoordinates[1]}`; 237 | let currentNode = nodes[currentId]; 238 | additionalxChange += currentNode.weight; 239 | } 240 | 241 | if (additionalxChange + additionalyChange < otherAdditionalxChange + otherAdditionalyChange) { 242 | xChange += additionalxChange; 243 | yChange += additionalyChange; 244 | } else { 245 | xChange += otherAdditionalxChange; 246 | yChange += otherAdditionalyChange; 247 | } 248 | } else if (nodeOneCoordinates[0] >= nodeTwoCoordinates[0] && nodeOneCoordinates[1] < nodeTwoCoordinates[1]) { 249 | let additionalxChange = 0, 250 | additionalyChange = 0; 251 | for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--) { 252 | let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`; 253 | let currentNode = nodes[currentId]; 254 | additionalxChange += currentNode.weight; 255 | } 256 | for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++) { 257 | let currentId = `${nodeTwoCoordinates[0]}-${currenty}`; 258 | let currentNode = nodes[currentId]; 259 | additionalyChange += currentNode.weight; 260 | } 261 | 262 | let otherAdditionalxChange = 0, 263 | otherAdditionalyChange = 0; 264 | for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++) { 265 | let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`; 266 | let currentNode = nodes[currentId]; 267 | additionalyChange += currentNode.weight; 268 | } 269 | for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--) { 270 | let currentId = `${currentx}-${nodeTwoCoordinates[1]}`; 271 | let currentNode = nodes[currentId]; 272 | additionalxChange += currentNode.weight; 273 | } 274 | 275 | if (additionalxChange + additionalyChange < otherAdditionalxChange + otherAdditionalyChange) { 276 | xChange += additionalxChange; 277 | yChange += additionalyChange; 278 | } else { 279 | xChange += otherAdditionalxChange; 280 | yChange += otherAdditionalyChange; 281 | } 282 | } else if (nodeOneCoordinates[0] >= nodeTwoCoordinates[0] && nodeOneCoordinates[1] >= nodeTwoCoordinates[1]) { 283 | let additionalxChange = 0, 284 | additionalyChange = 0; 285 | for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--) { 286 | let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`; 287 | let currentNode = nodes[currentId]; 288 | additionalxChange += currentNode.weight; 289 | } 290 | for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--) { 291 | let currentId = `${nodeTwoCoordinates[0]}-${currenty}`; 292 | let currentNode = nodes[currentId]; 293 | additionalyChange += currentNode.weight; 294 | } 295 | 296 | let otherAdditionalxChange = 0, 297 | otherAdditionalyChange = 0; 298 | for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--) { 299 | let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`; 300 | let currentNode = nodes[currentId]; 301 | additionalyChange += currentNode.weight; 302 | } 303 | for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--) { 304 | let currentId = `${currentx}-${nodeTwoCoordinates[1]}`; 305 | let currentNode = nodes[currentId]; 306 | additionalxChange += currentNode.weight; 307 | } 308 | 309 | if (additionalxChange + additionalyChange < otherAdditionalxChange + otherAdditionalyChange) { 310 | xChange += additionalxChange; 311 | yChange += additionalyChange; 312 | } else { 313 | xChange += otherAdditionalxChange; 314 | yChange += otherAdditionalyChange; 315 | } 316 | } 317 | 318 | return xChange + yChange; 319 | 320 | 321 | } 322 | 323 | module.exports = weightedSearchAlgorithm; 324 | -------------------------------------------------------------------------------- /public/styling/algorithms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/algorithms.png -------------------------------------------------------------------------------- /public/styling/bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/bomb.png -------------------------------------------------------------------------------- /public/styling/c_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/c_icon.png -------------------------------------------------------------------------------- /public/styling/car-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/car-down.png -------------------------------------------------------------------------------- /public/styling/car-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/car-left.png -------------------------------------------------------------------------------- /public/styling/car-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/car-right.png -------------------------------------------------------------------------------- /public/styling/car-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/car-up.png -------------------------------------------------------------------------------- /public/styling/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /public/styling/diamond.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /public/styling/dragging.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/dragging.gif -------------------------------------------------------------------------------- /public/styling/fonts/glyphicons/flat-ui-icons-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/glyphicons/flat-ui-icons-regular.eot -------------------------------------------------------------------------------- /public/styling/fonts/glyphicons/flat-ui-icons-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | { 7 | "fontFamily": "flat-ui-icons", 8 | "majorVersion": 1, 9 | "minorVersion": 1, 10 | "fontURL": "http://designmodo.com/flat", 11 | "designer": "Sergey Shmidt", 12 | "designerURL": "http://designmodo.com", 13 | "license": "Attribution-NonCommercial-NoDerivs 3.0 Unported", 14 | "licenseURL": "http://creativecommons.org/licenses/by-nc-nd/3.0/", 15 | "version": "Version 1.1", 16 | "fontId": "flat-ui-icons", 17 | "psName": "flat-ui-icons", 18 | "subFamily": "Regular", 19 | "fullName": "flat-ui-icons", 20 | "description": "Generated by IcoMoon" 21 | } 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /public/styling/fonts/glyphicons/flat-ui-icons-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/glyphicons/flat-ui-icons-regular.ttf -------------------------------------------------------------------------------- /public/styling/fonts/glyphicons/flat-ui-icons-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/glyphicons/flat-ui-icons-regular.woff -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-black.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-black.eot -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-black.ttf -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-black.woff -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-bold.eot -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-bold.ttf -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-bold.woff -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-bolditalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-bolditalic.eot -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-bolditalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-bolditalic.ttf -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-bolditalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-bolditalic.woff -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-italic.eot -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-italic.ttf -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-italic.woff -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-light.eot -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-light.ttf -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-light.woff -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-regular.eot -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-regular.ttf -------------------------------------------------------------------------------- /public/styling/fonts/lato/lato-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/fonts/lato/lato-regular.woff -------------------------------------------------------------------------------- /public/styling/navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/navbar.png -------------------------------------------------------------------------------- /public/styling/path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/path.png -------------------------------------------------------------------------------- /public/styling/pokemonweight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/pokemonweight.png -------------------------------------------------------------------------------- /public/styling/spaceshiptwo-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/styling/spaceshiptwo-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/styling/spaceshiptwo-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/styling/spaceshiptwo-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/styling/triangletwo-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/styling/triangletwo-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/styling/triangletwo-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/styling/triangletwo-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/styling/walls.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/walls.gif -------------------------------------------------------------------------------- /public/styling/wallsandweights.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clementmihailescu/Pathfinding-Visualizer/c51f2d5fd3386b485694581ca0f324cf343f39b1/public/styling/wallsandweights.png -------------------------------------------------------------------------------- /public/styling/weight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | app.use("/public", express.static(__dirname + "/public")) 5 | 6 | app.get("/", (req, res) => { 7 | res.sendFile(__dirname + "/index.html"); 8 | }) 9 | 10 | app.listen(1337, () => { 11 | console.log("The server is up and running!"); 12 | }); 13 | --------------------------------------------------------------------------------