Skip to content

Commit cf9ac6a

Browse files
committed
A*, Dijkstra, DFS, BFS, Maze Generator visualizer
Ground work done Final touch up left
0 parents  commit cf9ac6a

File tree

5 files changed

+797
-0
lines changed

5 files changed

+797
-0
lines changed

algorithms.js

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
let dirIndication = ["r", "d", "l", "u"]
2+
3+
let sleep = (sec = 100) => {
4+
return new Promise(x => setTimeout(x, sec))
5+
}
6+
7+
async function createMazeGoTo(node, from, isVisited = new Map()){
8+
node.addClass("cur")
9+
node.removeClass("wall")
10+
let fromdir = {"left" : 0, "up" : 1, "right" : 2, "down" : 3}
11+
let todir = {0 : "right", 1 : "down", 2 : "left", 3 : "up"}
12+
13+
let shuffle = (arr) => {
14+
let n = arr.length
15+
for(let i = n - 1 ; i >= 0 ; i--){
16+
let rand = Math.floor(Math.random() * (i + 1))
17+
let temp = arr[i]
18+
arr[i] = arr[rand]
19+
arr[rand] = temp
20+
}
21+
return arr
22+
}
23+
24+
if(!isVisited[node]){
25+
isVisited.set(node, true)
26+
node.removeWall(fromdir[from])
27+
let random = shuffle([0,1,2,3])
28+
for(let i = 0 ; i < random.length ; ++i){
29+
30+
31+
await sleep(1)
32+
33+
34+
if(node.neighbors[random[i]] && !isVisited.get(node.neighbors[random[i]])){
35+
node.removeWall(random[i])
36+
node.removeClass("cur")
37+
await createMazeGoTo(node.neighbors[random[i]], todir[random[i]], isVisited)
38+
node.addClass("cur")
39+
}
40+
}
41+
}
42+
43+
node.removeClass("cur")
44+
}
45+
46+
47+
async function BFSsearch(maze, start, end, isDone = new Map(), parent = new Map(), dis = new Map(), dir = new Map()){
48+
49+
let queue = new Array()
50+
queue.push(start)
51+
dis.set(start, 0)
52+
parent.set(start, undefined)
53+
54+
while(queue.length){
55+
let node = queue.shift()
56+
if(!isDone.get(node)){
57+
58+
await sleep(10)
59+
isDone.set(node, true)
60+
node.addClass("searching")
61+
62+
if(node == end){
63+
await backtrack(end, start, parent, dir)
64+
break;
65+
}
66+
67+
68+
for(let i = 0 ; i < node.sides ; ++i){
69+
if(!node.border[i] && node.neighbors[i] && !isDone.get(node.neighbors[i])){
70+
if(!dis.get(node.neighbors[i]) || dis.get(node.neighbors[i]) > dis.get(node) + 1){
71+
parent.set(node.neighbors[i], node)
72+
dis.set(node.neighbors[i], dis[node] + 1)
73+
dir.set(node.neighbors[i], dirIndication[i])
74+
}
75+
queue.push(node.neighbors[i])
76+
}
77+
}
78+
}
79+
}
80+
}
81+
82+
async function dijkstraSearch(maze, start, end, isDone = new Map(), parent = new Map(), dis = new Map(), dir = new Map()){
83+
84+
// start.addClass("startNode")
85+
// end.addClass("endNode")
86+
87+
let queue = new Array()
88+
queue.push(start)
89+
dis.set(start, 0)
90+
parent.set(start, undefined)
91+
92+
while(queue.length){
93+
let node = queue.shift()
94+
if(!isDone.get(node)){
95+
await sleep(10)
96+
isDone.set(node, true)
97+
node.addClass("searching")
98+
99+
if(node == end){
100+
await backtrack(end, start, parent, dir)
101+
break;
102+
}
103+
104+
105+
for(let i = 0 ; i < node.sides ; ++i){
106+
if(!node.border[i] && node.neighbors[i] && !isDone.get(node.neighbors[i])){
107+
if(!dis.get(node.neighbors[i]) || dis.get(node.neighbors[i]) > dis.get(node) + 1){
108+
parent.set(node.neighbors[i], node)
109+
dis.set(node.neighbors[i], dis[node] + 1)
110+
dir.set(node.neighbors[i], dirIndication[i])
111+
}
112+
queue.push(node.neighbors[i])
113+
}
114+
}
115+
}
116+
}
117+
}
118+
119+
async function AstarSearch(maze, start, end, isDone = new Map(), gscore = new Map(), fscore = new Map(), parent = new Map(), dir = new Map()){
120+
let dis = (x, y) => {
121+
return Math.hypot(x.i - y.i, x.j - y.j)
122+
}
123+
124+
let getMin = (openSet, fscore) => {
125+
let min = Math.min(...openSet.map(el => fscore.get(el)))
126+
127+
for(let i = 0 ; i < openSet.length ; i++){
128+
if(fscore.get(openSet[i]) == min){
129+
let ans = openSet[i]
130+
openSet.splice(i, 1)
131+
return ans
132+
}
133+
}
134+
}
135+
136+
let openSet = []
137+
138+
openSet.push(start)
139+
140+
parent.set(start, undefined)
141+
gscore.set(start, 0)
142+
fscore.set(start, dis(start, end))
143+
isDone.set(start, true)
144+
while(openSet.length){
145+
let curr = getMin(openSet, fscore)
146+
curr.addClass("searching")
147+
if(curr == end){
148+
await backtrack(end, start, parent, dir)
149+
return
150+
}
151+
152+
isDone.set(curr, true)
153+
154+
for(let i = 0 ; i < curr.border.length ; ++i){
155+
await sleep(10)
156+
157+
let n = curr.neighbors[i]
158+
if(n && !curr.border[i] && !isDone.get(n)){
159+
let tempG = gscore.get(curr) + dis(curr, n)
160+
161+
if(!gscore.get(n) || tempG < gscore.get(n)){
162+
parent.set(n, curr)
163+
dir.set(n, dirIndication[i])
164+
gscore.set(n, tempG)
165+
fscore.set(n, gscore.get(n) + dis(n, end))
166+
167+
}
168+
169+
if(!(openSet.find(el => el == n))) {
170+
openSet.push(n)
171+
}
172+
}
173+
}
174+
}
175+
}
176+
177+
async function DFSsearch(maze, node, end, isDone = new Map(), parent = new Map(), dis = new Map(), dir = new Map()){
178+
if(node == end){
179+
await backtrack(end, start, parent, dir)
180+
return true
181+
}
182+
if(isDone.get(node)){
183+
return
184+
}
185+
isDone.set(node, true)
186+
187+
node.addClass("searching")
188+
await sleep(10)
189+
190+
for(let i = 0; i < node.sides ; ++i){
191+
if(!node.border[i] && node.neighbors[i] && !isDone.get(node.neighbors[i])){
192+
if((!dis.get(node) || dis.get(node[i]) + 1 > dis.get(node.neighbors[i]))){
193+
dis.set(node.neighbors[i], dis.get(node) + 1)
194+
parent.set(node.neighbors[i], node)
195+
dir.set(node.neighbors[i], dirIndication[i])
196+
}
197+
if(await DFSsearch(maze, node.neighbors[i], end, isDone, parent ,dis, dir))
198+
return true
199+
}
200+
}
201+
}
202+
203+
async function backtrack(end, start, parent, dir){
204+
let node = end
205+
let to = ""
206+
while(node){
207+
await sleep(10)
208+
209+
node.removeClass("searching")
210+
node.addClass("inWay")
211+
node.el.innerHTML = to
212+
switch(dir.get(node)){
213+
case "u":
214+
to = '🠋'
215+
break;
216+
case "d":
217+
to = '🠉'
218+
break;
219+
case "r":
220+
to = '🠈'
221+
break;
222+
case "l":
223+
to = '🠊'
224+
break;
225+
}
226+
227+
node = parent.get(node)
228+
}
229+
230+
}

