function shuffleArray(unshuffled: {goTo: number[], dir: string}[]): any {
    return (
        unshuffled
            .map(value => ({ value, sort: Math.random() }))
            .sort((a, b) => a.sort - b.sort)
            .map(({ value }) => value)
    )
}

class Tile {

    private x: number;
    private y: number;
    private value: string;
    private visited: boolean;

    constructor(x: number, y: number, value: string, visited: boolean) {
        this.x = x
        this.y = y
        this.value = value
        this.visited = visited
    }

    get_value(): string {
        return this.value
    }

    get_visited(): boolean {
        return this.visited
    }

    set_value(value: string) {
        this.value = value
    }
    set_visited(visited: boolean) {
        this.visited = visited
    }
}

// Assuming the Grid is a square matrix
class Grid {
    private readonly startValue = 2
    private readonly endValue = 3
    private map: Tile[][]
    private size

    constructor(size: number) {
        this.size = size;
        this.map = [];

        for (var i: number = 0; i < size; i++) {
            this.map[i] = [];
            for (var j: number = 0; j < size; j++) {
                this.map[i][j] = new Tile(i, j, 0+"", false);
            }
        }

        this.map[0][0] = new Tile(0, 0, this.startValue+"", false)
        this.map[size - 1][size - 1] = new Tile(size - 1, size - 1, this.endValue+"", false)

        this.get_next_valid_position(0, 0)
    }

    get_visited(x: number, y: number): boolean {
        return this.map[x][y].get_visited()
    }

    get_tile(x: number, y: number): string {
        return this.map[x][y].get_value()
    }

    set_tile(x: number, y: number, value: string) {
        this.map[x][y].set_value(value+"")
    }

    set_visited(x: number, y: number, visited: boolean) {
        this.map[x][y].set_visited(visited)
    }

    check_position(x: number, y: number): boolean {
        // if (this.get_tile(x, y) != this.endValue) return false
        return true
    }

    private get_next_valid_position(x: number, y: number) {
        // while not at END

        const options = [
            {goTo: [x, y + 1], dir: ">"},
            {goTo: [x, y - 1], dir: "<"},
            {goTo: [x + 1, y], dir: "v"},
            {goTo: [x - 1, y], dir:"^"},
        ];

        let valid_options = options.filter(
            ({goTo: [x, y]}) =>
                ((0 <= x && x < this.size) && (0 <= y && y < this.size)) &&
                (this.get_tile(x, y) == "0" || this.get_tile(x, y) == this.endValue+"") &&
                !this.get_visited(x, y)
        );

        valid_options = shuffleArray(valid_options);

        const can_move_to = (x: number, y:number)=> {
            return !this.map[x][y].get_visited()
        }

        if (this.get_tile(x, y) == this.endValue+"") {
            return;
        }

        for (let option of valid_options) {
            const {goTo: [ox, oy], dir: dir} = option
            if (can_move_to(ox, oy)) {   
                    if (this.get_tile(ox, oy) !== this.endValue+"") {
                        this.set_tile(ox, oy, dir +1);
                        this.set_visited(ox, oy, true);
                        this.get_next_valid_position(ox, oy)
                        this.set_tile(ox, oy, 0+"");
                        this.set_visited(ox, oy, false);
                    } else {
                        this.set_tile(ox, oy, dir + this.endValue);
                        console.table(this.map.map(t => t.map(u => u.get_value())));
                    }
                }
            }
        }
}


new Grid(5)

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: