2023-08-25 05:48:41 +00:00
|
|
|
function spawnPathfinder(root) {
|
|
|
|
|
|
|
|
class Cell
|
|
|
|
{
|
|
|
|
is_state(state){return this.State == state}
|
|
|
|
reset(){this.make_state("null")}
|
|
|
|
make_state(state){this.State = state;}
|
|
|
|
draw(grid){
|
|
|
|
grid.children[this.Y].children[this.X].classList.remove("animate")
|
|
|
|
grid.children[this.Y].children[this.X].classList = "cell " + this.State + ` x${this.X}-y${this.Y}`;
|
|
|
|
grid.children[this.Y].children[this.X].style.backgroundPosition = `${this.X * horizontal_cells * 0}px ${this.Y * vertical_cells / 4}px`;
|
|
|
|
if(this.State != "null") grid.children[this.Y].children[this.X].classList.add("animate");
|
|
|
|
|
|
|
|
}
|
|
|
|
update_neighbours(grid, diagonal=true)
|
|
|
|
{
|
|
|
|
this.neighbours = []
|
|
|
|
let space_top = this.Y > 0 && grid[this.Y-1][this.X];
|
|
|
|
let space_down = this.Y < vertical_cells - 1 && grid[this.Y+1][this.X];
|
|
|
|
let space_left = this.X > 0 && grid[this.Y][this.X-1];
|
|
|
|
let space_right = this.X < horizontal_cells - 1 && grid[this.Y][this.X+1];
|
|
|
|
|
|
|
|
let shiftXR = this.Y % 2 === 0 ? 0 : 1;
|
|
|
|
let shiftXL = this.Y % 2 === 0 ? 1 : 0;
|
|
|
|
|
|
|
|
let left = space_left && !grid[this.Y][this.X - 1].is_state("wall");
|
|
|
|
let right = space_right && !grid[this.Y][this.X + 1].is_state("wall");
|
|
|
|
|
|
|
|
let top_right = space_top && grid[this.Y-1][this.X + 1 - shiftXR] && !grid[this.Y-1][this.X + 1 - shiftXR].is_state("wall");
|
|
|
|
let top_left = space_top && grid[this.Y-1][this.X - 1 + shiftXL] && !grid[this.Y-1][this.X - 1 + shiftXL].is_state("wall");
|
|
|
|
let down_right = space_down && grid[this.Y+1][this.X + 1 - shiftXR] && !grid[this.Y+1][this.X + 1 - shiftXR].is_state("wall");
|
|
|
|
let down_left = space_down && grid[this.Y+1][this.X - 1 + shiftXL] && !grid[this.Y+1][this.X - 1 + shiftXL].is_state("wall");
|
|
|
|
|
|
|
|
|
|
|
|
if(top_right) this.neighbours.push(grid[this.Y-1][this.X + 1 - shiftXR]);
|
|
|
|
|
|
|
|
if(top_left) this.neighbours.push(grid[this.Y-1][this.X - 1 + shiftXL]);
|
|
|
|
|
|
|
|
if(down_right) this.neighbours.push(grid[this.Y+1][this.X + 1 - shiftXR]);
|
|
|
|
|
|
|
|
if(down_left) this.neighbours.push(grid[this.Y+1][this.X - 1 + shiftXL]);
|
|
|
|
|
|
|
|
if(right) this.neighbours.push(grid[this.Y][this.X+1]);
|
|
|
|
|
|
|
|
if(left) this.neighbours.push(grid[this.Y][this.X-1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
update_wall_neighbours(grid,space=1)
|
|
|
|
{
|
|
|
|
this.wall_neighbours = []
|
|
|
|
|
|
|
|
let space_top = this.Y > space;
|
|
|
|
let space_down = this.Y < vertical_cells - 1 - space;
|
|
|
|
let space_left = this.X > space;
|
|
|
|
let space_right = this.X < horizontal_cells - 1 - space;
|
|
|
|
|
|
|
|
if(space_top) this.wall_neighbours.push(grid[this.Y-1-space][this.X]);// UP
|
|
|
|
|
|
|
|
if(space_down) this.wall_neighbours.push(grid[this.Y+1+space][this.X]);// DOWN
|
|
|
|
|
|
|
|
if(space_right) this.wall_neighbours.push(grid[this.Y][this.X+1+space]);// RIGHT
|
|
|
|
|
|
|
|
if(space_left) this.wall_neighbours.push(grid[this.Y][this.X-1-space]);// LEFT
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(X,Y)
|
|
|
|
{
|
|
|
|
this.X = X;
|
|
|
|
this.Y = Y;
|
|
|
|
this.State = "null"
|
|
|
|
this.neighbours = []
|
|
|
|
this.wall_neighbours = []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Main grid
|
|
|
|
let grid = [];
|
|
|
|
|
|
|
|
// Grid element
|
|
|
|
let body = root;
|
|
|
|
let grid_HTML = root.getElementsByClassName("grid")[0];
|
|
|
|
let navbar_HTML = root.getElementsByClassName("navbar")[0];
|
|
|
|
let speed_range = root.getElementsByClassName("range-speed")[0];
|
|
|
|
let algo_select = root.getElementsByClassName("algo-select")[0];
|
|
|
|
let maze_algo_select = root.getElementsByClassName("maze-algo-select")[0];
|
|
|
|
|
2023-08-30 17:39:31 +00:00
|
|
|
const window_y = (body.scrollHeight - navbar_HTML.scrollHeight);
|
2023-08-25 05:48:41 +00:00
|
|
|
const window_x = body.scrollWidth;
|
|
|
|
|
|
|
|
let horizontal_cells;
|
|
|
|
let vertical_cells;
|
|
|
|
let tile_size ;
|
|
|
|
let placing_tiles = false;
|
|
|
|
let erasing_tiles = false;
|
|
|
|
let dragging_tile = false;
|
|
|
|
let dragged_tile = "";
|
|
|
|
let is_running = false;
|
|
|
|
|
|
|
|
|
|
|
|
tile_size = "big"
|
|
|
|
horizontal_cells = Math.floor(window_x / 35);
|
|
|
|
vertical_cells = Math.floor(window_y / 35);
|
|
|
|
|
|
|
|
horizontal_cells = Math.floor( (window_x - horizontal_cells) /35);
|
2023-08-30 17:39:31 +00:00
|
|
|
vertical_cells = Math.floor( (window_y - vertical_cells) / 35 * 0.9);
|
2023-08-25 05:48:41 +00:00
|
|
|
|
|
|
|
grid_HTML.style.width = `${horizontal_cells * 35}px`;
|
|
|
|
grid_HTML.style.height = `${vertical_cells * 30}px`;
|
|
|
|
|
|
|
|
|
|
|
|
// Start and End nodes
|
2023-09-26 19:38:14 +00:00
|
|
|
let start_node = [Math.floor(horizontal_cells*0.25) , Math.floor(vertical_cells/2)];
|
|
|
|
let end_node = [Math.floor(horizontal_cells*0.75) , Math.floor(vertical_cells/2)];
|
2023-08-25 05:48:41 +00:00
|
|
|
|
2023-09-26 19:38:14 +00:00
|
|
|
let start_node_initial = [Math.floor(horizontal_cells*0.2) , Math.floor(vertical_cells/2)]
|
|
|
|
let end_node_initial = [Math.floor(horizontal_cells*0.8) , Math.floor(vertical_cells/2)]
|
2023-08-25 05:48:41 +00:00
|
|
|
|
|
|
|
// Populating grid
|
|
|
|
for(var i = 0; i < vertical_cells; i++)
|
|
|
|
{
|
|
|
|
let row = [];
|
|
|
|
let odd = i % 2 === 0;
|
|
|
|
var row_HTML = document.createElement("div");
|
|
|
|
row_HTML.classList = "row " + tile_size + (odd ? " odd" : "");
|
|
|
|
for(var j = 0; j < horizontal_cells; j++)
|
|
|
|
{
|
|
|
|
let cell = new Cell(j,i);
|
|
|
|
row.push(cell);
|
|
|
|
|
|
|
|
var cell_HTML = document.createElement("div");
|
|
|
|
cell_HTML.classList.add("cell");
|
|
|
|
row_HTML.appendChild(cell_HTML);
|
|
|
|
}
|
|
|
|
grid.push(row);
|
|
|
|
|
|
|
|
grid_HTML.appendChild(row_HTML);
|
|
|
|
}
|
|
|
|
// Setting tile placing
|
|
|
|
for(var i = 0; i < vertical_cells; i++)
|
|
|
|
{
|
|
|
|
for(var j = 0; j < horizontal_cells; j++)
|
|
|
|
{
|
|
|
|
grid[i][j].draw(grid_HTML);
|
|
|
|
grid_HTML.children[i].children[j].X=j;
|
|
|
|
grid_HTML.children[i].children[j].Y=i;
|
|
|
|
|
|
|
|
grid_HTML.children[i].children[j].onmouseover = function()
|
|
|
|
{
|
|
|
|
if(placing_tiles) PlaceTile(this.X,this.Y);
|
|
|
|
else if(erasing_tiles) ResetTile(this.X,this.Y);
|
|
|
|
|
|
|
|
if(dragging_tile) this.classList = "cell "+dragged_tile
|
|
|
|
} ;
|
|
|
|
grid_HTML.children[i].children[j].onmouseleave = function()
|
|
|
|
{
|
|
|
|
if(dragged_tile) grid[this.Y][this.X].draw(grid_HTML);
|
|
|
|
}
|
|
|
|
grid_HTML.children[i].children[j].onmousedown = function() {
|
|
|
|
if(this.X == start_node[0] && this.Y == start_node[1] || this.X == end_node[0] && this.Y == end_node[1]){
|
|
|
|
dragging_tile = true;
|
|
|
|
dragged_tile = grid[this.Y][this.X].State;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
if(grid[this.Y][this.X].is_state("wall")) erasing_tiles = true;
|
|
|
|
else placing_tiles = true;
|
|
|
|
|
|
|
|
if(placing_tiles) PlaceTile(this.X,this.Y);
|
|
|
|
if(erasing_tiles) ResetTile(this.X,this.Y)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
grid_HTML.children[i].children[j].onmouseup = function() {
|
|
|
|
if(dragging_tile)
|
|
|
|
{
|
|
|
|
dragging_tile = false;
|
|
|
|
PlaceTile(this.X,this.Y,dragged_tile)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
grid_HTML.children[i].children[j].ondragstart = function(){return false};
|
|
|
|
grid_HTML.children[i].children[j].ondrop = function(){return false};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
grid_HTML.onmouseup = function() {
|
|
|
|
placing_tiles = false
|
|
|
|
erasing_tiles = false
|
|
|
|
}
|
|
|
|
|
|
|
|
PlaceTile(start_node_initial[0],start_node_initial[1],"start")
|
|
|
|
PlaceTile(end_node_initial[0],end_node_initial[1],"end")
|
|
|
|
|
|
|
|
async function SelectChange(select)
|
|
|
|
{
|
|
|
|
if(select == "maze-algo-select")
|
|
|
|
{
|
|
|
|
await RunMaze();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function PlaceTile(x,y,tile = "wall")
|
|
|
|
{
|
|
|
|
if(is_running) return;
|
|
|
|
let is_start = start_node.length != 0 ? x==start_node[0] && y==start_node[1] : false;
|
|
|
|
let is_end = end_node.length != 0 ? x==end_node[0] && y==end_node[1] : false;
|
|
|
|
|
|
|
|
if(start_node.length == 0 || tile == "start"){
|
|
|
|
if(!is_end){
|
|
|
|
if(start_node.length != 0) ResetTile(start_node[0],start_node[1], true);
|
|
|
|
|
|
|
|
grid[y][x].make_state("start");
|
|
|
|
start_node = [x,y];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(end_node.length == 0 || tile == "end"){
|
|
|
|
if(!is_start){
|
|
|
|
if(end_node.length != 0) ResetTile(end_node[0],end_node[1], true);
|
|
|
|
|
|
|
|
grid[y][x].make_state("end");
|
|
|
|
end_node = [x,y]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
if(!is_start && !is_end){
|
|
|
|
grid[y][x].make_state("wall");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
grid[y][x].draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
function ResetTile(x,y,full=false)
|
|
|
|
{
|
|
|
|
if(is_running) return
|
|
|
|
if(full){
|
|
|
|
grid[y][x].reset();
|
|
|
|
grid[y][x].draw(grid_HTML);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(grid[y][x].is_state("wall"))
|
|
|
|
{
|
|
|
|
grid[y][x].reset();
|
|
|
|
}
|
|
|
|
grid[y][x].draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function ReconstructPath(came_from, current)
|
|
|
|
{
|
|
|
|
while(came_from[""+current.X+"y"+current.Y] != undefined){
|
|
|
|
current = came_from[""+current.X+"y"+current.Y]
|
|
|
|
current.make_state("path")
|
|
|
|
current.draw(grid_HTML);
|
|
|
|
await sleep(25)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function sleep (time) {
|
|
|
|
return new Promise((resolve) => setTimeout(resolve, time));
|
|
|
|
}
|
|
|
|
|
|
|
|
function ResetPath()
|
|
|
|
{
|
|
|
|
if(is_running) return
|
|
|
|
for(var i = 0; i < vertical_cells; i++){
|
|
|
|
for(var j = 0; j < horizontal_cells; j++){
|
|
|
|
let cell = grid[i][j];
|
|
|
|
if(!cell.is_state("wall") && !cell.is_state("start") && !cell.is_state("end")){
|
|
|
|
grid[i][j].reset();
|
|
|
|
grid[i][j].draw(grid_HTML);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function ClearWalls()
|
|
|
|
{
|
|
|
|
if(is_running) return
|
|
|
|
for(var i = 0; i < vertical_cells; i++){
|
|
|
|
for(var j = 0; j < horizontal_cells; j++){
|
|
|
|
let cell = grid[i][j];
|
|
|
|
if(cell.is_state("wall")){
|
|
|
|
grid[i][j].reset();
|
|
|
|
grid[i][j].draw(grid_HTML);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function ClearGrid()
|
|
|
|
{
|
|
|
|
if(is_running) return
|
|
|
|
start_node = [];
|
|
|
|
end_node = [];
|
|
|
|
for(var i = 0; i < vertical_cells; i++){
|
|
|
|
for(var j = 0; j < horizontal_cells; j++){
|
|
|
|
grid[i][j].reset();
|
|
|
|
grid[i][j].draw(grid_HTML);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PlaceTile(start_node_initial[0],start_node_initial[1])
|
|
|
|
PlaceTile(end_node_initial[0],end_node_initial[1])
|
|
|
|
}
|
|
|
|
|
|
|
|
async function Run()
|
|
|
|
{
|
|
|
|
let algo = algo_select.value;
|
|
|
|
switch(algo){
|
|
|
|
case "AStar":
|
|
|
|
await RunAStar();
|
|
|
|
break;
|
|
|
|
case "Dijkstra":
|
|
|
|
await RunDijkstra();
|
|
|
|
break;
|
|
|
|
case "BFS":
|
|
|
|
await RunBFS();
|
|
|
|
break;
|
|
|
|
case "DFS":
|
|
|
|
await RunDFS();
|
|
|
|
break;
|
|
|
|
case "Greedy":
|
|
|
|
await RunGreedy();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function RunAStar()
|
|
|
|
{
|
|
|
|
if(is_running) return;
|
|
|
|
ResetPath();
|
|
|
|
is_running = true;
|
|
|
|
let start = grid[start_node[1]][start_node[0]];
|
|
|
|
let end = grid[end_node[1]][end_node[0]];
|
|
|
|
for (var row of grid)
|
|
|
|
{
|
|
|
|
for (var cell of row)
|
|
|
|
{
|
|
|
|
cell.update_neighbours(grid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let count = 0;
|
|
|
|
let open_set = [];
|
|
|
|
open_set.push([0,count,start]);
|
|
|
|
let came_from = {}
|
|
|
|
|
|
|
|
let g_score = {}
|
|
|
|
for(let row of grid){
|
|
|
|
for(let cell of row){
|
|
|
|
g_score[""+cell.X+"y"+cell.Y] = Number.MAX_VALUE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_score[""+start.X+"y"+start.Y] = 0;
|
|
|
|
let f_score = {}
|
|
|
|
for(let row of grid){
|
|
|
|
for(let cell of row){
|
|
|
|
f_score[""+cell.X+"y"+cell.Y] = Number.MAX_VALUE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f_score[""+start.X+"y"+start.Y] = H(start_node[0],start_node[1],end_node[0],end_node[1]);
|
|
|
|
|
|
|
|
let open_set_hash = new Set();
|
|
|
|
open_set_hash.add(start);
|
|
|
|
|
|
|
|
while(!open_set.length == 0)
|
|
|
|
{
|
|
|
|
let current = open_set[0][2]
|
|
|
|
let current_smallest = Number.MAX_VALUE
|
|
|
|
let temp_index = 0;
|
|
|
|
for(let cell = 0; cell < open_set.length; cell ++)
|
|
|
|
{
|
|
|
|
if(open_set[cell][0] < current_smallest)
|
|
|
|
{
|
|
|
|
temp_index = cell;
|
|
|
|
current = open_set[cell][2]
|
|
|
|
current_smallest = open_set[cell][0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
open_set.splice(temp_index, 1);
|
|
|
|
open_set_hash.delete(current)
|
|
|
|
if(current == end)
|
|
|
|
{
|
|
|
|
// make path
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
await ReconstructPath(came_from,end)
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
is_running = false;
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var neighbour of current.neighbours){
|
|
|
|
let temp_g_score = g_score[""+current.X+"y"+current.Y]+1
|
|
|
|
if(temp_g_score < g_score[""+neighbour.X+"y"+neighbour.Y] || g_score[""+neighbour.X+"y"+neighbour.Y] == undefined){
|
|
|
|
came_from[""+neighbour.X+"y"+neighbour.Y] = current
|
|
|
|
g_score[""+neighbour.X+"y"+neighbour.Y] = temp_g_score
|
|
|
|
f_score[""+neighbour.X+"y"+neighbour.Y] = temp_g_score + H(neighbour.X,neighbour.Y, end.X,end.Y)
|
|
|
|
if(!open_set_hash.has(neighbour)){
|
|
|
|
count++;
|
|
|
|
open_set.push([f_score[""+neighbour.X+"y"+neighbour.Y],count,neighbour])
|
|
|
|
open_set_hash.add(neighbour)
|
|
|
|
neighbour.make_state("open")
|
|
|
|
}
|
|
|
|
neighbour.draw(grid_HTML)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(current != start)
|
|
|
|
{
|
|
|
|
current.make_state("closed");
|
|
|
|
current.draw(grid_HTML)
|
|
|
|
}
|
|
|
|
|
|
|
|
await sleep(200-speed_range.value)
|
|
|
|
}
|
|
|
|
is_running = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function H(x1,y1,x2,y2){
|
|
|
|
return Math.abs(x1-x2)+Math.abs(y1-y2);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function RunDijkstra()
|
|
|
|
{
|
|
|
|
if(is_running) return;
|
|
|
|
ResetPath();
|
|
|
|
is_running = true;
|
|
|
|
let start = grid[start_node[1]][start_node[0]];
|
|
|
|
let end = grid[end_node[1]][end_node[0]];
|
|
|
|
|
|
|
|
let dist = {};
|
|
|
|
let prev = {};
|
|
|
|
let Q = []
|
|
|
|
|
|
|
|
|
|
|
|
for (var row of grid)
|
|
|
|
{
|
|
|
|
for (var cell of row)
|
|
|
|
{
|
|
|
|
cell.update_neighbours(grid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var row of grid){
|
|
|
|
for(var cell of row){
|
|
|
|
dist[""+cell.X+"y"+cell.Y] = Number.MAX_VALUE;
|
|
|
|
prev[""+cell.X+"y"+cell.Y] = null;
|
|
|
|
Q.push(cell)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dist[""+start.X+"y"+start.Y] = 0;
|
|
|
|
|
|
|
|
while (Q.length > 0)
|
|
|
|
{
|
|
|
|
let u = Q[0];
|
|
|
|
let current_smallest = Number.MAX_VALUE;
|
|
|
|
let temp_index = 0;
|
|
|
|
for(let cell = 0; cell < Q.length; cell ++)
|
|
|
|
{
|
|
|
|
if(parseFloat(dist[""+Q[cell].X+"y"+Q[cell].Y]) < current_smallest)
|
|
|
|
{
|
|
|
|
temp_index = cell;
|
|
|
|
current_smallest = parseFloat(dist[""+ Q[cell].X+"y"+ Q[cell].Y]);
|
|
|
|
u = Q[cell];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Q.splice(temp_index, 1);
|
|
|
|
|
|
|
|
if(current_smallest == Number.MAX_VALUE)
|
|
|
|
{
|
|
|
|
is_running = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for(let v of u.neighbours)
|
|
|
|
{
|
|
|
|
if(Q.indexOf(v) != -1)
|
|
|
|
{
|
|
|
|
let alt = parseFloat(dist[""+u.X+"y"+u.Y]) + H(u.X,u.Y,v.X,v.Y);
|
|
|
|
if(alt < parseFloat(dist[""+v.X+"y"+v.Y])){
|
|
|
|
dist[""+v.X+"y"+v.Y] = alt;
|
|
|
|
prev[""+v.X+"y"+v.Y] = u;
|
|
|
|
}
|
|
|
|
v.make_state("open");
|
|
|
|
v.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
if(v == end)
|
|
|
|
{
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
await ReconstructPath(prev,end)
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
is_running = false;
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(u != start && u != end)
|
|
|
|
{
|
|
|
|
u.make_state("closed");
|
|
|
|
u.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
await sleep(200-speed_range.value)
|
|
|
|
}
|
|
|
|
is_running = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function RunBFS()
|
|
|
|
{
|
|
|
|
if(is_running) return;
|
|
|
|
ResetPath();
|
|
|
|
is_running = true;
|
|
|
|
let start = grid[start_node[1]][start_node[0]];
|
|
|
|
let end = grid[end_node[1]][end_node[0]];
|
|
|
|
|
|
|
|
let disc = [];
|
|
|
|
let prev = {};
|
|
|
|
let Q = []
|
|
|
|
|
|
|
|
disc.push(start)
|
|
|
|
Q.push(start)
|
|
|
|
|
|
|
|
for (var row of grid)
|
|
|
|
{
|
|
|
|
for (var cell of row)
|
|
|
|
{
|
|
|
|
cell.update_neighbours(grid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while(Q.length > 0)
|
|
|
|
{
|
|
|
|
let v = Q[0];
|
|
|
|
Q.splice(0,1);
|
|
|
|
|
|
|
|
for(var n of v.neighbours)
|
|
|
|
{
|
|
|
|
if(disc.indexOf(n) == -1)
|
|
|
|
{
|
|
|
|
disc.push(n);
|
|
|
|
Q.push(n);
|
|
|
|
n.make_state("open");
|
|
|
|
n.draw(grid_HTML);
|
|
|
|
prev[""+n.X+"y"+n.Y] = v;
|
|
|
|
if(n == end)
|
|
|
|
{
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
await ReconstructPath(prev,end)
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
is_running = false;
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(v != start && v != end)
|
|
|
|
{
|
|
|
|
v.make_state("closed");
|
|
|
|
v.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
await sleep(200-speed_range.value)
|
|
|
|
}
|
|
|
|
is_running = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function RunDFS()
|
|
|
|
{
|
|
|
|
if(is_running) return;
|
|
|
|
ResetPath();
|
|
|
|
is_running = true;
|
|
|
|
let start = grid[start_node[1]][start_node[0]];
|
|
|
|
let end = grid[end_node[1]][end_node[0]];
|
|
|
|
|
|
|
|
let disc = [];
|
|
|
|
let prev = {};
|
|
|
|
let S = []
|
|
|
|
|
|
|
|
S.push(start)
|
|
|
|
|
|
|
|
for (var row of grid)
|
|
|
|
{
|
|
|
|
for (var cell of row)
|
|
|
|
{
|
|
|
|
cell.update_neighbours(grid,false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while(S.length > 0)
|
|
|
|
{
|
|
|
|
let v = S.pop();
|
|
|
|
disc.push(v);
|
|
|
|
|
|
|
|
for(var i = v.neighbours.length-1;i>=0;i--)
|
|
|
|
{
|
|
|
|
let n = v.neighbours[i];
|
|
|
|
if(disc.indexOf(n) == -1)
|
|
|
|
{
|
|
|
|
n.make_state("open");
|
|
|
|
n.draw(grid_HTML);
|
|
|
|
prev[""+n.X+"y"+n.Y] = v;
|
|
|
|
if(n == end)
|
|
|
|
{
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
await ReconstructPath(prev,end)
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
is_running = false;
|
|
|
|
return
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
S.push(n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(v != start && v != end)
|
|
|
|
{
|
|
|
|
v.make_state("closed");
|
|
|
|
v.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
await sleep(200-speed_range.value)
|
|
|
|
}
|
|
|
|
is_running = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function RunGreedy()
|
|
|
|
{
|
|
|
|
if(is_running) return;
|
|
|
|
ResetPath();
|
|
|
|
is_running = true;
|
|
|
|
let start = grid[start_node[1]][start_node[0]];
|
|
|
|
let end = grid[end_node[1]][end_node[0]];
|
|
|
|
|
|
|
|
let dist = {}
|
|
|
|
let disc = []
|
|
|
|
let prev = {};
|
|
|
|
let Q = []
|
|
|
|
|
|
|
|
for (var row of grid)
|
|
|
|
{
|
|
|
|
for (var cell of row)
|
|
|
|
{
|
|
|
|
cell.update_neighbours(grid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for(let row of grid){
|
|
|
|
for(let cell of row){
|
|
|
|
dist[""+cell.X+"y"+cell.Y] = Number.MAX_VALUE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dist[""+start.X+"y"+start.Y] = H(start.X,start.Y,end.X,end.Y);
|
|
|
|
Q.push(start);
|
|
|
|
|
|
|
|
while(Q.length > 0){
|
|
|
|
let u = Q[0];
|
|
|
|
let current_smallest = Number.MAX_VALUE;
|
|
|
|
let temp_index = 0;
|
|
|
|
for(let cell = 0; cell < Q.length; cell ++)
|
|
|
|
{
|
|
|
|
if(parseFloat(dist[""+Q[cell].X+"y"+Q[cell].Y]) < current_smallest)
|
|
|
|
{
|
|
|
|
temp_index = cell;
|
|
|
|
current_smallest = parseFloat(dist[""+ Q[cell].X+"y"+ Q[cell].Y]);
|
|
|
|
u = Q[cell];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Q.splice(temp_index, 1);
|
|
|
|
disc.push(u);
|
|
|
|
|
|
|
|
if(u == end)
|
|
|
|
{
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
await ReconstructPath(prev,end)
|
|
|
|
end.make_state("end")
|
|
|
|
start.make_state("start")
|
|
|
|
end.draw(grid_HTML);
|
|
|
|
start.draw(grid_HTML)
|
|
|
|
is_running = false;
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var n of u.neighbours)
|
|
|
|
{
|
|
|
|
if(disc.indexOf(n) == -1)
|
|
|
|
{
|
|
|
|
dist[""+n.X+"y"+n.Y] = H(n.X,n.Y,end.X,end.Y);
|
|
|
|
disc.push(n);
|
|
|
|
Q.push(n);
|
|
|
|
n.make_state("open");
|
|
|
|
n.draw(grid_HTML);
|
|
|
|
prev[""+n.X+"y"+n.Y] = u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(u != start && u != end)
|
|
|
|
{
|
|
|
|
u.make_state("closed");
|
|
|
|
u.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
await sleep(200-speed_range.value)
|
|
|
|
}
|
|
|
|
is_running = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MAZES
|
|
|
|
async function RunMaze()
|
|
|
|
{
|
|
|
|
ClearGrid();
|
|
|
|
is_running = true;
|
|
|
|
for (var row of grid)
|
|
|
|
{
|
|
|
|
for (var cell of row)
|
|
|
|
{
|
|
|
|
cell.update_wall_neighbours(grid);
|
|
|
|
cell.update_neighbours(grid,false);
|
|
|
|
cell.visited_fill = null;
|
|
|
|
cell.visited = null;
|
|
|
|
cell.connect_visited = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let ctX = Math.floor(horizontal_cells/2);
|
|
|
|
let ctY = Math.floor(vertical_cells/2);
|
|
|
|
|
|
|
|
if(grid[ctY][ctX].is_state("start") || grid[ctY][ctX].is_state("end") && !grid[ctY+1][ctX].is_state("start") && !grid[ctY+1][ctX].is_state("end"))
|
|
|
|
{
|
|
|
|
ctY++;
|
|
|
|
}
|
|
|
|
else if(grid[ctY+1][ctX].is_state("start") || grid[ctY+1][ctX].is_state("end") && !grid[ctY][ctX+1].is_state("start") && !grid[ctY][ctX+1].is_state("end")){
|
|
|
|
ctX++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let algo = maze_algo_select.value;
|
|
|
|
switch(algo){
|
|
|
|
case "DFS":
|
|
|
|
FillWithWalls();
|
|
|
|
await RecursiveMazeDFS(ctY,ctX);
|
|
|
|
break;
|
|
|
|
case "Prim":
|
|
|
|
FillWithWalls();
|
|
|
|
await MazePrim(grid[ctY][ctX]);
|
|
|
|
break;
|
|
|
|
case "Random":
|
|
|
|
FillWithWalls();
|
|
|
|
await RecursiveMazeRandom(grid[ctY][ctX]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
is_running = false;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function FillWithWalls()
|
|
|
|
{
|
|
|
|
if(!grid[0][0].is_state("start") && !grid[0][0].is_state("end"))
|
|
|
|
{
|
|
|
|
RecursiveFillWithWalls(grid[0][0]);
|
|
|
|
}
|
|
|
|
else if(!grid[0][1].is_state("start") && !grid[0][1].is_state("end"))
|
|
|
|
{
|
|
|
|
RecursiveFillWithWalls(grid[0][1]);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
RecursiveFillWithWalls(grid[1][0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function RecursiveFillWithWalls(current)
|
|
|
|
{
|
|
|
|
if(current.is_state("start") || current.is_state("end")) return;
|
|
|
|
current.make_state("wall");
|
|
|
|
current.draw(grid_HTML);
|
|
|
|
current.visited_fill = true;
|
|
|
|
for(let n of current.neighbours)
|
|
|
|
{
|
|
|
|
if(n.visited_fill != true)RecursiveFillWithWalls(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function RecursiveMazeRandom(current)
|
|
|
|
{
|
|
|
|
current.visited = true;
|
|
|
|
let neighbours = current.wall_neighbours;
|
|
|
|
while(neighbours.length > 0)
|
|
|
|
{
|
|
|
|
let idx = Math.floor(Math.random() * neighbours.length);
|
|
|
|
let n = neighbours[idx];
|
|
|
|
neighbours.splice(idx,1);
|
|
|
|
|
|
|
|
if(n.visited != true)
|
|
|
|
{
|
|
|
|
n.visited = true;
|
|
|
|
let wall = [];
|
|
|
|
if(n.X == current.X) wall = [n.X, n.Y>current.Y ? current.Y+1 : n.Y+1 ];
|
|
|
|
else wall = [n.X>current.X ? current.X+1 : n.X+1, n.Y ];
|
|
|
|
|
|
|
|
let wall_cell = grid[wall[1]][wall[0]]
|
|
|
|
if(!wall_cell.is_state("start") && !wall_cell.is_state("end"))
|
|
|
|
{
|
|
|
|
wall_cell.make_state("null");
|
|
|
|
wall_cell.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
//await sleep(1);
|
|
|
|
await RecursiveMazeRandom(wall_cell);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function RecursiveMazeDFS(r,c)
|
|
|
|
{
|
|
|
|
let randDirs = [1,2,3,4];
|
|
|
|
shuffle(randDirs);
|
|
|
|
for (var i = 0; i < randDirs.length; i++) {
|
|
|
|
|
|
|
|
switch(randDirs[i]){
|
|
|
|
case 1:
|
|
|
|
if (r - 2 <= 0 )
|
|
|
|
continue;
|
|
|
|
if (grid[r - 2][c].State != "null") {
|
|
|
|
if(!grid[r-2][c].is_state("start") && !grid[r-2][c].is_state("end")) grid[r-2][c].make_state("null");
|
|
|
|
if(!grid[r-1][c].is_state("start") && !grid[r-1][c].is_state("end")) grid[r-1][c].make_state("null");
|
|
|
|
|
|
|
|
grid[r-2][c].draw(grid_HTML);
|
|
|
|
grid[r-1][c].draw(grid_HTML);
|
|
|
|
|
|
|
|
await sleep(1);
|
|
|
|
RecursiveMazeDFS(r - 2, c);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (c + 2 >= horizontal_cells - 1)
|
|
|
|
continue;
|
|
|
|
if (grid[r][c + 2].State != "null") {
|
|
|
|
if(!grid[r][c + 2].is_state("start") && !grid[r][c + 2].is_state("end")) grid[r][c + 2].make_state("null");
|
|
|
|
if(!grid[r][c + 1].is_state("start") && !grid[r][c + 1].is_state("end")) grid[r][c + 1].make_state("null");
|
|
|
|
|
|
|
|
grid[r][c + 2].draw(grid_HTML);
|
|
|
|
grid[r][c + 1].draw(grid_HTML);
|
|
|
|
|
|
|
|
await sleep(1);
|
|
|
|
RecursiveMazeDFS(r, c + 2);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (r + 2 >= vertical_cells - 1)
|
|
|
|
continue;
|
|
|
|
if (grid[r + 2][c].State != "null") {
|
|
|
|
if(!grid[r+2][c].is_state("start") && !grid[r+2][c].is_state("end")) grid[r+2][c].make_state("null");
|
|
|
|
if(!grid[r+1][c].is_state("start") && !grid[r+1][c].is_state("end")) grid[r+1][c].make_state("null");
|
|
|
|
|
|
|
|
grid[r+2][c].draw(grid_HTML);
|
|
|
|
grid[r+1][c].draw(grid_HTML);
|
|
|
|
|
|
|
|
await sleep(1);
|
|
|
|
RecursiveMazeDFS(r + 2, c);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
if (c - 2 <= 0)
|
|
|
|
continue;
|
|
|
|
if (grid[r][c - 2].State != "null") {
|
|
|
|
if(!grid[r][c - 2].is_state("start") && !grid[r][c - 2].is_state("end")) grid[r][c - 2].make_state("null");
|
|
|
|
if(!grid[r][c - 1].is_state("start") && !grid[r][c - 1].is_state("end")) grid[r][c - 1].make_state("null");
|
|
|
|
|
|
|
|
grid[r][c - 2].draw(grid_HTML);
|
|
|
|
grid[r][c - 1].draw(grid_HTML);
|
|
|
|
|
|
|
|
await sleep(1);
|
|
|
|
RecursiveMazeDFS(r, c - 2);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async function MazePrim(start)
|
|
|
|
{
|
|
|
|
|
|
|
|
for(var row of grid){
|
|
|
|
for(var cell of row){
|
|
|
|
cell.update_wall_neighbours(grid,0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
start.make_state("null");
|
|
|
|
start.draw(grid_HTML);
|
|
|
|
start.visited = true;
|
|
|
|
|
|
|
|
let walls = [];
|
|
|
|
|
|
|
|
for(let n of start.wall_neighbours){
|
|
|
|
if(n.is_state("wall")) walls.push(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
while(walls.length > 0)
|
|
|
|
{
|
|
|
|
let idx = Math.floor(Math.random() * walls.length);
|
|
|
|
let r = walls[idx];
|
|
|
|
walls.splice(idx,1);
|
|
|
|
|
|
|
|
if( !(r.X <= 0 || r.X >= horizontal_cells-1) ){
|
|
|
|
|
|
|
|
let prev = grid[r.Y][r.X-1];
|
|
|
|
let next = grid[r.Y][r.X+1];
|
|
|
|
|
|
|
|
if(prev.visited != true && next.visited == true){
|
|
|
|
if(!r.is_state("start") && !r.is_state("end")){
|
|
|
|
r.make_state("null");
|
|
|
|
r.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
prev.visited = true;
|
|
|
|
if(!prev.is_state("start") && !prev.is_state("end")){
|
|
|
|
prev.make_state("null");
|
|
|
|
prev.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var n of prev.wall_neighbours){
|
|
|
|
if(n.is_state("wall")) walls.push(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(prev.visited == true && next.visited != true){
|
|
|
|
if(!r.is_state("start") && !r.is_state("end")){
|
|
|
|
r.make_state("null");
|
|
|
|
r.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
next.visited = true;
|
|
|
|
if(!next.is_state("start") && !next.is_state("end")){
|
|
|
|
next.make_state("null");
|
|
|
|
next.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var n of next.wall_neighbours){
|
|
|
|
if(n.is_state("wall")) walls.push(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !(r.Y <= 0 || r.Y >= vertical_cells-1) ){
|
|
|
|
let prev = grid[r.Y-1][r.X];
|
|
|
|
let next = grid[r.Y+1][r.X];
|
|
|
|
|
|
|
|
if(prev.visited != true && next.visited == true){
|
|
|
|
if(!r.is_state("start") && !r.is_state("end")){
|
|
|
|
r.make_state("null");
|
|
|
|
r.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
prev.visited = true;
|
|
|
|
if(!prev.is_state("start") && !prev.is_state("end")){
|
|
|
|
prev.make_state("null");
|
|
|
|
prev.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var n of prev.wall_neighbours){
|
|
|
|
if(n.is_state("wall")) walls.push(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(prev.visited == true && next.visited != true){
|
|
|
|
if(!r.is_state("start") && !r.is_state("end")){
|
|
|
|
r.make_state("null");
|
|
|
|
r.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
next.visited = true;
|
|
|
|
if(!next.is_state("start") && !next.is_state("end")){
|
|
|
|
next.make_state("null");
|
|
|
|
next.draw(grid_HTML);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var n of next.wall_neighbours){
|
|
|
|
if(n.is_state("wall")) walls.push(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
await sleep(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function RecursiveAreConnected(start,end)
|
|
|
|
{
|
|
|
|
if(start == end) return true;
|
|
|
|
start.connect_visited = true;
|
|
|
|
let connected = false;
|
|
|
|
start.update_neighbours(grid,false);
|
|
|
|
for(var n of start.neighbours)
|
|
|
|
{
|
|
|
|
if(n == end) return true;
|
|
|
|
if(n.connect_visited != true) connected = connected || RecursiveAreConnected(n,end);
|
|
|
|
}
|
|
|
|
return connected;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function shuffle(array) {
|
|
|
|
array.sort(() => Math.random() - 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
return {Run, RunMaze};
|
|
|
|
}
|