Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Fraction Factory</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Arial', sans-serif; | |
| background: #000000; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| .game-container { | |
| background: white; | |
| border-radius: 20px; | |
| box-shadow: 0 20px 60px rgba(0,0,0,0.3); | |
| padding: 30px; | |
| max-width: 900px; | |
| width: 100%; | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 20px; | |
| } | |
| .header h1 { | |
| color: #667eea; | |
| font-size: 2.5em; | |
| margin-bottom: 10px; | |
| } | |
| .header p { | |
| color: #666; | |
| font-size: 1.1em; | |
| } | |
| .stats { | |
| display: flex; | |
| justify-content: space-around; | |
| margin-bottom: 20px; | |
| padding: 15px; | |
| background: #f0f0f0; | |
| border-radius: 10px; | |
| } | |
| .stat { | |
| text-align: center; | |
| } | |
| .stat-label { | |
| font-size: 0.9em; | |
| color: #666; | |
| margin-bottom: 5px; | |
| } | |
| .stat-value { | |
| font-size: 1.8em; | |
| font-weight: bold; | |
| color: #667eea; | |
| } | |
| .game-canvas { | |
| width: 100%; | |
| border: 3px solid #667eea; | |
| border-radius: 10px; | |
| background: #e8f4f8; | |
| display: block; | |
| } | |
| .controls { | |
| margin-top: 20px; | |
| display: flex; | |
| gap: 10px; | |
| flex-wrap: wrap; | |
| justify-content: center; | |
| } | |
| .answer-btn { | |
| padding: 15px 30px; | |
| font-size: 1.2em; | |
| font-weight: bold; | |
| border: 3px solid #667eea; | |
| background: white; | |
| color: #667eea; | |
| border-radius: 10px; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| min-width: 120px; | |
| } | |
| .answer-btn:hover { | |
| background: #667eea; | |
| color: white; | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3); | |
| } | |
| .answer-btn:active { | |
| transform: translateY(0); | |
| } | |
| .answer-btn.correct { | |
| background: #4ade80; | |
| border-color: #4ade80; | |
| color: white; | |
| } | |
| .answer-btn.wrong { | |
| background: #f87171; | |
| border-color: #f87171; | |
| color: white; | |
| } | |
| .start-screen, .game-over-screen { | |
| text-align: center; | |
| padding: 40px; | |
| } | |
| .start-btn, .restart-btn { | |
| padding: 20px 50px; | |
| font-size: 1.5em; | |
| font-weight: bold; | |
| background: #667eea; | |
| color: white; | |
| border: none; | |
| border-radius: 15px; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| margin-top: 20px; | |
| } | |
| .start-btn:hover, .restart-btn:hover { | |
| background: #764ba2; | |
| transform: scale(1.05); | |
| } | |
| .feedback { | |
| text-align: center; | |
| margin-top: 15px; | |
| font-size: 1.2em; | |
| font-weight: bold; | |
| min-height: 30px; | |
| } | |
| .feedback.correct { | |
| color: #4ade80; | |
| } | |
| .feedback.wrong { | |
| color: #f87171; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| .instructions { | |
| background: #fff3cd; | |
| padding: 15px; | |
| border-radius: 10px; | |
| margin-bottom: 20px; | |
| border: 2px solid #ffc107; | |
| } | |
| .instructions h3 { | |
| color: #856404; | |
| margin-bottom: 10px; | |
| } | |
| .instructions p { | |
| color: #856404; | |
| line-height: 1.6; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="game-container"> | |
| <div class="header"> | |
| <h1>🏭 Fraction Factory</h1> | |
| <p>Solve fraction problems before they fall off the conveyor belt!</p> | |
| </div> | |
| <div id="startScreen" class="start-screen"> | |
| <div class="instructions"> | |
| <h3>How to Play:</h3> | |
| <p>📦 Fraction problems move across the conveyor belt<br> | |
| ⚡ Solve each problem in 5-10 seconds<br> | |
| ✅ Correct answers keep the boxes moving<br> | |
| ❌ Wrong answers make boxes fall off<br> | |
| 🎯 Get as many correct as you can!</p> | |
| </div> | |
| <button class="start-btn" onclick="startGame()">Start Factory</button> | |
| </div> | |
| <div id="gameScreen" class="hidden"> | |
| <div class="stats"> | |
| <div class="stat"> | |
| <div class="stat-label">Score</div> | |
| <div class="stat-value" id="score">0</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">Streak</div> | |
| <div class="stat-value" id="streak">0</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">Lives</div> | |
| <div class="stat-value" id="lives">❤️❤️❤️</div> | |
| </div> | |
| </div> | |
| <canvas id="gameCanvas" class="game-canvas" width="800" height="300"></canvas> | |
| <div class="controls" id="controls"> | |
| <!-- Answer buttons will be added dynamically --> | |
| </div> | |
| <div class="feedback" id="feedback"></div> | |
| </div> | |
| <div id="gameOverScreen" class="game-over-screen hidden"> | |
| <h2 style="color: #667eea; font-size: 2.5em; margin-bottom: 20px;">Factory Closed!</h2> | |
| <p style="font-size: 1.3em; margin-bottom: 10px;">Final Score: <span id="finalScore" style="color: #667eea; font-weight: bold;">0</span></p> | |
| <p style="font-size: 1.3em; margin-bottom: 30px;">Correct Answers: <span id="correctCount" style="color: #4ade80; font-weight: bold;">0</span></p> | |
| <button class="restart-btn" onclick="restartGame()">Play Again</button> | |
| </div> | |
| </div> | |
| <script> | |
| // Game state | |
| let gameState = { | |
| score: 0, | |
| streak: 0, | |
| lives: 3, | |
| currentProblem: null, | |
| boxPosition: 0, | |
| isMoving: false, | |
| correctAnswers: 0, | |
| totalAnswers: 0 | |
| }; | |
| // JSON-driven problems (this would normally be loaded from a file) | |
| const problems = [ | |
| { question: "1/2 + 1/4", answer: "3/4", options: ["3/4", "1/4", "2/4", "3/6"] }, | |
| { question: "2/3 + 1/3", answer: "1", options: ["1", "3/6", "2/3", "3/9"] }, | |
| { question: "3/4 - 1/4", answer: "1/2", options: ["1/2", "2/4", "1/4", "2/8"] }, | |
| { question: "1/2 × 1/2", answer: "1/4", options: ["1/4", "1/2", "2/4", "1/8"] }, | |
| { question: "3/5 + 1/5", answer: "4/5", options: ["4/5", "4/10", "2/5", "3/10"] }, | |
| { question: "5/6 - 2/6", answer: "1/2", options: ["1/2", "3/6", "1/3", "2/3"] }, | |
| { question: "1/3 + 1/6", answer: "1/2", options: ["1/2", "2/9", "1/9", "2/6"] }, | |
| { question: "2/5 × 5/2", answer: "1", options: ["1", "10/10", "4/10", "5/5"] }, | |
| { question: "7/8 - 3/8", answer: "1/2", options: ["1/2", "4/8", "4/16", "1/4"] }, | |
| { question: "1/4 + 2/4", answer: "3/4", options: ["3/4", "3/8", "1/2", "2/4"] } | |
| ]; | |
| let canvas, ctx; | |
| let animationId; | |
| function startGame() { | |
| document.getElementById('startScreen').classList.add('hidden'); | |
| document.getElementById('gameScreen').classList.remove('hidden'); | |
| canvas = document.getElementById('gameCanvas'); | |
| ctx = canvas.getContext('2d'); | |
| gameState = { | |
| score: 0, | |
| streak: 0, | |
| lives: 3, | |
| currentProblem: null, | |
| boxPosition: 0, | |
| isMoving: false, | |
| correctAnswers: 0, | |
| totalAnswers: 0 | |
| }; | |
| updateStats(); | |
| loadNextProblem(); | |
| } | |
| function loadNextProblem() { | |
| // Pick a random problem | |
| const problem = problems[Math.floor(Math.random() * problems.length)]; | |
| gameState.currentProblem = problem; | |
| gameState.boxPosition = 0; | |
| gameState.isMoving = true; | |
| // Shuffle options | |
| const shuffledOptions = [...problem.options].sort(() => Math.random() - 0.5); | |
| // Create answer buttons | |
| const controls = document.getElementById('controls'); | |
| controls.innerHTML = ''; | |
| shuffledOptions.forEach(option => { | |
| const btn = document.createElement('button'); | |
| btn.className = 'answer-btn'; | |
| btn.textContent = option; | |
| btn.onclick = () => checkAnswer(option, btn); | |
| controls.appendChild(btn); | |
| }); | |
| document.getElementById('feedback').textContent = ''; | |
| document.getElementById('feedback').className = 'feedback'; | |
| animate(); | |
| } | |
| function animate() { | |
| if (!gameState.isMoving) return; | |
| // Clear canvas | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Draw conveyor belt | |
| ctx.fillStyle = '#666'; | |
| ctx.fillRect(0, 220, canvas.width, 80); | |
| // Draw conveyor belt lines | |
| ctx.strokeStyle = '#fbbf24'; | |
| ctx.lineWidth = 2; | |
| for (let i = 0; i < canvas.width; i += 40) { | |
| ctx.beginPath(); | |
| ctx.moveTo(i + (gameState.boxPosition % 40), 260); | |
| ctx.lineTo(i + 20 + (gameState.boxPosition % 40), 260); | |
| ctx.stroke(); | |
| } | |
| // Draw factory elements | |
| ctx.fillStyle = '#8b5cf6'; | |
| ctx.fillRect(10, 100, 60, 120); | |
| ctx.fillStyle = '#6d28d9'; | |
| ctx.fillRect(20, 110, 40, 40); | |
| // Draw box with problem | |
| const boxX = 100 + gameState.boxPosition; | |
| const boxY = 150; | |
| // Box shadow | |
| ctx.fillStyle = 'rgba(0,0,0,0.2)'; | |
| ctx.fillRect(boxX + 5, boxY + 5, 120, 80); | |
| // Box | |
| ctx.fillStyle = '#fbbf24'; | |
| ctx.fillRect(boxX, boxY, 120, 80); | |
| ctx.strokeStyle = '#f59e0b'; | |
| ctx.lineWidth = 3; | |
| ctx.strokeRect(boxX, boxY, 120, 80); | |
| // Problem text | |
| ctx.fillStyle = '#1f2937'; | |
| ctx.font = 'bold 20px Arial'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText(gameState.currentProblem.question, boxX + 60, boxY + 45); | |
| ctx.font = '16px Arial'; | |
| ctx.fillText('= ?', boxX + 60, boxY + 65); | |
| // Move box | |
| gameState.boxPosition += 1.5; | |
| // Check if box fell off | |
| if (gameState.boxPosition > canvas.width - 100) { | |
| handleTimeout(); | |
| return; | |
| } | |
| animationId = requestAnimationFrame(animate); | |
| } | |
| function checkAnswer(selected, btn) { | |
| if (!gameState.isMoving) return; | |
| gameState.isMoving = false; | |
| cancelAnimationFrame(animationId); | |
| gameState.totalAnswers++; | |
| const feedback = document.getElementById('feedback'); | |
| const allBtns = document.querySelectorAll('.answer-btn'); | |
| if (selected === gameState.currentProblem.answer) { | |
| // Correct answer | |
| gameState.score += 10 + (gameState.streak * 5); | |
| gameState.streak++; | |
| gameState.correctAnswers++; | |
| btn.classList.add('correct'); | |
| feedback.textContent = '✓ Correct! Great job!'; | |
| feedback.className = 'feedback correct'; | |
| allBtns.forEach(b => b.disabled = true); | |
| setTimeout(() => { | |
| loadNextProblem(); | |
| }, 1500); | |
| } else { | |
| // Wrong answer | |
| gameState.streak = 0; | |
| gameState.lives--; | |
| btn.classList.add('wrong'); | |
| feedback.textContent = `✗ Not quite! The answer was ${gameState.currentProblem.answer}`; | |
| feedback.className = 'feedback wrong'; | |
| // Show correct answer | |
| allBtns.forEach(b => { | |
| if (b.textContent === gameState.currentProblem.answer) { | |
| b.classList.add('correct'); | |
| } | |
| b.disabled = true; | |
| }); | |
| if (gameState.lives <= 0) { | |
| setTimeout(gameOver, 2000); | |
| } else { | |
| setTimeout(() => { | |
| loadNextProblem(); | |
| }, 2000); | |
| } | |
| } | |
| updateStats(); | |
| } | |
| function handleTimeout() { | |
| gameState.isMoving = false; | |
| gameState.streak = 0; | |
| gameState.lives--; | |
| gameState.totalAnswers++; | |
| const feedback = document.getElementById('feedback'); | |
| feedback.textContent = '⏰ Time\'s up! The box fell off!'; | |
| feedback.className = 'feedback wrong'; | |
| const allBtns = document.querySelectorAll('.answer-btn'); | |
| allBtns.forEach(b => { | |
| if (b.textContent === gameState.currentProblem.answer) { | |
| b.classList.add('correct'); | |
| } | |
| b.disabled = true; | |
| }); | |
| updateStats(); | |
| if (gameState.lives <= 0) { | |
| setTimeout(gameOver, 2000); | |
| } else { | |
| setTimeout(() => { | |
| loadNextProblem(); | |
| }, 2000); | |
| } | |
| } | |
| function updateStats() { | |
| document.getElementById('score').textContent = gameState.score; | |
| document.getElementById('streak').textContent = gameState.streak; | |
| let hearts = ''; | |
| for (let i = 0; i < gameState.lives; i++) { | |
| hearts += '❤️'; | |
| } | |
| for (let i = gameState.lives; i < 3; i++) { | |
| hearts += '🖤'; | |
| } | |
| document.getElementById('lives').textContent = hearts; | |
| } | |
| function gameOver() { | |
| document.getElementById('gameScreen').classList.add('hidden'); | |
| document.getElementById('gameOverScreen').classList.remove('hidden'); | |
| document.getElementById('finalScore').textContent = gameState.score; | |
| document.getElementById('correctCount').textContent = | |
| `${gameState.correctAnswers} out of ${gameState.totalAnswers}`; | |
| } | |
| function restartGame() { | |
| document.getElementById('gameOverScreen').classList.add('hidden'); | |
| startGame(); | |
| } | |
| </script> | |
| </body> | |
| </html> |