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 * 0.9);

    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};
}
