main
90e35a3 ยท 1 year ago 22 commits
  1let cells;
  2let maze_created = false;
  3let won = false;
  4let answer_stack = [];
  5let answer_found = false;
  6let cheatcodes = [];
  7const cheatkeys = ["m", "i", "n", "h", "o"];
  8let cheatTimeout;
  9let isCheatCodeActivated = false;
 10
 11const rows = 15;
 12const cols = 15;
 13const visited = new Set();
 14const tooltip = document.getElementById("tooltip");
 15const w = document.querySelector("#w");
 16const s = document.querySelector("#s");
 17const a = document.querySelector("#a");
 18const d = document.querySelector("#d");
 19
 20function getRandomUnVisitedNeighbour(current) {
 21    const neighbors = [];
 22
 23    const row = Math.floor(current / cols);
 24    const col = current % cols;
 25
 26    if (row > 0) {
 27        neighbors.push(current - cols);
 28    }
 29
 30    if (row < rows - 1) {
 31        neighbors.push(current + cols);
 32    }
 33
 34    if (col > 0) {
 35        neighbors.push(current - 1);
 36    }
 37
 38    if (col < cols - 1) {
 39        neighbors.push(current + 1);
 40    }
 41
 42    const unvisited = neighbors.filter(neighbour => !visited.has(neighbour));
 43    if (unvisited.length === 0) {
 44        return null;
 45    }
 46    const randomNeighbour = unvisited[Math.floor(Math.random() * unvisited.length)];
 47    return randomNeighbour;
 48}
 49
 50function removeWall(current, next) {
 51    /*
 52   Explanation of the following code:
 53   If the direction is 1, it means that the random neighbour is to the right of the current cell
 54   If the direction is -1, it means that the random neighbour is to the left of the current cell
 55   If the direction is cols, it means that the random neighbour is below the current cell
 56   If the direction is -cols, it means that the random neighbour is above the current cell
 57   */
 58    const direction = next - current;
 59    if (direction === -cols) {
 60        cells[current].style.borderTop = "none";
 61        cells[next].style.borderBottom = "none";
 62    }
 63    if (direction === cols) {
 64        cells[current].style.borderBottom = "none";
 65        cells[next].style.borderTop = "none";
 66    }
 67    if (direction === -1) {
 68        cells[current].style.borderLeft = "none";
 69        cells[next].style.borderRight = "none";
 70    }
 71    if (direction === 1) {
 72        cells[current].style.borderRight = "none";
 73        cells[next].style.borderLeft = "none";
 74    }
 75}
 76
 77async function sleep(ms) {
 78    return new Promise(resolve => setTimeout(resolve, ms));
 79}
 80
 81async function dfs(maze, start) {
 82    const stack = [start];
 83    answer_stack = [start];
 84    while (stack.length > 0) {
 85        const current = stack.pop();
 86        if (current === rows * cols - 1) {
 87            answer_found = true;
 88        } else if (!answer_found) {
 89            answer_stack.pop();
 90        }
 91        maze.children[current].classList.add("current-gen");
 92        visited.add(current);
 93        const next = getRandomUnVisitedNeighbour(current);
 94        if (next) {
 95            removeWall(current, next);
 96            stack.push(current);
 97            stack.push(next);
 98            if (!answer_found) {
 99                answer_stack.push(current);
100                answer_stack.push(next);
101            }
102        }
103        await sleep(10);
104        maze.children[current].classList.remove("current-gen");
105    }
106    maze.children[start].classList.add("current");
107    maze_created = true;
108    tooltip.innerHTML = "Use W A S D or arrow keys to move";
109}
110
111function lightUpButtons(btn) {
112    switch (btn) {
113        case "w":
114            w.classList.add("lit");
115            setTimeout(() => { w.classList.remove("lit"); }, 100);
116            break;
117        case "s":
118            s.classList.add("lit");
119            setTimeout(() => { s.classList.remove("lit"); }, 100);
120            break;
121        case "a":
122            a.classList.add("lit");
123            setTimeout(() => { a.classList.remove("lit"); }, 100);
124            break;
125        case "d":
126            d.classList.add("lit");
127            setTimeout(() => { d.classList.remove("lit"); }, 100);
128            break;
129    }
130}
131
132function movement(maze, movement) {
133    if (won) {
134        return;
135    }
136    const current = document.querySelector(".current");
137    const index = Array.from(maze.children).indexOf(current);
138    if (movement === "UP" && current.style.borderTop === "none") {
139        maze.children[index].classList.remove("current");
140        maze.children[index - cols].classList.add("current");
141    }
142    if (movement === "DOWN" && current.style.borderBottom === "none") {
143        maze.children[index].classList.remove("current");
144        maze.children[index + cols].classList.add("current");
145    }
146    if (movement === "LEFT" && current.style.borderLeft === "none") {
147        maze.children[index].classList.remove("current");
148        maze.children[index - 1].classList.add("current");
149    }
150    if (movement === "RIGHT" && current.style.borderRight === "none") {
151        maze.children[index].classList.remove("current");
152        maze.children[index + 1].classList.add("current");
153    }
154    if (document.querySelector(".current").classList.contains("final")) {
155        document.querySelector(".current").classList.add("won");
156        won = true;
157        tooltip.innerHTML = "You won! Press F5 to play again";
158    }
159}
160
161
162document.addEventListener("DOMContentLoaded", async function () {
163    tooltip.innerHTML = "Generating maze..."
164    const maze = document.querySelector("#maze")
165    for (let i = 0; i < rows; i++) {
166        for (let j = 0; j < cols; j++) {
167            const cell_element = document.createElement("div");
168            cell_element.classList.add("cell");
169            maze.appendChild(cell_element);
170        }
171    }
172
173    const initial_position = 0;
174    const final_position = rows * cols - 1;
175
176    cells = document.querySelectorAll(".cell");
177    await dfs(maze, initial_position);
178    cells[final_position].classList.add("final");
179    document.querySelector(".controls").style.visibility = "visible";
180
181    document.addEventListener("keydown", function (event) {
182        if (!maze_created || won || isCheatCodeActivated) {
183            return;
184        }
185        if (event.key === "w" || event.key === "W" || event.key === "ArrowUp") {
186            movement(maze, "UP");
187            lightUpButtons("w");
188        }
189        if (event.key === "s" || event.key === "S" || event.key === "ArrowDown") {
190            movement(maze, "DOWN");
191            lightUpButtons("s");
192        }
193        if (event.key === "a" || event.key === "A" || event.key === "ArrowLeft") {
194            movement(maze, "LEFT");
195            lightUpButtons("a");
196        }
197        if (event.key === "d" || event.key === "D" || event.key === "ArrowRight") {
198            movement(maze, "RIGHT");
199            lightUpButtons("d");
200        }
201        // f5 key
202        if (event.key === "F5") {
203            window.location.reload();
204        }
205
206        // cheatcode 
207
208        if (cheatkeys.includes(event.key.toLowerCase())) {
209            cheatcodes.push(event.key.toLowerCase());
210            clearTimeout(cheatTimeout);
211            cheatTimeout = setTimeout(() => {
212                cheatcodes = [];
213            }, 3000);
214
215            if (cheatcodes.length == 5) {
216                let cheatcode = cheatcodes.join("");
217                if (cheatcode === "minho") {
218                    isCheatCodeActivated = true;
219                    moveAccordingToTheAnswerStack();
220                    tooltip.textContent = `Cheat code activated!`;
221                }
222                cheatcodes = [];
223            }
224        } else {
225            cheatcodes = [];
226        }
227    });
228
229    w.onclick = () => {
230        movement(maze, "UP");
231        lightUpButtons("w");
232    }
233
234    s.onclick = () => {
235        movement(maze, "DOWN");
236        lightUpButtons("s");
237    }
238
239    a.onclick = () => {
240        movement(maze, "LEFT");
241        lightUpButtons("a");
242    }
243
244    d.onclick = () => {
245        movement(maze, "RIGHT");
246        lightUpButtons("d");
247    }
248
249});
250
251
252async function moveAccordingToTheAnswerStack() {
253    if (answer_stack.length === 0) {
254        return;
255    }
256    const maze = document.querySelector("#maze");
257    while (answer_stack.length > 0) {
258        let current = document.querySelector(".current");
259        current.style.backgroundColor = "rgba(255, 215, 0, 0.7)";
260        let index = Array.from(maze.children).indexOf(current);
261        let next = answer_stack.shift();
262        if (next === index - cols) {
263            movement(maze, "UP");
264        }
265        if (next === index + cols) {
266            movement(maze, "DOWN");
267        }
268        if (next === index - 1) {
269            movement(maze, "LEFT");
270        }
271        if (next === index + 1) {
272            movement(maze, "RIGHT");
273        }
274        await sleep(50);
275    }
276}