index.html

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Maze Path Finding Algo</title>
8+
<link rel="stylesheet" href="style.css">
9+
</head>
10+
<body>
11+
12+
<div id="inputSection">
13+
<!-- <button class ="button" onclick = "start()">Start</button> -->
14+
<!-- <section>
15+
<label for="nums">Enter nums:</label>
16+
<div style="position: relative;height: 56px">
17+
<input type="range" min = "10" max = "1000" id= "range" value = "35" oninput="updateView();updateInput()" disabled = true/>
18+
<div id="rangeSelector">
19+
<div id = "selectorBtn"></div>
20+
<div id = "rangeDisplayer"></div>
21+
</div>
22+
</div>
23+
<script>
24+
updateView()
25+
function updateView(){
26+
var slider = document.getElementById("range")
27+
var selector = document.getElementById("rangeSelector")
28+
var display = document.getElementById("rangeDisplayer")
29+
selector.style.left = (slider.value - 10)/990 * 100 + '%';
30+
display.innerHTML = slider.value
31+
}
32+
</script>
33+
</section> -->
34+
<section>
35+
<label for="algoUsed" >Algorithm Used</label>
36+
<select class ="select" name = "algoUsed" id = "algoUsed" oninput="updateInput()">
37+
<option value = "bfs" >BFS search</option>
38+
<option value = "dijkstra">dijkstra search</option>
39+
<option value = "astar" selected>A* search</option>
40+
<option value = "dfs" >DFS search</option>
41+
</select>
42+
</section>
43+
<section>
44+
<button id = "start" class = "button" onclick = "start()">start</button>
45+
<button id = "generateMaze" class = "button" onclick = "generateMaze()">generate Maze</button>
46+
<button id = "clearMaze" class = "button" onclick = "clearMaze()">clear Maze</button>
47+
<button id = "clearMaze" class = "button" onclick = "newMaze()">new Maze</button>
48+
</section>
49+
</div>
50+
51+
<div id="mazeContainer"><table id="maze"> </table></div>
52+
53+
<script src="obj.js"></script>
54+
<script src="algorithms.js"></script>
55+
<script src="main.js"></script>
56+
</body>
57+
</html>

