├── .github
└── workflows
│ └── static.yml
├── .gitignore
├── README.md
├── index.html
├── package.json
├── public
├── browser
│ ├── animations
│ │ ├── launchAnimations.js
│ │ ├── launchInstantAnimations.js
│ │ └── mazeGenerationAnimations.js
│ ├── board.js
│ ├── bomb.png
│ ├── bundle.js
│ ├── dragging.gif
│ ├── getDistance.js
│ ├── mazeAlgorithms
│ │ ├── otherMaze.js
│ │ ├── otherOtherMaze.js
│ │ ├── recursiveDivisionMaze.js
│ │ ├── simpleDemonstration.js
│ │ ├── stairDemonstration.js
│ │ └── weightsDemonstration.js
│ ├── pathfindingalgorithm
│ │ ├── astar.js
│ │ ├── bidirectional.js
│ │ ├── testAlgorithms.js
│ │ ├── unweightedSearchAlgorithm.js
│ │ └── weightedSearchAlgorithm.js
│ └── walls.gif
├── img
│ └── login
│ │ └── imac.png
├── index.html
├── manifest.json
├── robots.txt
└── styling
│ ├── bomb.png
│ ├── c_icon.png
│ ├── circle.png
│ ├── cssBasic.css
│ ├── cssPokemon.css
│ ├── diamond.png
│ ├── dragging.gif
│ ├── spaceship.png
│ ├── triangle-down.png
│ ├── triangle-left.png
│ ├── triangle-right.png
│ ├── triangle-up.png
│ ├── walls.gif
│ └── weight.png
├── server.js
├── src
└── index.js
└── yarn.lock
/.github/workflows/static.yml:
--------------------------------------------------------------------------------
1 | # Simple workflow for deploying static content to GitHub Pages
2 | name: Deploy static content to Pages
3 |
4 | on:
5 | # Runs on pushes targeting the default branch
6 | push:
7 | branches: ["main"]
8 |
9 | # Allows you to run this workflow manually from the Actions tab
10 | workflow_dispatch:
11 |
12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
13 | permissions:
14 | contents: read
15 | pages: write
16 | id-token: write
17 |
18 | # Allow one concurrent deployment
19 | concurrency:
20 | group: "pages"
21 | cancel-in-progress: true
22 |
23 | jobs:
24 | # Single deploy job since we're just deploying
25 | deploy:
26 | environment:
27 | name: github-pages
28 | url: ${{ steps.deployment.outputs.page_url }}
29 | runs-on: ubuntu-latest
30 | steps:
31 | - name: Checkout
32 | uses: actions/checkout@v3
33 | - name: Setup Pages
34 | uses: actions/configure-pages@v3
35 | - name: Upload artifact
36 | uses: actions/upload-pages-artifact@v1
37 | with:
38 | # Upload entire repository
39 | path: '.'
40 | - name: Deploy to GitHub Pages
41 | id: deployment
42 | uses: actions/deploy-pages@v1
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pathfinding Visualizer
2 |
3 | Welcome to Pathfinding Visualizer! I built this application because I wanted to learn and implement pathfinding algorithms.You can access it here (use Google Chrome!) https://vardapanchal005.github.io/Pathfinding-Visualizer/
4 | ## Meet the Algorithms
5 |
6 |
7 | This application supports the following algorithms
8 | #### Dijkstra's Algorithm (weighted)
9 | The father of pathfinding algorithms; guarantees the shortest path.
10 |
11 | #### A Search* (weighted)
12 | Arguably the best pathfinding algorithm; uses heuristics to guarantee the shortest path much faster than Dijkstra's Algorithm.
13 |
14 | #### Greedy Best-first Search (weighted)
15 | A faster, more heuristic-heavy version of A*; does not guarantee the shortest path.
16 |
17 | #### Swarm Algorithm (weighted)
18 | A mixture of Dijkstra's Algorithm and A*; does not guarantee the shortest-path.
19 |
20 | #### Convergent Swarm Algorithm (weighted)
21 | the faster, more heuristic-heavy version of Swarm; does not guarantee the shortest path
22 |
23 | #### Bidirectional Swarm Algorithm (weighted)
24 | Swarm from both sides; does not guarantee the shortest path.
25 |
26 | #### Breath-first Search (unweighted)
27 | A great algorithm; guarantees the shortest path.
28 |
29 | #### Depth-first Search (unweighted)
30 | A very bad algorithm for pathfinding; does not guarantee the shortest path.
31 |
32 | On top of the pathfinding algorithms listed above, I implemented a Recursive Division Maze Generation algorithm.
33 |
34 |
35 | reference: https://www.youtube.com/watch?v=msttfIHHkak&t=2286s
36 |
37 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Pathfinding Visualizer
4 |
5 |
6 |
7 |
17 |
18 |
19 |
70 |
71 |
72 |
73 |
Welcome to Pathfinding Visualizer!
74 |
This short tutorial will walk you through all of the features of this application.
75 |
If you want to dive right in, feel free to press the "Skip Tutorial" button below. Otherwise, press "Next"!
76 |
1/9
77 |
78 |
79 |
Next
80 |
Previous
81 |
Skip Tutorial
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
Start Node
90 |
91 |
Target Node
92 |
93 |
Bomb Node
94 |
95 |
Weight Node
96 |
97 |
Unvisited Node
98 |
99 |
Visited Nodes
100 |
101 |
Shortest-path Node
102 |
103 |
Wall Node
104 |
105 |
106 |
Pick an algorithm and visualize it!
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pathfindingalgorithm",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.14.1",
7 | "@testing-library/react": "^13.0.0",
8 | "@testing-library/user-event": "^13.2.1",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-scripts": "5.0.1",
12 | "web-vitals": "^2.1.0"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/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;
--------------------------------------------------------------------------------
/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;
--------------------------------------------------------------------------------
/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 | export default mazeGenerationAnimations;
--------------------------------------------------------------------------------
/public/browser/board.js:
--------------------------------------------------------------------------------
1 | import Node from "./node";
2 | import launchAnimations from "./animations/launchAnimations";
3 | import launchInstantAnimations from "./animations/launchInstantAnimations";
4 | import mazeGenerationAnimations from "./animations/mazeGenerationAnimations";
5 | import weightedSearchAlgorithm from "./pathfindingAlgorithms/weightedSearchAlgorithm";
6 | import unweightedSearchAlgorithm from "./pathfindingAlgorithms/unweightedSearchAlgorithm";
7 | import recursiveDivisionMaze from "./mazeAlgorithms/recursiveDivisionMaze";
8 | import otherMaze from "./mazeAlgorithms/otherMaze";
9 | import otherOtherMaze from "./mazeAlgorithms/otherOtherMaze";
10 | import astar from "./pathfindingAlgorithms/astar";
11 | import stairDemonstration from "./mazeAlgorithms/stairDemonstration";
12 | import weightsDemonstration from "./mazeAlgorithms/weightsDemonstration";
13 | import simpleDemonstration from "./mazeAlgorithms/simpleDemonstration";
14 | import bidirectional from "./pathfindingAlgorithms/bidirectional";
15 | import getDistance from "./getDistance";
16 |
17 | function Board(height, width) {
18 | this.height = height;
19 | this.width = width;
20 | this.start = null;
21 | this.target = null;
22 | this.object = null;
23 | this.boardArray = [];
24 | this.nodes = {};
25 | this.nodesToAnimate = [];
26 | this.objectNodesToAnimate = [];
27 | this.shortestPathNodesToAnimate = [];
28 | this.objectShortestPathNodesToAnimate = [];
29 | this.wallsToAnimate = [];
30 | this.mouseDown = false;
31 | this.pressedNodeStatus = "normal";
32 | this.previouslyPressedNodeStatus = null;
33 | this.previouslySwitchedNode = null;
34 | this.previouslySwitchedNodeWeight = 0;
35 | this.keyDown = false;
36 | this.algoDone = false;
37 | this.currentAlgorithm = null;
38 | this.currentHeuristic = null;
39 | this.numberOfObjects = 0;
40 | this.isObject = false;
41 | this.buttonsOn = false;
42 | this.speed = "fast";
43 | }
44 |
45 | Board.prototype.initialise = function() {
46 | this.createGrid();
47 | this.addEventListeners();
48 | this.toggleTutorialButtons();
49 | };
50 |
51 | Board.prototype.createGrid = function() {
52 | let tableHTML = "";
53 | for (let r = 0; r < this.height; r++) {
54 | let currentArrayRow = [];
55 | let currentHTMLRow = ``;
56 | for (let c = 0; c < this.width; c++) {
57 | let newNodeId = `${r}-${c}`, newNodeClass, newNode;
58 | if (r === Math.floor(this.height / 2) && c === Math.floor(this.width / 4)) {
59 | newNodeClass = "start";
60 | this.start = `${newNodeId}`;
61 | } else if (r === Math.floor(this.height / 2) && c === Math.floor(3 * this.width / 4)) {
62 | newNodeClass = "target";
63 | this.target = `${newNodeId}`;
64 | } else {
65 | newNodeClass = "unvisited";
66 | }
67 | newNode = new Node(newNodeId, newNodeClass);
68 | currentArrayRow.push(newNode);
69 | currentHTMLRow += ` `;
70 | this.nodes[`${newNodeId}`] = newNode;
71 | }
72 | this.boardArray.push(currentArrayRow);
73 | tableHTML += `${currentHTMLRow} `;
74 | }
75 | let board = document.getElementById("board");
76 | board.innerHTML = tableHTML;
77 | };
78 |
79 | Board.prototype.addEventListeners = function() {
80 | let board = this;
81 | for (let r = 0; r < board.height; r++) {
82 | for (let c = 0; c < board.width; c++) {
83 | let currentId = `${r}-${c}`;
84 | let currentNode = board.getNode(currentId);
85 | let currentElement = document.getElementById(currentId);
86 | currentElement.onmousedown = (e) => {
87 | e.preventDefault();
88 | if (this.buttonsOn) {
89 | board.mouseDown = true;
90 | if (currentNode.status === "start" || currentNode.status === "target" || currentNode.status === "object") {
91 | board.pressedNodeStatus = currentNode.status;
92 | } else {
93 | board.pressedNodeStatus = "normal";
94 | board.changeNormalNode(currentNode);
95 | }
96 | }
97 | }
98 | currentElement.onmouseup = () => {
99 | if (this.buttonsOn) {
100 | board.mouseDown = false;
101 | if (board.pressedNodeStatus === "target") {
102 | board.target = currentId;
103 | } else if (board.pressedNodeStatus === "start") {
104 | board.start = currentId;
105 | } else if (board.pressedNodeStatus === "object") {
106 | board.object = currentId;
107 | }
108 | board.pressedNodeStatus = "normal";
109 | }
110 | }
111 | currentElement.onmouseenter = () => {
112 | if (this.buttonsOn) {
113 | if (board.mouseDown && board.pressedNodeStatus !== "normal") {
114 | board.changeSpecialNode(currentNode);
115 | if (board.pressedNodeStatus === "target") {
116 | board.target = currentId;
117 | if (board.algoDone) {
118 | board.redoAlgorithm();
119 | }
120 | } else if (board.pressedNodeStatus === "start") {
121 | board.start = currentId;
122 | if (board.algoDone) {
123 | board.redoAlgorithm();
124 | }
125 | } else if (board.pressedNodeStatus === "object") {
126 | board.object = currentId;
127 | if (board.algoDone) {
128 | board.redoAlgorithm();
129 | }
130 | }
131 | } else if (board.mouseDown) {
132 | board.changeNormalNode(currentNode);
133 | }
134 | }
135 | }
136 | currentElement.onmouseleave = () => {
137 | if (this.buttonsOn) {
138 | if (board.mouseDown && board.pressedNodeStatus !== "normal") {
139 | board.changeSpecialNode(currentNode);
140 | }
141 | }
142 | }
143 | }
144 | }
145 | };
146 |
147 | Board.prototype.getNode = function(id) {
148 | let coordinates = id.split("-");
149 | let r = parseInt(coordinates[0]);
150 | let c = parseInt(coordinates[1]);
151 | return this.boardArray[r][c];
152 | };
153 |
154 | Board.prototype.changeSpecialNode = function(currentNode) {
155 | let element = document.getElementById(currentNode.id), previousElement;
156 | if (this.previouslySwitchedNode) previousElement = document.getElementById(this.previouslySwitchedNode.id);
157 | if (currentNode.status !== "target" && currentNode.status !== "start" && currentNode.status !== "object") {
158 | if (this.previouslySwitchedNode) {
159 | this.previouslySwitchedNode.status = this.previouslyPressedNodeStatus;
160 | previousElement.className = this.previouslySwitchedNodeWeight === 15 ?
161 | "unvisited weight" : this.previouslyPressedNodeStatus;
162 | this.previouslySwitchedNode.weight = this.previouslySwitchedNodeWeight === 15 ?
163 | 15 : 0;
164 | this.previouslySwitchedNode = null;
165 | this.previouslySwitchedNodeWeight = currentNode.weight;
166 |
167 | this.previouslyPressedNodeStatus = currentNode.status;
168 | element.className = this.pressedNodeStatus;
169 | currentNode.status = this.pressedNodeStatus;
170 |
171 | currentNode.weight = 0;
172 | }
173 | } else if (currentNode.status !== this.pressedNodeStatus && !this.algoDone) {
174 | this.previouslySwitchedNode.status = this.pressedNodeStatus;
175 | previousElement.className = this.pressedNodeStatus;
176 | } else if (currentNode.status === this.pressedNodeStatus) {
177 | this.previouslySwitchedNode = currentNode;
178 | element.className = this.previouslyPressedNodeStatus;
179 | currentNode.status = this.previouslyPressedNodeStatus;
180 | }
181 | };
182 |
183 | Board.prototype.changeNormalNode = function(currentNode) {
184 | let element = document.getElementById(currentNode.id);
185 | let relevantStatuses = ["start", "target", "object"];
186 | let unweightedAlgorithms = ["dfs", "bfs"]
187 | if (!this.keyDown) {
188 | if (!relevantStatuses.includes(currentNode.status)) {
189 | element.className = currentNode.status !== "wall" ?
190 | "wall" : "unvisited";
191 | currentNode.status = element.className !== "wall" ?
192 | "unvisited" : "wall";
193 | currentNode.weight = 0;
194 | }
195 | } else if (this.keyDown === 87 && !unweightedAlgorithms.includes(this.currentAlgorithm)) {
196 | if (!relevantStatuses.includes(currentNode.status)) {
197 | element.className = currentNode.weight !== 15 ?
198 | "unvisited weight" : "unvisited";
199 | currentNode.weight = element.className !== "unvisited weight" ?
200 | 0 : 15;
201 | currentNode.status = "unvisited";
202 | }
203 | }
204 | };
205 |
206 | Board.prototype.drawShortestPath = function(targetNodeId, startNodeId, object) {
207 | let currentNode;
208 | if (this.currentAlgorithm !== "bidirectional") {
209 | currentNode = this.nodes[this.nodes[targetNodeId].previousNode];
210 | if (object) {
211 | while (currentNode.id !== startNodeId) {
212 | this.objectShortestPathNodesToAnimate.unshift(currentNode);
213 | currentNode = this.nodes[currentNode.previousNode];
214 | }
215 | } else {
216 | while (currentNode.id !== startNodeId) {
217 | this.shortestPathNodesToAnimate.unshift(currentNode);
218 | document.getElementById(currentNode.id).className = `shortest-path`;
219 | currentNode = this.nodes[currentNode.previousNode];
220 | }
221 | }
222 | } else {
223 | if (this.middleNode !== this.target && this.middleNode !== this.start) {
224 | currentNode = this.nodes[this.nodes[this.middleNode].previousNode];
225 | secondCurrentNode = this.nodes[this.nodes[this.middleNode].otherpreviousNode];
226 | if (secondCurrentNode.id === this.target) {
227 | this.nodes[this.target].direction = getDistance(this.nodes[this.middleNode], this.nodes[this.target])[2];
228 | }
229 | if (this.nodes[this.middleNode].weight === 0) {
230 | document.getElementById(this.middleNode).className = `shortest-path`;
231 | } else {
232 | document.getElementById(this.middleNode).className = `shortest-path weight`;
233 | }
234 | while (currentNode.id !== startNodeId) {
235 | this.shortestPathNodesToAnimate.unshift(currentNode);
236 | document.getElementById(currentNode.id).className = `shortest-path`;
237 | currentNode = this.nodes[currentNode.previousNode];
238 | }
239 | while (secondCurrentNode.id !== targetNodeId) {
240 | this.shortestPathNodesToAnimate.unshift(secondCurrentNode);
241 | document.getElementById(secondCurrentNode.id).className = `shortest-path`;
242 | if (secondCurrentNode.otherpreviousNode === targetNodeId) {
243 | if (secondCurrentNode.otherdirection === "left") {
244 | secondCurrentNode.direction = "right";
245 | } else if (secondCurrentNode.otherdirection === "right") {
246 | secondCurrentNode.direction = "left";
247 | } else if (secondCurrentNode.otherdirection === "up") {
248 | secondCurrentNode.direction = "down";
249 | } else if (secondCurrentNode.otherdirection === "down") {
250 | secondCurrentNode.direction = "up";
251 | }
252 | this.nodes[this.target].direction = getDistance(secondCurrentNode, this.nodes[this.target])[2];
253 | }
254 | secondCurrentNode = this.nodes[secondCurrentNode.otherpreviousNode]
255 | }
256 | } else {
257 | document.getElementById(this.nodes[this.target].previousNode).className = `shortest-path`;
258 | }
259 | }
260 | };
261 |
262 | Board.prototype.addShortestPath = function(targetNodeId, startNodeId, object) {
263 | let currentNode = this.nodes[this.nodes[targetNodeId].previousNode];
264 | if (object) {
265 | while (currentNode.id !== startNodeId) {
266 | this.objectShortestPathNodesToAnimate.unshift(currentNode);
267 | currentNode.relatesToObject = true;
268 | currentNode = this.nodes[currentNode.previousNode];
269 | }
270 | } else {
271 | while (currentNode.id !== startNodeId) {
272 | this.shortestPathNodesToAnimate.unshift(currentNode);
273 | currentNode = this.nodes[currentNode.previousNode];
274 | }
275 | }
276 | };
277 |
278 | Board.prototype.drawShortestPathTimeout = function(targetNodeId, startNodeId, type, object) {
279 | let board = this;
280 | let currentNode;
281 | let secondCurrentNode;
282 | let currentNodesToAnimate;
283 |
284 | if (board.currentAlgorithm !== "bidirectional") {
285 | currentNode = board.nodes[board.nodes[targetNodeId].previousNode];
286 | if (object) {
287 | board.objectShortestPathNodesToAnimate.push("object");
288 | currentNodesToAnimate = board.objectShortestPathNodesToAnimate.concat(board.shortestPathNodesToAnimate);
289 | } else {
290 | currentNodesToAnimate = [];
291 | while (currentNode.id !== startNodeId) {
292 | currentNodesToAnimate.unshift(currentNode);
293 | currentNode = board.nodes[currentNode.previousNode];
294 | }
295 | }
296 | } else {
297 | if (board.middleNode !== board.target && board.middleNode !== board.start) {
298 | currentNode = board.nodes[board.nodes[board.middleNode].previousNode];
299 | secondCurrentNode = board.nodes[board.nodes[board.middleNode].otherpreviousNode];
300 | if (secondCurrentNode.id === board.target) {
301 | board.nodes[board.target].direction = getDistance(board.nodes[board.middleNode], board.nodes[board.target])[2];
302 | }
303 | if (object) {
304 |
305 | } else {
306 | currentNodesToAnimate = [];
307 | board.nodes[board.middleNode].direction = getDistance(currentNode, board.nodes[board.middleNode])[2];
308 | while (currentNode.id !== startNodeId) {
309 | currentNodesToAnimate.unshift(currentNode);
310 | currentNode = board.nodes[currentNode.previousNode];
311 | }
312 | currentNodesToAnimate.push(board.nodes[board.middleNode]);
313 | while (secondCurrentNode.id !== targetNodeId) {
314 | if (secondCurrentNode.otherdirection === "left") {
315 | secondCurrentNode.direction = "right";
316 | } else if (secondCurrentNode.otherdirection === "right") {
317 | secondCurrentNode.direction = "left";
318 | } else if (secondCurrentNode.otherdirection === "up") {
319 | secondCurrentNode.direction = "down";
320 | } else if (secondCurrentNode.otherdirection === "down") {
321 | secondCurrentNode.direction = "up";
322 | }
323 | currentNodesToAnimate.push(secondCurrentNode);
324 | if (secondCurrentNode.otherpreviousNode === targetNodeId) {
325 | board.nodes[board.target].direction = getDistance(secondCurrentNode, board.nodes[board.target])[2];
326 | }
327 | secondCurrentNode = board.nodes[secondCurrentNode.otherpreviousNode]
328 | }
329 | }
330 | } else {
331 | currentNodesToAnimate = [];
332 | let target = board.nodes[board.target];
333 | currentNodesToAnimate.push(board.nodes[target.previousNode], target);
334 | }
335 |
336 | }
337 |
338 |
339 | timeout(0);
340 |
341 | function timeout(index) {
342 | if (!currentNodesToAnimate.length) currentNodesToAnimate.push(board.nodes[board.start]);
343 | setTimeout(function () {
344 | if (index === 0) {
345 | shortestPathChange(currentNodesToAnimate[index]);
346 | } else if (index < currentNodesToAnimate.length) {
347 | shortestPathChange(currentNodesToAnimate[index], currentNodesToAnimate[index - 1]);
348 | } else if (index === currentNodesToAnimate.length) {
349 | shortestPathChange(board.nodes[board.target], currentNodesToAnimate[index - 1], "isActualTarget");
350 | }
351 | if (index > currentNodesToAnimate.length) {
352 | board.toggleButtons();
353 | return;
354 | }
355 | timeout(index + 1);
356 | }, 40)
357 | }
358 |
359 |
360 | function shortestPathChange(currentNode, previousNode, isActualTarget) {
361 | if (currentNode === "object") {
362 | let element = document.getElementById(board.object);
363 | element.className = "objectTransparent";
364 | } else if (currentNode.id !== board.start) {
365 | if (currentNode.id !== board.target || currentNode.id === board.target && isActualTarget) {
366 | let currentHTMLNode = document.getElementById(currentNode.id);
367 | if (type === "unweighted") {
368 | currentHTMLNode.className = "shortest-path-unweighted";
369 | } else {
370 | let direction;
371 | if (currentNode.relatesToObject && !currentNode.overwriteObjectRelation && currentNode.id !== board.target) {
372 | direction = "storedDirection";
373 | currentNode.overwriteObjectRelation = true;
374 | } else {
375 | direction = "direction";
376 | }
377 | if (currentNode[direction] === "up") {
378 | currentHTMLNode.className = "shortest-path-up";
379 | } else if (currentNode[direction] === "down") {
380 | currentHTMLNode.className = "shortest-path-down";
381 | } else if (currentNode[direction] === "right") {
382 | currentHTMLNode.className = "shortest-path-right";
383 | } else if (currentNode[direction] === "left") {
384 | currentHTMLNode.className = "shortest-path-left";
385 | } else {
386 | currentHTMLNode.className = "shortest-path";
387 | }
388 | }
389 | }
390 | }
391 | if (previousNode) {
392 | if (previousNode !== "object" && previousNode.id !== board.target && previousNode.id !== board.start) {
393 | let previousHTMLNode = document.getElementById(previousNode.id);
394 | previousHTMLNode.className = previousNode.weight === 15 ? "shortest-path weight" : "shortest-path";
395 | }
396 | } else {
397 | let element = document.getElementById(board.start);
398 | element.className = "startTransparent";
399 | }
400 | }
401 |
402 |
403 |
404 |
405 |
406 | };
407 |
408 | Board.prototype.createMazeOne = function(type) {
409 | Object.keys(this.nodes).forEach(node => {
410 | let random = Math.random();
411 | let currentHTMLNode = document.getElementById(node);
412 | let relevantClassNames = ["start", "target", "object"]
413 | let randomTwo = type === "wall" ? 0.25 : 0.35;
414 | if (random < randomTwo && !relevantClassNames.includes(currentHTMLNode.className)) {
415 | if (type === "wall") {
416 | currentHTMLNode.className = "wall";
417 | this.nodes[node].status = "wall";
418 | this.nodes[node].weight = 0;
419 | } else if (type === "weight") {
420 | currentHTMLNode.className = "unvisited weight";
421 | this.nodes[node].status = "unvisited";
422 | this.nodes[node].weight = 15;
423 | }
424 | }
425 | });
426 | };
427 |
428 | Board.prototype.clearPath = function(clickedButton) {
429 | if (clickedButton) {
430 | let start = this.nodes[this.start];
431 | let target = this.nodes[this.target];
432 | let object = this.numberOfObjects ? this.nodes[this.object] : null;
433 | start.status = "start";
434 | document.getElementById(start.id).className = "start";
435 | target.status = "target";
436 | document.getElementById(target.id).className = "target";
437 | if (object) {
438 | object.status = "object";
439 | document.getElementById(object.id).className = "object";
440 | }
441 | }
442 |
443 | document.getElementById("startButtonStart").onclick = () => {
444 | if (!this.currentAlgorithm) {
445 | document.getElementById("startButtonStart").innerHTML = 'Pick an Algorithm! '
446 | } else {
447 | this.clearPath("clickedButton");
448 | this.toggleButtons();
449 | let weightedAlgorithms = ["dijkstra", "CLA", "greedy"];
450 | let unweightedAlgorithms = ["dfs", "bfs"];
451 | let success;
452 | if (this.currentAlgorithm === "bidirectional") {
453 | if (!this.numberOfObjects) {
454 | success = bidirectional(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic, this);
455 | launchAnimations(this, success, "weighted");
456 | } else {
457 | this.isObject = true;
458 | }
459 | this.algoDone = true;
460 | } else if (this.currentAlgorithm === "astar") {
461 | if (!this.numberOfObjects) {
462 | success = weightedSearchAlgorithm(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
463 | launchAnimations(this, success, "weighted");
464 | } else {
465 | this.isObject = true;
466 | success = weightedSearchAlgorithm(this.nodes, this.start, this.object, this.objectNodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
467 | launchAnimations(this, success, "weighted", "object", this.currentAlgorithm, this.currentHeuristic);
468 | }
469 | this.algoDone = true;
470 | } else if (weightedAlgorithms.includes(this.currentAlgorithm)) {
471 | if (!this.numberOfObjects) {
472 | success = weightedSearchAlgorithm(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
473 | launchAnimations(this, success, "weighted");
474 | } else {
475 | this.isObject = true;
476 | success = weightedSearchAlgorithm(this.nodes, this.start, this.object, this.objectNodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
477 | launchAnimations(this, success, "weighted", "object", this.currentAlgorithm, this.currentHeuristic);
478 | }
479 | this.algoDone = true;
480 | } else if (unweightedAlgorithms.includes(this.currentAlgorithm)) {
481 | if (!this.numberOfObjects) {
482 | success = unweightedSearchAlgorithm(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm);
483 | launchAnimations(this, success, "unweighted");
484 | } else {
485 | this.isObject = true;
486 | success = unweightedSearchAlgorithm(this.nodes, this.start, this.object, this.objectNodesToAnimate, this.boardArray, this.currentAlgorithm);
487 | launchAnimations(this, success, "unweighted", "object", this.currentAlgorithm);
488 | }
489 | this.algoDone = true;
490 | }
491 | }
492 | }
493 |
494 | this.algoDone = false;
495 | Object.keys(this.nodes).forEach(id => {
496 | let currentNode = this.nodes[id];
497 | currentNode.previousNode = null;
498 | currentNode.distance = Infinity;
499 | currentNode.totalDistance = Infinity;
500 | currentNode.heuristicDistance = null;
501 | currentNode.direction = null;
502 | currentNode.storedDirection = null;
503 | currentNode.relatesToObject = false;
504 | currentNode.overwriteObjectRelation = false;
505 | currentNode.otherpreviousNode = null;
506 | currentNode.otherdistance = Infinity;
507 | currentNode.otherdirection = null;
508 | let currentHTMLNode = document.getElementById(id);
509 | let relevantStatuses = ["wall", "start", "target", "object"];
510 | if ((!relevantStatuses.includes(currentNode.status) || currentHTMLNode.className === "visitedobject") && currentNode.weight !== 15) {
511 | currentNode.status = "unvisited";
512 | currentHTMLNode.className = "unvisited";
513 | } else if (currentNode.weight === 15) {
514 | currentNode.status = "unvisited";
515 | currentHTMLNode.className = "unvisited weight";
516 | }
517 | });
518 | };
519 |
520 | Board.prototype.clearWalls = function() {
521 | this.clearPath("clickedButton");
522 | Object.keys(this.nodes).forEach(id => {
523 | let currentNode = this.nodes[id];
524 | let currentHTMLNode = document.getElementById(id);
525 | if (currentNode.status === "wall" || currentNode.weight === 15) {
526 | currentNode.status = "unvisited";
527 | currentNode.weight = 0;
528 | currentHTMLNode.className = "unvisited";
529 | }
530 | });
531 | }
532 |
533 | Board.prototype.clearWeights = function() {
534 | Object.keys(this.nodes).forEach(id => {
535 | let currentNode = this.nodes[id];
536 | let currentHTMLNode = document.getElementById(id);
537 | if (currentNode.weight === 15) {
538 | currentNode.status = "unvisited";
539 | currentNode.weight = 0;
540 | currentHTMLNode.className = "unvisited";
541 | }
542 | });
543 | }
544 |
545 | Board.prototype.clearNodeStatuses = function() {
546 | Object.keys(this.nodes).forEach(id => {
547 | let currentNode = this.nodes[id];
548 | currentNode.previousNode = null;
549 | currentNode.distance = Infinity;
550 | currentNode.totalDistance = Infinity;
551 | currentNode.heuristicDistance = null;
552 | currentNode.storedDirection = currentNode.direction;
553 | currentNode.direction = null;
554 | let relevantStatuses = ["wall", "start", "target", "object"];
555 | if (!relevantStatuses.includes(currentNode.status)) {
556 | currentNode.status = "unvisited";
557 | }
558 | })
559 | };
560 |
561 | Board.prototype.instantAlgorithm = function() {
562 | let weightedAlgorithms = ["dijkstra", "CLA", "greedy"];
563 | let unweightedAlgorithms = ["dfs", "bfs"];
564 | let success;
565 | if (this.currentAlgorithm === "bidirectional") {
566 | if (!this.numberOfObjects) {
567 | success = bidirectional(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic, this);
568 | launchInstantAnimations(this, success, "weighted");
569 | } else {
570 | this.isObject = true;
571 | }
572 | this.algoDone = true;
573 | } else if (this.currentAlgorithm === "astar") {
574 | if (!this.numberOfObjects) {
575 | success = weightedSearchAlgorithm(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
576 | launchInstantAnimations(this, success, "weighted");
577 | } else {
578 | this.isObject = true;
579 | success = weightedSearchAlgorithm(this.nodes, this.start, this.object, this.objectNodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
580 | launchInstantAnimations(this, success, "weighted", "object", this.currentAlgorithm);
581 | }
582 | this.algoDone = true;
583 | }
584 | if (weightedAlgorithms.includes(this.currentAlgorithm)) {
585 | if (!this.numberOfObjects) {
586 | success = weightedSearchAlgorithm(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
587 | launchInstantAnimations(this, success, "weighted");
588 | } else {
589 | this.isObject = true;
590 | success = weightedSearchAlgorithm(this.nodes, this.start, this.object, this.objectNodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
591 | launchInstantAnimations(this, success, "weighted", "object", this.currentAlgorithm, this.currentHeuristic);
592 | }
593 | this.algoDone = true;
594 | } else if (unweightedAlgorithms.includes(this.currentAlgorithm)) {
595 | if (!this.numberOfObjects) {
596 | success = unweightedSearchAlgorithm(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm);
597 | launchInstantAnimations(this, success, "unweighted");
598 | } else {
599 | this.isObject = true;
600 | success = unweightedSearchAlgorithm(this.nodes, this.start, this.object, this.objectNodesToAnimate, this.boardArray, this.currentAlgorithm);
601 | launchInstantAnimations(this, success, "unweighted", "object", this.currentAlgorithm);
602 | }
603 | this.algoDone = true;
604 | }
605 | };
606 |
607 | Board.prototype.redoAlgorithm = function() {
608 | this.clearPath();
609 | this.instantAlgorithm();
610 | };
611 |
612 | Board.prototype.reset = function(objectNotTransparent) {
613 | this.nodes[this.start].status = "start";
614 | document.getElementById(this.start).className = "startTransparent";
615 | this.nodes[this.target].status = "target";
616 | if (this.object) {
617 | this.nodes[this.object].status = "object";
618 | if (objectNotTransparent) {
619 | document.getElementById(this.object).className = "visitedObjectNode";
620 | } else {
621 | document.getElementById(this.object).className = "objectTransparent";
622 | }
623 | }
624 | };
625 |
626 | Board.prototype.resetHTMLNodes = function() {
627 | let start = document.getElementById(this.start);
628 | let target = document.getElementById(this.target);
629 | start.className = "start";
630 | target.className = "target";
631 | };
632 |
633 | Board.prototype.changeStartNodeImages = function() {
634 | let unweighted = ["bfs", "dfs"];
635 | let strikethrough = ["bfs", "dfs"];
636 | let guaranteed = ["dijkstra", "astar"];
637 | let name = "";
638 | if (this.currentAlgorithm === "bfs") {
639 | name = "Breath-first Search";
640 | } else if (this.currentAlgorithm === "dfs") {
641 | name = "Depth-first Search";
642 | } else if (this.currentAlgorithm === "dijkstra") {
643 | name = "Dijkstra's Algorithm";
644 | } else if (this.currentAlgorithm === "astar") {
645 | name = "A* Search";
646 | } else if (this.currentAlgorithm === "greedy") {
647 | name = "Greedy Best-first Search";
648 | } else if (this.currentAlgorithm === "CLA" && this.currentHeuristic !== "extraPoweredManhattanDistance") {
649 | name = "Swarm Algorithm";
650 | } else if (this.currentAlgorithm === "CLA" && this.currentHeuristic === "extraPoweredManhattanDistance") {
651 | name = "Convergent Swarm Algorithm";
652 | } else if (this.currentAlgorithm === "bidirectional") {
653 | name = "Bidirectional Swarm Algorithm";
654 | }
655 | if (unweighted.includes(this.currentAlgorithm)) {
656 | if (this.currentAlgorithm === "dfs") {
657 | document.getElementById("algorithmDescriptor").innerHTML = `${name} is unweighted and does not guarantee the shortest path!`;
658 | } else {
659 | document.getElementById("algorithmDescriptor").innerHTML = `${name} is unweighted and guarantees the shortest path!`;
660 | }
661 | document.getElementById("weightLegend").className = "strikethrough";
662 | for (let i = 0; i < 14; i++) {
663 | let j = i.toString();
664 | let backgroundImage = document.styleSheets["1"].rules[j].style.backgroundImage;
665 | document.styleSheets["1"].rules[j].style.backgroundImage = backgroundImage.replace("triangle", "spaceship");
666 | }
667 | } else {
668 | if (this.currentAlgorithm === "greedy" || this.currentAlgorithm === "CLA") {
669 | document.getElementById("algorithmDescriptor").innerHTML = `${name} is weighted and does not guarantee the shortest path!`;
670 | }
671 | document.getElementById("weightLegend").className = "";
672 | for (let i = 0; i < 14; i++) {
673 | let j = i.toString();
674 | let backgroundImage = document.styleSheets["1"].rules[j].style.backgroundImage;
675 | document.styleSheets["1"].rules[j].style.backgroundImage = backgroundImage.replace("spaceship", "triangle");
676 | }
677 | }
678 | if (this.currentAlgorithm === "bidirectional") {
679 |
680 | document.getElementById("algorithmDescriptor").innerHTML = `${name} is weighted and does not guarantee the shortest path!`;
681 | document.getElementById("bombLegend").className = "strikethrough";
682 | document.getElementById("startButtonAddObject").className = "navbar-inverse navbar-nav disabledA";
683 | } else {
684 | document.getElementById("bombLegend").className = "";
685 | document.getElementById("startButtonAddObject").className = "navbar-inverse navbar-nav";
686 | }
687 | if (guaranteed.includes(this.currentAlgorithm)) {
688 | document.getElementById("algorithmDescriptor").innerHTML = `${name} is weighted and guarantees the shortest path!`;
689 | }
690 | };
691 |
692 | let counter = 1;
693 | Board.prototype.toggleTutorialButtons = function() {
694 |
695 | document.getElementById("skipButton").onclick = () => {
696 | document.getElementById("tutorial").style.display = "none";
697 | this.toggleButtons();
698 | }
699 |
700 | if (document.getElementById("nextButton")) {
701 | document.getElementById("nextButton").onclick = () => {
702 | if (counter < 9) counter++;
703 | nextPreviousClick();
704 | this.toggleTutorialButtons();
705 | }
706 | }
707 |
708 | document.getElementById("previousButton").onclick = () => {
709 | if (counter > 1) counter--;
710 | nextPreviousClick();
711 | this.toggleTutorialButtons()
712 | }
713 |
714 | let board = this;
715 | function nextPreviousClick() {
716 | if (counter === 1) {
717 | document.getElementById("tutorial").innerHTML = `Welcome to Pathfinding Visualizer! This short tutorial will walk you through all of the features of this application. If you want to dive right in, feel free to press the "Skip Tutorial" button below. Otherwise, press "Next"!
1/9
Next Previous Skip Tutorial `
718 | } else if (counter === 2) {
719 | document.getElementById("tutorial").innerHTML = `What is a pathfinding algorithm? At its core, a pathfinding algorithm seeks to find the shortest path between two points. This application visualizes various pathfinding algorithms in action, and more! All of the algorithms on this application are adapted for a 2D grid, where 90 degree turns have a "cost" of 1 and movements from a node to another have a "cost" of 1.
${counter}/9
Next Previous Skip Tutorial `
720 | } else if (counter === 3) {
721 | document.getElementById("tutorial").innerHTML = `Picking an algorithm Choose an algorithm from the "Algorithms" drop-down menu. Note that some algorithms are unweighted , while others are weighted . Unweighted algorithms do not take turns or weight nodes into account, whereas weighted ones do. Additionally, not all algorithms guarantee the shortest path.
${counter}/9
Next Previous Skip Tutorial `
722 | } else if (counter === 4) {
723 | document.getElementById("tutorial").innerHTML = `Meet the algorithms Not all algorithms are created equal. Dijkstra's Algorithm (weighted): the father of pathfinding algorithms; guarantees the shortest pathA* Search (weighted): arguably the best pathfinding algorithm; uses heuristics to guarantee the shortest path much faster than Dijkstra's AlgorithmGreedy Best-first Search (weighted): a faster, more heuristic-heavy version of A*; does not guarantee the shortest pathSwarm Algorithm (weighted): a mixture of Dijkstra's Algorithm and A*; does not guarantee the shortest-pathConvergent Swarm Algorithm (weighted): the faster, more heuristic-heavy version of Swarm; does not guarantee the shortest pathBidirectional Swarm Algorithm (weighted): Swarm from both sides; does not guarantee the shortest pathBreath-first Search (unweighted): a great algorithm; guarantees the shortest pathDepth-first Search (unweighted): a very bad algorithm for pathfinding; does not guarantee the shortest path${counter}/9
Next Previous Skip Tutorial `
724 | } else if (counter === 5) {
725 | document.getElementById("tutorial").innerHTML = `Adding walls and weights Click on the grid to add a wall. Click on the grid while pressing W to add a weight. Generate mazes and patterns from the "Mazes & Patterns" drop-down menu. Walls are impenetrable, meaning that a path cannot cross through them. Weights, however, are not impassable. They are simply more "costly" to move through. In this application, moving through a weight node has a "cost" of 15.
${counter}/9
Next Previous Skip Tutorial `
726 | } else if (counter === 6) {
727 | document.getElementById("tutorial").innerHTML = `Adding a bomb Click the "Add Bomb" button. Adding a bomb will change the course of the chosen algorithm. In other words, the algorithm will first look for the bomb (in an effort to diffuse it) and will then look for the target node. Note that the Bidirectional Swarm Algorithm does not support adding a bomb.
${counter}/9
Next Previous Skip Tutorial `
728 | } else if (counter === 7) {
729 | document.getElementById("tutorial").innerHTML = `Dragging nodes Click and drag the start, bomb, and target nodes to move them. Note that you can drag nodes even after an algorithm has finished running. This will allow you to instantly see different paths.
${counter}/9
Next Previous Skip Tutorial `
730 | } else if (counter === 8) {
731 | document.getElementById("tutorial").innerHTML = `Visualizing and more Use the navbar buttons to visualize algorithms and to do other stuff! You can clear the current path, clear walls and weights, clear the entire board, and adjust the visualization speed, all from the navbar. If you want to access this tutorial again, click on "Pathfinding Visualizer" in the top left corner of your screen.
${counter}/9
Next Previous Skip Tutorial `
732 | } else if (counter === 9) {
733 | document.getElementById("tutorial").innerHTML = `Enjoy! I hope you have just as much fun playing around with this visualization tool as I had building it! .${counter}/9
Finish Previous Skip Tutorial `
734 | document.getElementById("finishButton").onclick = () => {
735 | document.getElementById("tutorial").style.display = "none";
736 | board.toggleButtons();
737 | }
738 | }
739 | }
740 |
741 | };
742 |
743 | Board.prototype.toggleButtons = function() {
744 | document.getElementById("refreshButton").onclick = () => {
745 | window.location.reload(true);
746 | }
747 |
748 | if (!this.buttonsOn) {
749 | this.buttonsOn = true;
750 |
751 | document.getElementById("startButtonStart").onclick = () => {
752 | if (!this.currentAlgorithm) {
753 | document.getElementById("startButtonStart").innerHTML = 'Pick an Algorithm! '
754 | } else {
755 | this.clearPath("clickedButton");
756 | this.toggleButtons();
757 | let weightedAlgorithms = ["dijkstra", "CLA", "CLA", "greedy"];
758 | let unweightedAlgorithms = ["dfs", "bfs"];
759 | let success;
760 | if (this.currentAlgorithm === "bidirectional") {
761 | if (!this.numberOfObjects) {
762 | success = bidirectional(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic, this);
763 | launchAnimations(this, success, "weighted");
764 | } else {
765 | this.isObject = true;
766 | success = bidirectional(this.nodes, this.start, this.object, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic, this);
767 | launchAnimations(this, success, "weighted");
768 | }
769 | this.algoDone = true;
770 | } else if (this.currentAlgorithm === "astar") {
771 | if (!this.numberOfObjects) {
772 | success = weightedSearchAlgorithm(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
773 | launchAnimations(this, success, "weighted");
774 | } else {
775 | this.isObject = true;
776 | success = weightedSearchAlgorithm(this.nodes, this.start, this.object, this.objectNodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
777 | launchAnimations(this, success, "weighted", "object", this.currentAlgorithm);
778 | }
779 | this.algoDone = true;
780 | } else if (weightedAlgorithms.includes(this.currentAlgorithm)) {
781 | if (!this.numberOfObjects) {
782 | success = weightedSearchAlgorithm(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
783 | launchAnimations(this, success, "weighted");
784 | } else {
785 | this.isObject = true;
786 | success = weightedSearchAlgorithm(this.nodes, this.start, this.object, this.objectNodesToAnimate, this.boardArray, this.currentAlgorithm, this.currentHeuristic);
787 | launchAnimations(this, success, "weighted", "object", this.currentAlgorithm, this.currentHeuristic);
788 | }
789 | this.algoDone = true;
790 | } else if (unweightedAlgorithms.includes(this.currentAlgorithm)) {
791 | if (!this.numberOfObjects) {
792 | success = unweightedSearchAlgorithm(this.nodes, this.start, this.target, this.nodesToAnimate, this.boardArray, this.currentAlgorithm);
793 | launchAnimations(this, success, "unweighted");
794 | } else {
795 | this.isObject = true;
796 | success = unweightedSearchAlgorithm(this.nodes, this.start, this.object, this.objectNodesToAnimate, this.boardArray, this.currentAlgorithm);
797 | launchAnimations(this, success, "unweighted", "object", this.currentAlgorithm);
798 | }
799 | this.algoDone = true;
800 | }
801 | }
802 | }
803 |
804 | document.getElementById("adjustFast").onclick = () => {
805 | this.speed = "fast";
806 | document.getElementById("adjustSpeed").innerHTML = 'Speed: Fast ';
807 | }
808 |
809 | document.getElementById("adjustAverage").onclick = () => {
810 | this.speed = "average";
811 | document.getElementById("adjustSpeed").innerHTML = 'Speed: Average ';
812 | }
813 |
814 | document.getElementById("adjustSlow").onclick = () => {
815 | this.speed = "slow";
816 | document.getElementById("adjustSpeed").innerHTML = 'Speed: Slow ';
817 | }
818 |
819 | document.getElementById("startStairDemonstration").onclick = () => {
820 | this.clearWalls();
821 | this.clearPath("clickedButton");
822 | this.toggleButtons();
823 | stairDemonstration(this);
824 | mazeGenerationAnimations(this);
825 | }
826 |
827 |
828 | document.getElementById("startButtonBidirectional").onclick = () => {
829 | document.getElementById("startButtonStart").innerHTML = 'Visualize Bidirectional Swarm! '
830 | this.currentAlgorithm = "bidirectional";
831 | this.currentHeuristic = "manhattanDistance";
832 | if (this.numberOfObjects) {
833 | let objectNodeId = this.object;
834 | document.getElementById("startButtonAddObject").innerHTML = 'Add a Bomb ';
835 | document.getElementById(objectNodeId).className = "unvisited";
836 | this.object = null;
837 | this.numberOfObjects = 0;
838 | this.nodes[objectNodeId].status = "unvisited";
839 | this.isObject = false;
840 | }
841 | this.clearPath("clickedButton");
842 | this.changeStartNodeImages();
843 | }
844 |
845 | document.getElementById("startButtonDijkstra").onclick = () => {
846 | document.getElementById("startButtonStart").innerHTML = 'Visualize Dijkstra\'s! '
847 | this.currentAlgorithm = "dijkstra";
848 | this.changeStartNodeImages();
849 | }
850 |
851 | document.getElementById("startButtonAStar").onclick = () => {
852 | document.getElementById("startButtonStart").innerHTML = 'Visualize Swarm! '
853 | this.currentAlgorithm = "CLA";
854 | this.currentHeuristic = "manhattanDistance"
855 | this.changeStartNodeImages();
856 | }
857 |
858 | document.getElementById("startButtonAStar2").onclick = () => {
859 | document.getElementById("startButtonStart").innerHTML = 'Visualize A*! '
860 | this.currentAlgorithm = "astar";
861 | this.currentHeuristic = "poweredManhattanDistance"
862 | this.changeStartNodeImages();
863 | }
864 |
865 | document.getElementById("startButtonAStar3").onclick = () => {
866 | document.getElementById("startButtonStart").innerHTML = 'Visualize Convergent Swarm! '
867 | this.currentAlgorithm = "CLA";
868 | this.currentHeuristic = "extraPoweredManhattanDistance"
869 | this.changeStartNodeImages();
870 | }
871 |
872 | document.getElementById("startButtonGreedy").onclick = () => {
873 | document.getElementById("startButtonStart").innerHTML = 'Visualize Greedy! '
874 | this.currentAlgorithm = "greedy";
875 | this.changeStartNodeImages();
876 | }
877 |
878 | document.getElementById("startButtonBFS").onclick = () => {
879 | document.getElementById("startButtonStart").innerHTML = 'Visualize BFS! '
880 | this.currentAlgorithm = "bfs";
881 | this.clearWeights();
882 | this.changeStartNodeImages();
883 | }
884 |
885 | document.getElementById("startButtonDFS").onclick = () => {
886 | document.getElementById("startButtonStart").innerHTML = 'Visualize DFS! '
887 | this.currentAlgorithm = "dfs";
888 | this.clearWeights();
889 | this.changeStartNodeImages();
890 | }
891 |
892 | document.getElementById("startButtonCreateMazeOne").onclick = () => {
893 | this.clearWalls();
894 | this.clearPath("clickedButton");
895 | this.createMazeOne("wall");
896 | }
897 |
898 | document.getElementById("startButtonCreateMazeTwo").onclick = () => {
899 | this.clearWalls();
900 | this.clearPath("clickedButton");
901 | this.toggleButtons();
902 | recursiveDivisionMaze(this, 2, this.height - 3, 2, this.width - 3, "horizontal", false, "wall");
903 | mazeGenerationAnimations(this);
904 | }
905 |
906 | document.getElementById("startButtonCreateMazeWeights").onclick = () => {
907 | this.clearWalls();
908 | this.clearPath("clickedButton");
909 | this.createMazeOne("weight");
910 | }
911 |
912 | document.getElementById("startButtonClearBoard").onclick = () => {
913 | document.getElementById("startButtonAddObject").innerHTML = 'Add Bomb ';
914 |
915 |
916 |
917 | let navbarHeight = document.getElementById("navbarDiv").clientHeight;
918 | let textHeight = document.getElementById("mainText").clientHeight + document.getElementById("algorithmDescriptor").clientHeight;
919 | let height = Math.floor((document.documentElement.clientHeight - navbarHeight - textHeight) / 28);
920 | let width = Math.floor(document.documentElement.clientWidth / 25);
921 | let start = Math.floor(height / 2).toString() + "-" + Math.floor(width / 4).toString();
922 | let target = Math.floor(height / 2).toString() + "-" + Math.floor(3 * width / 4).toString();
923 |
924 | Object.keys(this.nodes).forEach(id => {
925 | let currentNode = this.nodes[id];
926 | let currentHTMLNode = document.getElementById(id);
927 | if (id === start) {
928 | currentHTMLNode.className = "start";
929 | currentNode.status = "start";
930 | } else if (id === target) {
931 | currentHTMLNode.className = "target";
932 | currentNode.status = "target"
933 | } else {
934 | currentHTMLNode.className = "unvisited";
935 | currentNode.status = "unvisited";
936 | }
937 | currentNode.previousNode = null;
938 | currentNode.path = null;
939 | currentNode.direction = null;
940 | currentNode.storedDirection = null;
941 | currentNode.distance = Infinity;
942 | currentNode.totalDistance = Infinity;
943 | currentNode.heuristicDistance = null;
944 | currentNode.weight = 0;
945 | currentNode.relatesToObject = false;
946 | currentNode.overwriteObjectRelation = false;
947 |
948 | });
949 | this.start = start;
950 | this.target = target;
951 | this.object = null;
952 | this.nodesToAnimate = [];
953 | this.objectNodesToAnimate = [];
954 | this.shortestPathNodesToAnimate = [];
955 | this.objectShortestPathNodesToAnimate = [];
956 | this.wallsToAnimate = [];
957 | this.mouseDown = false;
958 | this.pressedNodeStatus = "normal";
959 | this.previouslyPressedNodeStatus = null;
960 | this.previouslySwitchedNode = null;
961 | this.previouslySwitchedNodeWeight = 0;
962 | this.keyDown = false;
963 | this.algoDone = false;
964 | this.numberOfObjects = 0;
965 | this.isObject = false;
966 | }
967 |
968 | document.getElementById("startButtonClearWalls").onclick = () => {
969 | this.clearWalls();
970 | }
971 |
972 | document.getElementById("startButtonClearPath").onclick = () => {
973 | this.clearPath("clickedButton");
974 | }
975 |
976 | document.getElementById("startButtonCreateMazeThree").onclick = () => {
977 | this.clearWalls();
978 | this.clearPath("clickedButton");
979 | this.toggleButtons();
980 | otherMaze(this, 2, this.height - 3, 2, this.width - 3, "vertical", false);
981 | mazeGenerationAnimations(this);
982 | }
983 |
984 | document.getElementById("startButtonCreateMazeFour").onclick = () => {
985 | this.clearWalls();
986 | this.clearPath("clickedButton");
987 | this.toggleButtons();
988 | otherOtherMaze(this, 2, this.height - 3, 2, this.width - 3, "horizontal", false);
989 | mazeGenerationAnimations(this);
990 | }
991 |
992 | document.getElementById("startButtonAddObject").onclick = () => {
993 | let innerHTML = document.getElementById("startButtonAddObject").innerHTML;
994 | if (this.currentAlgorithm !== "bidirectional") {
995 | if (innerHTML.includes("Add")) {
996 | let r = Math.floor(this.height / 2);
997 | let c = Math.floor(2 * this.width / 4);
998 | let objectNodeId = `${r}-${c}`;
999 | if (this.target === objectNodeId || this.start === objectNodeId || this.numberOfObjects === 1) {
1000 | console.log("Failure to place object.");
1001 | } else {
1002 | document.getElementById("startButtonAddObject").innerHTML = 'Remove Bomb ';
1003 | this.clearPath("clickedButton");
1004 | this.object = objectNodeId;
1005 | this.numberOfObjects = 1;
1006 | this.nodes[objectNodeId].status = "object";
1007 | document.getElementById(objectNodeId).className = "object";
1008 | }
1009 | } else {
1010 | let objectNodeId = this.object;
1011 | document.getElementById("startButtonAddObject").innerHTML = 'Add Bomb ';
1012 | document.getElementById(objectNodeId).className = "unvisited";
1013 | this.object = null;
1014 | this.numberOfObjects = 0;
1015 | this.nodes[objectNodeId].status = "unvisited";
1016 | this.isObject = false;
1017 | this.clearPath("clickedButton");
1018 | }
1019 | }
1020 |
1021 | }
1022 |
1023 | document.getElementById("startButtonClearPath").className = "navbar-inverse navbar-nav";
1024 | document.getElementById("startButtonClearWalls").className = "navbar-inverse navbar-nav";
1025 | document.getElementById("startButtonClearBoard").className = "navbar-inverse navbar-nav";
1026 | if (this.currentAlgorithm !== "bidirectional") {
1027 | document.getElementById("startButtonAddObject").className = "navbar-inverse navbar-nav";
1028 | }
1029 | document.getElementById("startButtonCreateMazeOne").className = "navbar-inverse navbar-nav";
1030 | document.getElementById("startButtonCreateMazeTwo").className = "navbar-inverse navbar-nav";
1031 | document.getElementById("startButtonCreateMazeThree").className = "navbar-inverse navbar-nav";
1032 | document.getElementById("startButtonCreateMazeFour").className = "navbar-inverse navbar-nav";
1033 | document.getElementById("startButtonCreateMazeWeights").className = "navbar-inverse navbar-nav";
1034 | document.getElementById("startStairDemonstration").className = "navbar-inverse navbar-nav";
1035 | document.getElementById("startButtonDFS").className = "navbar-inverse navbar-nav";
1036 | document.getElementById("startButtonBFS").className = "navbar-inverse navbar-nav";
1037 | document.getElementById("startButtonDijkstra").className = "navbar-inverse navbar-nav";
1038 | document.getElementById("startButtonAStar").className = "navbar-inverse navbar-nav";
1039 | document.getElementById("startButtonAStar2").className = "navbar-inverse navbar-nav";
1040 | document.getElementById("startButtonAStar3").className = "navbar-inverse navbar-nav";
1041 | document.getElementById("adjustFast").className = "navbar-inverse navbar-nav";
1042 | document.getElementById("adjustAverage").className = "navbar-inverse navbar-nav";
1043 | document.getElementById("adjustSlow").className = "navbar-inverse navbar-nav";
1044 | document.getElementById("startButtonBidirectional").className = "navbar-inverse navbar-nav";
1045 | document.getElementById("startButtonGreedy").className = "navbar-inverse navbar-nav";
1046 | document.getElementById("actualStartButton").style.backgroundColor = "";
1047 |
1048 | } else {
1049 | this.buttonsOn = false;
1050 | document.getElementById("startButtonDFS").onclick = null;
1051 | document.getElementById("startButtonBFS").onclick = null;
1052 | document.getElementById("startButtonDijkstra").onclick = null;
1053 | document.getElementById("startButtonAStar").onclick = null;
1054 | document.getElementById("startButtonGreedy").onclick = null;
1055 | document.getElementById("startButtonAddObject").onclick = null;
1056 | document.getElementById("startButtonAStar2").onclick = null;
1057 | document.getElementById("startButtonAStar3").onclick = null;
1058 | document.getElementById("startButtonBidirectional").onclick = null;
1059 | document.getElementById("startButtonCreateMazeOne").onclick = null;
1060 | document.getElementById("startButtonCreateMazeTwo").onclick = null;
1061 | document.getElementById("startButtonCreateMazeThree").onclick = null;
1062 | document.getElementById("startButtonCreateMazeFour").onclick = null;
1063 | document.getElementById("startButtonCreateMazeWeights").onclick = null;
1064 | document.getElementById("startStairDemonstration").onclick = null;
1065 | document.getElementById("startButtonClearPath").onclick = null;
1066 | document.getElementById("startButtonClearWalls").onclick = null;
1067 | document.getElementById("startButtonClearBoard").onclick = null;
1068 | document.getElementById("startButtonStart").onclick = null;
1069 | document.getElementById("adjustFast").onclick = null;
1070 | document.getElementById("adjustAverage").onclick = null;
1071 | document.getElementById("adjustSlow").onclick = null;
1072 |
1073 | document.getElementById("adjustFast").className = "navbar-inverse navbar-nav disabledA";
1074 | document.getElementById("adjustAverage").className = "navbar-inverse navbar-nav disabledA";
1075 | document.getElementById("adjustSlow").className = "navbar-inverse navbar-nav disabledA";
1076 | document.getElementById("startButtonClearPath").className = "navbar-inverse navbar-nav disabledA";
1077 | document.getElementById("startButtonClearWalls").className = "navbar-inverse navbar-nav disabledA";
1078 | document.getElementById("startButtonClearBoard").className = "navbar-inverse navbar-nav disabledA";
1079 | document.getElementById("startButtonAddObject").className = "navbar-inverse navbar-nav disabledA";
1080 | document.getElementById("startButtonCreateMazeOne").className = "navbar-inverse navbar-nav disabledA";
1081 | document.getElementById("startButtonCreateMazeTwo").className = "navbar-inverse navbar-nav disabledA";
1082 | document.getElementById("startButtonCreateMazeThree").className = "navbar-inverse navbar-nav disabledA";
1083 | document.getElementById("startButtonCreateMazeFour").className = "navbar-inverse navbar-nav disabledA";
1084 | document.getElementById("startButtonCreateMazeWeights").className = "navbar-inverse navbar-nav disabledA";
1085 | document.getElementById("startStairDemonstration").className = "navbar-inverse navbar-nav disabledA";
1086 | document.getElementById("startButtonDFS").className = "navbar-inverse navbar-nav disabledA";
1087 | document.getElementById("startButtonBFS").className = "navbar-inverse navbar-nav disabledA";
1088 | document.getElementById("startButtonDijkstra").className = "navbar-inverse navbar-nav disabledA";
1089 | document.getElementById("startButtonAStar").className = "navbar-inverse navbar-nav disabledA";
1090 | document.getElementById("startButtonGreedy").className = "navbar-inverse navbar-nav disabledA";
1091 | document.getElementById("startButtonAStar2").className = "navbar-inverse navbar-nav disabledA";
1092 | document.getElementById("startButtonAStar3").className = "navbar-inverse navbar-nav disabledA";
1093 | document.getElementById("startButtonBidirectional").className = "navbar-inverse navbar-nav disabledA";
1094 |
1095 | document.getElementById("actualStartButton").style.backgroundColor = "rgb(185, 15, 15)";
1096 | }
1097 |
1098 |
1099 | }
1100 |
1101 | let navbarHeight = $("#navbarDiv").height();
1102 | let textHeight = $("#mainText").height() + $("#algorithmDescriptor").height();
1103 | let height = Math.floor(($(document).height() - navbarHeight - textHeight) / 28);
1104 | let width = Math.floor($(document).width() / 25);
1105 | let newBoard = new Board(height, width)
1106 | newBoard.initialise();
1107 |
1108 | window.onkeydown = (e) => {
1109 | newBoard.keyDown = e.keyCode;
1110 | }
1111 |
1112 | window.onkeyup = (e) => {
1113 | newBoard.keyDown = false;
1114 | }
--------------------------------------------------------------------------------
/public/browser/bomb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/browser/bomb.png
--------------------------------------------------------------------------------
/public/browser/dragging.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/browser/dragging.gif
--------------------------------------------------------------------------------
/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 | export default getDistance;
--------------------------------------------------------------------------------
/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 | export default 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 | export default recursiveDivisionMaze;
--------------------------------------------------------------------------------
/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 | export default recursiveDivisionMaze;
--------------------------------------------------------------------------------
/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 | export default simpleDemonstration;
--------------------------------------------------------------------------------
/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 | export default stairDemonstration;
--------------------------------------------------------------------------------
/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 | export default weightsDemonstration;
--------------------------------------------------------------------------------
/public/browser/pathfindingalgorithm/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 | export default astar;
--------------------------------------------------------------------------------
/public/browser/pathfindingalgorithm/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/pathfindingalgorithm/testAlgorithms.js:
--------------------------------------------------------------------------------
1 | function test(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].direction = "up";
7 | let unvisitedNodes = Object.keys(nodes);
8 | while (unvisitedNodes.length) {
9 | let currentNode = closestNode(nodes, unvisitedNodes);
10 | while (currentNode.status === "wall" && unvisitedNodes.length) {
11 | currentNode = closestNode(nodes, unvisitedNodes)
12 | }
13 | if (currentNode.distance === Infinity) return false;
14 | currentNode.status = "visited";
15 | if (currentNode.id === target) {
16 | while (currentNode.id !== start) {
17 | nodesToAnimate.unshift(currentNode);
18 | currentNode = nodes[currentNode.previousNode];
19 | }
20 | return "success!";
21 | }
22 | if (name === "astar" || 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 === "astar") {
67 | if (heuristic === "manhattanDistance") {
68 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0] + manhattanDistance(targetNode, actualTargetNode);
69 | } else if (heuristic === "poweredManhattanDistance") {
70 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0] + Math.pow(manhattanDistance(targetNode, actualTargetNode), 3);
71 | } else if (heuristic === "extraPoweredManhattanDistance") {
72 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0] + Math.pow(manhattanDistance(targetNode, actualTargetNode), 5);
73 | }
74 | let startNodeManhattanDistance = manhattanDistance(actualStartNode, actualTargetNode);
75 | } else if (actualTargetNode && name === "greedy") {
76 | distanceToCompare = targetNode.weight + distance[0] + manhattanDistance(targetNode, actualTargetNode);
77 | } else {
78 | distanceToCompare = currentNode.distance + targetNode.weight + distance[0];
79 | }
80 | if (distanceToCompare < targetNode.distance) {
81 | targetNode.distance = distanceToCompare;
82 | targetNode.previousNode = currentNode.id;
83 | targetNode.path = distance[1];
84 | targetNode.direction = distance[2];
85 | }
86 | }
87 |
88 | function getNeighbors(id, nodes, boardArray) {
89 | let coordinates = id.split("-");
90 | let x = parseInt(coordinates[0]);
91 | let y = parseInt(coordinates[1]);
92 | let neighbors = [];
93 | let potentialNeighbor;
94 | if (boardArray[x - 1] && boardArray[x - 1][y]) {
95 | potentialNeighbor = `${(x - 1).toString()}-${y.toString()}`
96 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
97 | }
98 | if (boardArray[x + 1] && boardArray[x + 1][y]) {
99 | potentialNeighbor = `${(x + 1).toString()}-${y.toString()}`
100 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
101 | }
102 | if (boardArray[x][y - 1]) {
103 | potentialNeighbor = `${x.toString()}-${(y - 1).toString()}`
104 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
105 | }
106 | if (boardArray[x][y + 1]) {
107 | potentialNeighbor = `${x.toString()}-${(y + 1).toString()}`
108 | if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
109 | }
110 | return neighbors;
111 | }
112 |
113 |
114 | function getDistance(nodeOne, nodeTwo) {
115 | let currentCoordinates = nodeOne.id.split("-");
116 | let targetCoordinates = nodeTwo.id.split("-");
117 | let x1 = parseInt(currentCoordinates[0]);
118 | let y1 = parseInt(currentCoordinates[1]);
119 | let x2 = parseInt(targetCoordinates[0]);
120 | let y2 = parseInt(targetCoordinates[1]);
121 | if (x2 < x1) {
122 | if (nodeOne.direction === "up") {
123 | return [1, ["f"], "up"];
124 | } else if (nodeOne.direction === "right") {
125 | return [2, ["l", "f"], "up"];
126 | } else if (nodeOne.direction === "left") {
127 | return [2, ["r", "f"], "up"];
128 | } else if (nodeOne.direction === "down") {
129 | return [3, ["r", "r", "f"], "up"];
130 | }
131 | } else if (x2 > x1) {
132 | if (nodeOne.direction === "up") {
133 | return [3, ["r", "r", "f"], "down"];
134 | } else if (nodeOne.direction === "right") {
135 | return [2, ["r", "f"], "down"];
136 | } else if (nodeOne.direction === "left") {
137 | return [2, ["l", "f"], "down"];
138 | } else if (nodeOne.direction === "down") {
139 | return [1, ["f"], "down"];
140 | }
141 | }
142 | if (y2 < y1) {
143 | if (nodeOne.direction === "up") {
144 | return [2, ["l", "f"], "left"];
145 | } else if (nodeOne.direction === "right") {
146 | return [3, ["l", "l", "f"], "left"];
147 | } else if (nodeOne.direction === "left") {
148 | return [1, ["f"], "left"];
149 | } else if (nodeOne.direction === "down") {
150 | return [2, ["r", "f"], "left"];
151 | }
152 | } else if (y2 > y1) {
153 | if (nodeOne.direction === "up") {
154 | return [2, ["r", "f"], "right"];
155 | } else if (nodeOne.direction === "right") {
156 | return [1, ["f"], "right"];
157 | } else if (nodeOne.direction === "left") {
158 | return [3, ["r", "r", "f"], "right"];
159 | } else if (nodeOne.direction === "down") {
160 | return [2, ["l", "f"], "right"];
161 | }
162 | }
163 | }
164 |
165 | function manhattanDistance(nodeOne, nodeTwo) {
166 | let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele));
167 | let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele));
168 | let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]);
169 | let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]);
170 | return (xChange + yChange);
171 | }
172 |
173 | export default test;
174 |
175 |
--------------------------------------------------------------------------------
/public/browser/pathfindingalgorithm/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 | export default unweightedSearchAlgorithm;
--------------------------------------------------------------------------------
/public/browser/pathfindingalgorithm/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/browser/walls.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/browser/walls.gif
--------------------------------------------------------------------------------
/public/img/login/imac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/img/login/imac.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Pathfinding Visualizer
4 |
5 |
6 |
7 |
17 |
18 |
19 |
70 |
71 |
72 |
73 |
Welcome to Pathfinding Visualizer!
74 |
This short tutorial will walk you through all of the features of this application.
75 |
If you want to dive right in, feel free to press the "Skip Tutorial" button below. Otherwise, press "Next"!
76 |
1/9
77 |
78 |
79 |
Next
80 |
Previous
81 |
Skip Tutorial
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
Start Node
90 |
91 |
Target Node
92 |
93 |
Bomb Node
94 |
95 |
Weight Node
96 |
97 |
Unvisited Node
98 |
99 |
Visited Nodes
100 |
101 |
Shortest-path Node
102 |
103 |
Wall Node
104 |
105 |
106 |
Pick an algorithm and visualize it!
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/styling/bomb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/bomb.png
--------------------------------------------------------------------------------
/public/styling/c_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/c_icon.png
--------------------------------------------------------------------------------
/public/styling/circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/circle.png
--------------------------------------------------------------------------------
/public/styling/diamond.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/diamond.png
--------------------------------------------------------------------------------
/public/styling/dragging.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/dragging.gif
--------------------------------------------------------------------------------
/public/styling/spaceship.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/spaceship.png
--------------------------------------------------------------------------------
/public/styling/triangle-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/triangle-down.png
--------------------------------------------------------------------------------
/public/styling/triangle-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/triangle-left.png
--------------------------------------------------------------------------------
/public/styling/triangle-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/triangle-right.png
--------------------------------------------------------------------------------
/public/styling/triangle-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/triangle-up.png
--------------------------------------------------------------------------------
/public/styling/walls.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/walls.gif
--------------------------------------------------------------------------------
/public/styling/weight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VardaPanchal005/Pathfinding-Visualizer/a7cdcd726b4a7329fabae3e0ff9c42b64ad522b1/public/styling/weight.png
--------------------------------------------------------------------------------
/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 | });
--------------------------------------------------------------------------------
/src/index.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;
--------------------------------------------------------------------------------