Frontend Forever App
We have a mobile app for you to download and use. And you can unlock many features in the app.
Get it now
Intall Later
Run
HTML
CSS
Javascript
Output
Document
Rows:
Columns:
Reset
Auto Solve
Congratulations! You reached the finish!
@charset "UTF-8"; @import url(https://fonts.googleapis.com/css?family=Nunito+Sans:300,400,600,700,800); *, :after, :before { box-sizing: border-box; padding: 0; margin: 0; } @import url("https://fonts.googleapis.com/css2?family=Lato&display=swap"); @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"); body { margin: 0; overflow: hidden; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #000; color: #fff; font-family: "Lato", sans-serif; } #controls { position: absolute; top: 10px; left: 10px; display: flex; flex-direction: column; gap: 10px; } #maze-canvas { display: block; margin-left: 0; /* Adjust to add space for controls */ } #congratulations-popup { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.5); padding: 20px; border: 1px solid #fffa; border-radius: 10px; text-align: center; font-size: 24px; color: #ff0; } .hidden { display: none; } button { padding: 10px 20px; font-size: 16px; cursor: pointer; } label { font-size: 16px; }
console.log("Event Fired") const canvas = document.getElementById("maze-canvas"); const ctx = canvas.getContext("2d"); const resetButton = document.getElementById("reset-button"); const autoSolveButton = document.getElementById("auto-solve-button"); const rowSlider = document.getElementById("row-slider"); const colSlider = document.getElementById("col-slider"); const popup = document.getElementById("congratulations-popup"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; let rows = 16; let cols = 18; let cellSize; let mazeWidth; let mazeHeight; let offsetX; let offsetY; let grid = []; let stack = []; let currentCell; let path = []; let isDrawing = false; let hasWon = false; class Cell { constructor(row, column) { this.row = row; this.column = column; this.walls = [true, true, true, true]; // top, right, bottom, left this.visited = false; } checkNeighbors() { const neighbors = []; const top = grid[index(this.row - 1, this.column)]; const right = grid[index(this.row, this.column + 1)]; const bottom = grid[index(this.row + 1, this.column)]; const left = grid[index(this.row, this.column - 1)]; if (top && !top.visited) neighbors.push(top); if (right && !right.visited) neighbors.push(right); if (bottom && !bottom.visited) neighbors.push(bottom); if (left && !left.visited) neighbors.push(left); if (neighbors.length > 0) { const r = Math.floor(Math.random() * neighbors.length); return neighbors[r]; } else { return undefined; } } } function index(row, col) { if (row < 0 || col < 0 || row >= rows || col >= cols) { return -1; } return row * cols + col; } function resetMaze() { rows = parseInt(rowSlider.value); cols = parseInt(colSlider.value); cellSize = Math.min(canvas.width, canvas.height) / Math.max(rows, cols); mazeWidth = cellSize * cols; mazeHeight = cellSize * rows; offsetX = (canvas.width - mazeWidth) / 2; offsetY = (canvas.height - mazeHeight) / 2; ctx.clearRect(0, 0, canvas.width, canvas.height); grid = []; stack = []; generateMaze(); path = []; isDrawing = false; hasWon = false; popup.classList.add("hidden"); drawStartFinishText(); } function generateMaze() { for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { grid.push(new Cell(y, x)); } } grid[0].walls[3] = false; // Remove left wall of top-left cell grid[0].walls[0] = false; // Remove top wall of top-left cell grid[grid.length - 1].walls[2] = false; // Remove bottom wall of bottom-right cell grid[grid.length - 1].walls[1] = false; // Remove right wall of bottom-right cell currentCell = grid[grid.length - 1]; currentCell.visited = true; function removeWalls(a, b) { const x = a.column - b.column; if (x === 1) { a.walls[3] = false; b.walls[1] = false; } else if (x === -1) { a.walls[1] = false; b.walls[3] = false; } const y = a.row - b.row; if (y === 1) { a.walls[0] = false; b.walls[2] = false; } else if (y === -1) { a.walls[2] = false; b.walls[0] = false; } } while (true) { const nextCell = currentCell.checkNeighbors(); if (nextCell) { nextCell.visited = true; stack.push(currentCell); removeWalls(currentCell, nextCell); currentCell = nextCell; } else if (stack.length > 0) { currentCell = stack.pop(); } else { break; } } drawMaze(); } function drawMaze() { ctx.strokeStyle = "#fff"; ctx.lineWidth = 2; for (let i = 0; i < grid.length; i++) { const cell = grid[i]; const x = cell.column * cellSize + offsetX; const y = cell.row * cellSize + offsetY; if (cell.walls[0]) { ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + cellSize, y); ctx.stroke(); } if (cell.walls[1]) { ctx.beginPath(); ctx.moveTo(x + cellSize, y); ctx.lineTo(x + cellSize, y + cellSize); ctx.stroke(); } if (cell.walls[2]) { ctx.beginPath(); ctx.moveTo(x + cellSize, y + cellSize); ctx.lineTo(x, y + cellSize); ctx.stroke(); } if (cell.walls[3]) { ctx.beginPath(); ctx.moveTo(x, y + cellSize); ctx.lineTo(x, y); ctx.stroke(); } } } function drawPath(x, y) { if (hasWon) return; ctx.fillStyle = "green"; ctx.fillRect( x * cellSize + cellSize / 4 + offsetX, y * cellSize + cellSize / 4 + offsetY, cellSize / 2, cellSize / 2 ); path.push({ x, y }); if (x === 0 && y === 0) { hasWon = true; path.forEach((point) => { ctx.fillStyle = "blue"; ctx.fillRect( point.x * cellSize + cellSize / 4 + offsetX, point.y * cellSize + cellSize / 4 + offsetY, cellSize / 2, cellSize / 2 ); }); popup.classList.remove("hidden"); } } function drawStartFinishText() { ctx.fillStyle = "white"; ctx.font = "20px Arial"; ctx.fillText( "Start", (cols - 1) * cellSize + 10 + offsetX, (rows - 1) * cellSize + 30 + offsetY ); ctx.fillText("Finish", offsetX - 40, offsetY + 10); } canvas.addEventListener("mousemove", (e) => { if (!isDrawing) return; const rect = canvas.getBoundingClientRect(); const x = Math.floor((e.clientX - rect.left - offsetX) / cellSize); const y = Math.floor((e.clientY - rect.top - offsetY) / cellSize); if (x >= 0 && y >= 0 && x < cols && y < rows) { drawPath(x, y); } }); canvas.addEventListener("mousedown", () => { isDrawing = true; }); canvas.addEventListener("mouseup", () => { isDrawing = false; }); // function autoSolve() { if (hasWon) return; let current = grid[grid.length - 1]; const solutionPath = []; const visited = new Set(); function solve(cell) { visited.add(cell); solutionPath.push(cell); if (cell.row === 0 && cell.column === 0) { return true; } const neighbors = [ grid[index(cell.row - 1, cell.column)], // top grid[index(cell.row, cell.column + 1)], // right grid[index(cell.row + 1, cell.column)], // bottom grid[index(cell.row, cell.column - 1)] // left ]; for (const nextCell of neighbors) { if (nextCell && !visited.has(nextCell) && !hasWallBetween(cell, nextCell)) { if (solve(nextCell)) { return true; } } } solutionPath.pop(); return false; } function hasWallBetween(a, b) { const x = a.column - b.column; const y = a.row - b.row; if (x === 1 && a.walls[3]) return true; // left wall if (x === -1 && a.walls[1]) return true; // right wall if (y === 1 && a.walls[0]) return true; // top wall if (y === -1 && a.walls[2]) return true; // bottom wall return false; } solve(current); solutionPath.forEach(cell => { drawPath(cell.column, cell.row); }); } // resetButton.addEventListener("click", resetMaze); autoSolveButton.addEventListener("click", autoSolve); rowSlider.addEventListener("input", resetMaze); colSlider.addEventListener("input", resetMaze); resetMaze();