| document.addEventListener('DOMContentLoaded', () => { |
| const canvas = document.getElementById('maze-canvas'); |
| const ctx = canvas.getContext('2d'); |
| const timeDisplay = document.getElementById('time'); |
| const movesDisplay = document.getElementById('moves'); |
| const difficultySelect = document.getElementById('difficulty'); |
| const startBtn = document.getElementById('start-btn'); |
| const upBtn = document.getElementById('up-btn'); |
| const downBtn = document.getElementById('down-btn'); |
| const leftBtn = document.getElementById('left-btn'); |
| const rightBtn = document.getElementById('right-btn'); |
|
|
| |
| function resizeCanvas() { |
| const container = document.getElementById('maze-container'); |
| const size = Math.min(container.offsetWidth, container.offsetHeight); |
| canvas.width = size; |
| canvas.height = size; |
| } |
| |
| let maze = []; |
| let cellSize = 0; |
| let orientationEnabled = false; |
| let ufo = { x: 0, y: 0 }; |
| let exit = { x: 0, y: 0 }; |
| let moves = 0; |
| let time = 0; |
| let timer = null; |
| let gameRunning = false; |
| let mazeSize = 25; |
| |
| function handleOrientation(event) { |
| if (!gameRunning) return; |
| |
| const tiltThreshold = 15; |
| const beta = event.beta; |
| const gamma = event.gamma; |
| |
| if (beta > tiltThreshold) { |
| moveUfo(0, 1); |
| } else if (beta < -tiltThreshold) { |
| moveUfo(0, -1); |
| } |
| |
| if (gamma > tiltThreshold) { |
| moveUfo(1, 0); |
| } else if (gamma < -tiltThreshold) { |
| moveUfo(-1, 0); |
| } |
| } |
|
|
| |
| function initGame() { |
| resizeCanvas(); |
| window.addEventListener('resize', resizeCanvas); |
| |
| |
| if (window.DeviceOrientationEvent) { |
| window.addEventListener('deviceorientation', handleOrientation); |
| } |
| |
| switch(difficultySelect.value) { |
| case 'easy': mazeSize = 15; break; |
| case 'medium': mazeSize = 25; break; |
| case 'hard': mazeSize = 35; break; |
| } |
| |
| |
| generateMaze(); |
| drawMaze(); |
| |
| |
| document.addEventListener('keydown', handleKeyPress); |
| upBtn.addEventListener('click', () => moveUfo(0, -1)); |
| downBtn.addEventListener('click', () => moveUfo(0, 1)); |
| leftBtn.addEventListener('click', () => moveUfo(-1, 0)); |
| rightBtn.addEventListener('click', () => moveUfo(1, 0)); |
| |
| |
| startBtn.addEventListener('click', startGame); |
| } |
|
|
| |
| function generateMaze() { |
| |
| maze = Array(mazeSize).fill().map(() => Array(mazeSize).fill(1)); |
| |
| |
| ufo = { x: 0, y: 0 }; |
| maze[ufo.y][ufo.x] = 0; |
| |
| |
| exit = { x: mazeSize - 1, y: mazeSize - 1 }; |
| |
| |
| const stack = [{ x: ufo.x, y: ufo.y }]; |
| const directions = [ |
| { dx: 1, dy: 0 }, |
| { dx: -1, dy: 0 }, |
| { dx: 0, dy: 1 }, |
| { dx: 0, dy: -1 }, |
| { dx: 1, dy: 1 }, |
| { dx: -1, dy: -1 }, |
| { dx: 1, dy: -1 }, |
| { dx: -1, dy: 1 } |
| ]; |
| while (stack.length > 0) { |
| const current = stack[stack.length - 1]; |
| const neighbors = []; |
| |
| |
| for (const dir of directions) { |
| |
| const step = (Math.abs(dir.dx) + Math.abs(dir.dy) === 2) ? 3 : 2; |
| const nx = current.x + dir.dx * step; |
| const ny = current.y + dir.dy * step; |
| if (nx >= 0 && nx < mazeSize && ny >= 0 && ny < mazeSize && maze[ny][nx] === 1) { |
| neighbors.push({ x: nx, y: ny, dir }); |
| } |
| } |
| |
| if (neighbors.length > 0) { |
| const next = neighbors[Math.floor(Math.random() * neighbors.length)]; |
| |
| for (let i = 1; i <= (Math.abs(next.dir.dx) + Math.abs(next.dir.dy) === 2 ? 3 : 2); i++) { |
| const pathX = current.x + next.dir.dx * i; |
| const pathY = current.y + next.dir.dy * i; |
| if (pathX >= 0 && pathX < mazeSize && pathY >= 0 && pathY < mazeSize) { |
| maze[pathY][pathX] = 0; |
| } |
| } |
| stack.push({ x: next.x, y: next.y }); |
| } else { |
| stack.pop(); |
| } |
| } |
| |
| |
| maze[exit.y][exit.x] = 0; |
| } |
|
|
| |
| function drawMaze() { |
| cellSize = canvas.width / mazeSize; |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| |
| |
| ctx.fillStyle = '#374151'; |
| ctx.shadowColor = 'rgba(0, 0, 0, 0.3)'; |
| ctx.shadowBlur = 5; |
| ctx.shadowOffsetX = 2; |
| ctx.shadowOffsetY = 2; |
| for (let y = 0; y < mazeSize; y++) { |
| for (let x = 0; x < mazeSize; x++) { |
| if (maze[y][x] === 1) { |
| ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); |
| } |
| } |
| } |
| |
| |
| ctx.fillStyle = '#10B981'; |
| ctx.shadowColor = 'rgba(16, 185, 129, 0.5)'; |
| ctx.shadowBlur = 10; |
| ctx.fillRect(exit.x * cellSize, exit.y * cellSize, cellSize, cellSize); |
| |
| |
| drawUfo(); |
| } |
|
|
| |
| function drawUfo() { |
| const centerX = ufo.x * cellSize + cellSize / 2; |
| const centerY = ufo.y * cellSize + cellSize / 2; |
| const radius = cellSize * 0.3; |
| |
| ctx.beginPath(); |
| ctx.ellipse(centerX, centerY, radius, radius * 0.6, 0, 0, Math.PI * 2); |
| ctx.fillStyle = '#7C3AED'; |
| ctx.shadowColor = 'rgba(124, 58, 237, 0.5)'; |
| ctx.shadowBlur = 10; |
| ctx.fill(); |
| |
| |
| ctx.beginPath(); |
| ctx.ellipse(centerX, centerY - radius * 0.2, radius * 0.7, radius * 0.4, 0, 0, Math.PI * 2); |
| ctx.fillStyle = 'rgba(167, 139, 250, 0.7)'; |
| ctx.fill(); |
| |
| |
| ctx.beginPath(); |
| for (let i = 0; i < 5; i++) { |
| const lightX = centerX - radius * 0.6 + i * radius * 0.3; |
| ctx.arc(lightX, centerY + radius * 0.3, radius * 0.1, 0, Math.PI * 2); |
| } |
| ctx.fillStyle = '#F59E0B'; |
| ctx.shadowColor = 'rgba(245, 158, 11, 0.5)'; |
| ctx.shadowBlur = 5; |
| ctx.fill(); |
| } |
|
|
| |
| function handleKeyPress(e) { |
| if (!gameRunning) return; |
| |
| switch(e.key) { |
| case 'ArrowUp': moveUfo(0, -1); break; |
| case 'ArrowDown': moveUfo(0, 1); break; |
| case 'ArrowLeft': moveUfo(-1, 0); break; |
| case 'ArrowRight': moveUfo(1, 0); break; |
| } |
| } |
|
|
| |
| function moveUfo(dx, dy) { |
| if (!gameRunning || (orientationEnabled && dx === 0 && dy === 0)) return; |
| const newX = ufo.x + dx; |
| const newY = ufo.y + dy; |
| |
| |
| if (newX >= 0 && newX < mazeSize && newY >= 0 && newY < mazeSize && maze[newY][newX] === 0) { |
| ufo.x = newX; |
| ufo.y = newY; |
| moves++; |
| movesDisplay.textContent = moves; |
| drawMaze(); |
| |
| |
| if (ufo.x === exit.x && ufo.y === exit.y) { |
| endGame(true); |
| } |
| } |
| } |
| |
| document.getElementById('tilt-control').addEventListener('change', function() { |
| orientationEnabled = this.checked; |
| if (orientationEnabled && !gameRunning) { |
| alert('Tilt controls enabled! Tilt your device to move the UFO.'); |
| } |
| }); |
|
|
| |
| function startGame() { |
| if (gameRunning) return; |
| |
| moves = 0; |
| time = 0; |
| movesDisplay.textContent = moves; |
| timeDisplay.textContent = '00:00'; |
| |
| |
| generateMaze(); |
| drawMaze(); |
| |
| |
| gameRunning = true; |
| timer = setInterval(() => { |
| time++; |
| const minutes = Math.floor(time / 60).toString().padStart(2, '0'); |
| const seconds = (time % 60).toString().padStart(2, '0'); |
| timeDisplay.textContent = `${minutes}:${seconds}`; |
| }, 1000); |
| |
| startBtn.textContent = 'Restart Game'; |
| } |
|
|
| |
| function endGame(win) { |
| gameRunning = false; |
| clearInterval(timer); |
| |
| if (win) { |
| setTimeout(() => { |
| alert(`Congratulations! You escaped the maze in ${time} seconds with ${moves} moves!`); |
| }, 100); |
| } |
| } |
|
|
| |
| initGame(); |
| }); |