Commit c09bbeb

Ansari <ansari_official@yahoo.com>
2024-06-16 10:22:39
004
1 parent 788c343
Changed files (4)
004.Maze-Runner/index.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>004.Maze-Runner</title>
+    <link rel="stylesheet" href="./style.css">
+    <link rel="stylesheet" href="../assets/common.css">
+    <link rel="shortcut icon" href="../assets/favicon.png" type="image/png">
+</head>
+
+<body>
+    <!-- Modal Section -->
+    <input class="modal-state" id="modal" type="checkbox" />
+    <div class="modal">
+        <label class="modal__bg" for="modal"></label>
+        <div class="modal__inner" style="width: 30%;">
+            <label class="modal__close" for="modal"></label>
+            <h2>004.Maze-Runner</h2>
+            <p class="modal__p">
+            <ul>
+                <li>A simple maze game which uses the concept of
+                    <span style="color:var(--lightGold)">
+                        backtracking (DFS)
+                    </span>
+                    to generate the maze.
+                </li>
+                <li>
+                    Initially the starting cell is marked as visited.
+                </li>
+                <li>
+                    If the current cell has any neighbours which have not been visited, then randomly selected neighbour
+                    is
+                    marked as visited and made as the current cell.
+                </li>
+                <li>
+                    If the current cell has no unvisited neighbours,then the current cell is backtracked to the previous
+                    cell.
+                </li>
+                <li>
+                    The process continues until all cells have been visited.
+                    Refer to
+                    <a href="https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_depth-first_search:~:text=branch%20before%20backtracking.-,Recursive%20implementation,-%5Bedit%5D"
+                        target="_blank" style="color:var(--lightGold)">
+                        Maze Generation Algorithm
+                    </a>
+                </li>
+            </ul>
+            </p>
+        </div>
+    </div>
+
+    <!-- Main Content -->
+    <h1>Maze Runner</h1>
+    <p id="tooltip"></p>
+    <div id="maze">
+
+
+    </div>
+    <!-- Footer -->
+    <div class="footer">
+        <label for="modal">(i)</label>
+        <a class="arrow" href="../"></a>
+    </div>
+</body>
+<script src="./script.js"></script>
+
+</html>
\ No newline at end of file
004.Maze-Runner/script.js
@@ -0,0 +1,160 @@
+let cells;
+let maze_created = false;
+let won = false;
+
+const rows = 15;
+const cols = 15;
+const visited = new Set();
+const tooltip = document.getElementById("tooltip");
+
+
+function getRandomUnVisitedNeighbour(current) {
+    const neighbors = [];
+
+    const row = Math.floor(current / cols);
+    const col = current % cols;
+
+    if (row > 0) {
+        neighbors.push(current - cols);
+    }
+
+    if (row < rows - 1) {
+        neighbors.push(current + cols);
+    }
+
+    if (col > 0) {
+        neighbors.push(current - 1);
+    }
+
+    if (col < cols - 1) {
+        neighbors.push(current + 1);
+    }
+
+    const unvisited = neighbors.filter(neighbour => !visited.has(neighbour));
+    if (unvisited.length === 0) {
+        return null;
+    }
+    const randomNeighbour = unvisited[Math.floor(Math.random() * unvisited.length)];
+    return randomNeighbour;
+}
+
+function removeWall(current, next) {
+ /*
+Explanation of the following code:
+If the direction is 1, it means that the random neighbour is to the right of the current cell
+If the direction is -1, it means that the random neighbour is to the left of the current cell
+If the direction is cols, it means that the random neighbour is below the current cell
+If the direction is -cols, it means that the random neighbour is above the current cell
+*/
+    const direction = next - current;
+    if (direction === -cols) {
+        cells[current].style.borderTop = "none";
+        cells[next].style.borderBottom = "none";
+    }
+    if (direction === cols) {
+        cells[current].style.borderBottom = "none";
+        cells[next].style.borderTop = "none";
+    }
+    if (direction === -1) {
+        cells[current].style.borderLeft = "none";
+        cells[next].style.borderRight = "none";
+    }
+    if (direction === 1) {
+        cells[current].style.borderRight = "none";
+        cells[next].style.borderLeft = "none";
+    }
+}
+
+async function sleep(ms) {
+    return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+async function dfs(maze,start) {
+    const stack = [start];
+    while (stack.length > 0){
+        const current = stack.pop();
+        maze.children[current].classList.add("current-gen");
+        visited.add(current);        
+        const next = getRandomUnVisitedNeighbour(current);
+        if (next) {
+            removeWall(current, next);
+            stack.push(current);
+            stack.push(next);
+        }
+        await sleep(10);
+        maze.children[current].classList.remove("current-gen");
+    }
+    maze.children[start].classList.add("current");
+    maze_created = true;
+    tooltip.innerHTML = "Use W A S D or arrow keys to move";
+}
+
+
+function movement(maze,movement){
+    const current = document.querySelector(".current");
+    const index = Array.from(maze.children).indexOf(current);
+    if(movement === "UP" && current.style.borderTop === "none"){
+        maze.children[index].classList.remove("current");
+        maze.children[index-cols].classList.add("current");
+    }
+    if(movement === "DOWN" && current.style.borderBottom === "none"){
+        maze.children[index].classList.remove("current");
+        maze.children[index+cols].classList.add("current");
+    }
+    if(movement === "LEFT" && current.style.borderLeft === "none"){
+        maze.children[index].classList.remove("current");
+        maze.children[index-1].classList.add("current");
+    }
+    if(movement === "RIGHT" && current.style.borderRight === "none"){
+        maze.children[index].classList.remove("current");
+        maze.children[index+1].classList.add("current");
+    }
+    if(document.querySelector(".current").classList.contains("final")){
+        document.querySelector(".current").classList.add("won");
+        won = true;
+        tooltip.innerHTML = "You won! Press F5 to play again";
+    }
+}
+
+
+document.addEventListener("DOMContentLoaded", async function () {
+    tooltip.innerHTML = "Generating maze..."
+    const maze = document.querySelector("#maze")
+    for (let i = 0; i < rows; i++) {
+        for (let j = 0; j < cols; j++) {
+            const cell_element = document.createElement("div");
+            cell_element.classList.add("cell");
+            maze.appendChild(cell_element);
+        }
+    }
+
+    const initial_position = 0;
+    const final_position = rows * cols - 1;
+
+    cells = document.querySelectorAll(".cell");
+    await dfs(maze,initial_position);
+    cells[final_position].classList.add("final");
+
+    document.addEventListener("keydown", function (event) {
+        if(!maze_created || won){
+            return;
+        }
+        if(event.key ==="w" || event.key === "W" || event.key === "ArrowUp"){
+            movement(maze,"UP");
+        }
+        if(event.key ==="s" || event.key === "S" || event.key === "ArrowDown"){
+            movement(maze,"DOWN");
+        }
+        if(event.key ==="a" || event.key === "A" || event.key === "ArrowLeft"){
+            movement(maze,"LEFT");
+        }
+        if(event.key ==="d" || event.key === "D" || event.key === "ArrowRight"){
+            movement(maze,"RIGHT");
+        }
+        // f5 key
+        if(event.key === "F5"){
+            window.location.reload();
+        }
+    });
+    
+});
\ No newline at end of file
004.Maze-Runner/style.css
@@ -0,0 +1,55 @@
+body{
+    gap:20px;
+}
+
+#maze {
+    margin-top: 20px;
+    border: 1px solid white;
+    position: relative;
+    width: 300px;
+    height: 300px;
+    grid-template-columns: repeat(15,1fr);
+    grid-template-rows: repeat(15,1fr);
+    display: grid;
+    background-color: black;
+}
+.cell {
+    width: 20px;
+    height: 20px;
+    border: 1px solid white;
+}
+
+.final::before{
+    content: "๐Ÿ‘‘";
+    font-size: 13px;
+    text-align: center;
+    position: relative;
+    top: -3px;
+}
+
+.current-gen{
+    background-color: var(--gold);
+}
+
+.current{
+    color: var(--gold);
+}
+.current::before{
+    content: "โ™Ÿ";
+    font-size: 18px;
+    position: relative;
+    top: -3px;
+  
+}
+
+.won::before{
+    content: "๐ŸŽ‰";
+    font-size: 13px;
+    text-align: center;
+    position: relative;
+    top: -3px;
+}
+
+#tooltip{
+    color: var(--gold);
+}
\ No newline at end of file
index.html
@@ -16,6 +16,7 @@
         <a href="./001.Vision/" class="opacity-75 hover:opacity-100 hover:text-yellow-400">001.Vision</a>
         <a href="./002.TOTP/" class="opacity-75 hover:opacity-100 hover:text-yellow-400" >002.TOTP</a>
         <a href="./003.3n+1/" class="opacity-75 hover:opacity-100 hover:text-yellow-400" >003.3n+1</a>
+        <a href="./004.Maze-Runner/" class="opacity-75 hover:opacity-100 hover:text-yellow-400" >004.Maze-Runner</a>
     </div>
 </body>
 </html>
\ No newline at end of file