document.addEventListener('DOMContentLoaded', () => { const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const startScreen = document.getElementById('startScreen'); const gameOverScreen = document.getElementById('gameOverScreen'); const startBtn = document.getElementById('startBtn'); const restartBtn = document.getElementById('restartBtn'); const scoreDisplay = document.getElementById('scoreDisplay'); // Set canvas size function resizeCanvas() { const container = canvas.parentElement; canvas.width = container.clientWidth; canvas.height = container.clientHeight; } resizeCanvas(); window.addEventListener('resize', resizeCanvas); // Game variables let ball = { x: 0, y: 0, radius: 110, minRadius: 5, maxRadius: 200, color: '#8B5CF6', speed: 3, dx: 0, dy: 0 // Start with no movement }; let rings = []; let currentRing = 0; let gameActive = false; let animationId; let touchStartX = 0; let touchStartY = 0; // Initialize game function initGame() { ball.x = canvas.width / 2; ball.y = canvas.height - 100; ball.dx = 0; ball.dy = 0; // Start with no movement (let player control) rings = []; currentRing = 0; // Create 100 rings with random gaps for (let i = 0; i < 100; i++) { rings.push({ x: canvas.width / 2, y: 50 + i * 60, radius: 40 + i * 2, thickness: 8, gapStart: Math.random() * Math.PI * 2, gapSize: Math.PI / 3 + Math.random() * Math.PI / 6, color: i % 2 === 0 ? '#7C3AED' : '#6D28D9' }); } gameActive = true; startScreen.classList.add('hidden'); gameOverScreen.classList.add('hidden'); animate(); } // Draw ball function drawBall() { ctx.beginPath(); ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2); ctx.fillStyle = ball.color; ctx.fill(); ctx.closePath(); // Add shine effect ctx.beginPath(); ctx.arc(ball.x - ball.radius/3, ball.y - ball.radius/3, ball.radius/4, 0, Math.PI * 2); ctx.fillStyle = 'rgba(255, 255, 255, 0.4)'; ctx.fill(); ctx.closePath(); // Draw size counter ctx.font = `${Math.min(20, ball.radius/2)}px Arial`; ctx.fillStyle = 'white'; ctx.textAlign = 'center'; ctx.fillText(Math.round(ball.radius), ball.x, ball.y + 5); } // Draw rings function drawRings() { for (let i = currentRing; i < Math.min(currentRing + 10, rings.length); i++) { const ring = rings[i]; // Draw the ring with gap ctx.beginPath(); ctx.arc(ring.x, ring.y, ring.radius, ring.gapStart + ring.gapSize, ring.gapStart, true); ctx.lineWidth = ring.thickness; ctx.strokeStyle = ring.color; ctx.stroke(); ctx.closePath(); // Add glow effect ctx.beginPath(); ctx.arc(ring.x, ring.y, ring.radius + 5, 0, Math.PI * 2); ctx.strokeStyle = 'rgba(167, 139, 250, 0.3)'; ctx.lineWidth = 2; ctx.stroke(); ctx.closePath(); } } // Check collision with current ring function checkCollision() { const ring = rings[currentRing]; const distance = Math.sqrt((ball.x - ring.x) ** 2 + (ball.y - ring.y) ** 2); // Check if ball is within ring boundaries if (distance > ring.radius - ring.thickness/2 && distance < ring.radius + ring.thickness/2) { // Calculate angle to check if ball is passing through the gap const angle = Math.atan2(ball.y - ring.y, ball.x - ring.x) + Math.PI; const normalizedAngle = (angle - ring.gapStart + Math.PI * 2) % (Math.PI * 2); if (normalizedAngle > ring.gapSize) { return true; // Collision detected } } else if (distance < ring.radius - ring.thickness/2) { return true; // Collision with inner part } return false; } // Update game state function update() { // Move ball (auto movement only) ball.x += ball.dx; ball.y += ball.dy; // Wall collision - change size if (ball.x - ball.radius < 0 || ball.x + ball.radius > canvas.width) { ball.dx = -ball.dx; ball.radius = Math.max(ball.minRadius, Math.min(ball.maxRadius, ball.radius + (ball.dx > 0 ? -5 : 5))); } if (ball.y - ball.radius < 0 || ball.y + ball.radius > canvas.height) { ball.dy = -ball.dy; ball.radius = Math.max(ball.minRadius, Math.min(ball.maxRadius, ball.radius + (ball.dy > 0 ? -5 : 5))); } // Check if ball passed current ring if (currentRing < rings.length) { const ring = rings[currentRing]; if (ball.y < ring.y - ring.radius - ring.thickness/2) { currentRing++; // Check if game is won if (currentRing >= rings.length) { gameWon(); return; } } // Check collision with current ring if (checkCollision()) { gameOver(); return; } } } // Game over function gameOver() { gameActive = false; cancelAnimationFrame(animationId); scoreDisplay.innerHTML = `You reached ring: ${currentRing}`; gameOverScreen.classList.remove('hidden'); } // Game won function gameWon() { gameActive = false; cancelAnimationFrame(animationId); scoreDisplay.innerHTML = `Congratulations! You completed all 100 rings!`; gameOverScreen.classList.remove('hidden'); } // Animation loop function animate() { animationId = requestAnimationFrame(animate); ctx.clearRect(0, 0, canvas.width, canvas.height); drawRings(); drawBall(); if (gameActive) { update(); } } // Event listeners startBtn.addEventListener('click', () => { initGame(); // Enable controls after game starts document.addEventListener('keydown', handleKeyDown); document.addEventListener('keyup', handleKeyUp); canvas.addEventListener('touchstart', handleTouchStart); canvas.addEventListener('touchmove', handleTouchMove); }); restartBtn.addEventListener('click', () => { initGame(); // Re-enable controls on restart document.addEventListener('keydown', handleKeyDown); document.addEventListener('keyup', handleKeyUp); canvas.addEventListener('touchstart', handleTouchStart); canvas.addEventListener('touchmove', handleTouchMove); }); // Control handlers function handleKeyDown(e) { if (!gameActive) return; if (e.key === 'ArrowLeft') { ball.dx = -ball.speed; } else if (e.key === 'ArrowRight') { ball.dx = ball.speed; } else if (e.key === 'ArrowUp') { ball.dy = -ball.speed; } else if (e.key === 'ArrowDown') { ball.dy = ball.speed; } } function handleKeyUp(e) { if (!gameActive) return; if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { ball.dx = 0; } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { ball.dy = 0; } } function handleTouchStart(e) { if (!gameActive) return; touchStartX = e.touches[0].clientX; touchStartY = e.touches[0].clientY; } function handleTouchMove(e) { if (!gameActive) return; e.preventDefault(); const touchX = e.touches[0].clientX; const touchY = e.touches[0].clientY; const diffX = touchX - touchStartX; const diffY = touchY - touchStartY; if (Math.abs(diffX) > 5) { // Threshold to prevent accidental movement ball.dx = diffX > 0 ? ball.speed : -ball.speed; } if (Math.abs(diffY) > 5) { ball.dy = diffY > 0 ? ball.speed : -ball.speed; } } });