main.js

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
let maze
2+
3+
// const rowCount = 20
4+
// const colCount = 62
5+
let startNode, endNode
6+
7+
const rowCount = 20
8+
const colCount = 20
9+
10+
let getId = (i, j) => {
11+
return i +"-"+j
12+
}
13+
let getrowId = (i) =>{
14+
return "row " + i
15+
}
16+
let getStyle = (i, j, rows, cols) => {
17+
return ""
18+
}
19+
20+
preCompute()
21+
function preCompute(){
22+
23+
maze = new Maze(rowCount, colCount)
24+
maze.setStartNode(Math.floor(rowCount/4),Math.floor(colCount/4))
25+
maze.setEndNode(Math.floor(rowCount/4),Math.floor(3 *colCount/4))
26+
// generateMaze()
27+
}
28+
29+
30+
31+
32+
function start(){
33+
let algoUsed = document.getElementById("algoUsed").value
34+
switch(algoUsed){
35+
case "bfs":
36+
BFSsearch(maze, maze.startNode, maze.endNode)
37+
break
38+
case "dijkstra":
39+
dijkstraSearch(maze, maze.startNode, maze.endNode)
40+
break
41+
case "astar":
42+
AstarSearch(maze, maze.startNode, maze.endNode)
43+
break
44+
case "dfs":
45+
DFSsearch(maze, maze.startNode, maze.endNode)
46+
break
47+
48+
}
49+
}
50+
51+
function updateInput(){
52+
53+
}
54+
55+
function generateMaze(){
56+
newMaze()
57+
maze.matrix.forEach(row => {
58+
row.forEach(node => {
59+
node.addWall(0)
60+
node.addWall(1)
61+
node.addWall(2)
62+
node.addWall(3)
63+
node.addClass("wall")
64+
})
65+
})
66+
createMazeGoTo(maze.matrix[0][0], "up")
67+
}
68+
69+
// function to clear all visited non visited for new algorithm
70+
function clearMaze(){
71+
maze.clearMaze()
72+
}
73+
74+
function newMaze(){
75+
maze.newMaze()
76+
maze.setStartNode(Math.floor(rowCount/4),Math.floor(colCount/4))
77+
maze.setEndNode(Math.floor(rowCount/4),Math.floor(3 *colCount/4))
78+
}
79+
80+

0 commit comments

Comments
 (0)