talk-end-of-procrastination/module/pathfinder/main.js

1028 lines
34 KiB
JavaScript
Raw Normal View History

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];
const window_y = body.scrollHeight - navbar_HTML.scrollHeight;
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);
vertical_cells = Math.floor( (window_y - vertical_cells) / 35);
grid_HTML.style.width = `${horizontal_cells * 35}px`;
grid_HTML.style.height = `${vertical_cells * 30}px`;
// Start and End nodes
let start_node = [Math.floor(horizontal_cells/3) , Math.floor(vertical_cells/2)];
let end_node = [Math.floor(horizontal_cells/3*2) , Math.floor(vertical_cells/2)];
let start_node_initial = [Math.floor(horizontal_cells/3) , Math.floor(vertical_cells/2)]
let end_node_initial = [Math.floor(horizontal_cells/3*2) , Math.floor(vertical_cells/2)]
// 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};
}