Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Math Whiskers: Гениальные математические котики</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: #f3f4f6; | |
| overflow-x: hidden; | |
| } | |
| .container { | |
| max-width: 600px; | |
| } | |
| .vibrant-button { | |
| background-image: linear-gradient(to right, #4ade80, #16a34a); | |
| transition: all 0.3s ease-in-out; | |
| } | |
| .vibrant-button:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| } | |
| @keyframes bounce { | |
| 0%, 100% { | |
| transform: translateY(-25%); | |
| animation-timing-function: cubic-bezier(0.8, 0, 1, 1); | |
| } | |
| 50% { | |
| transform: translateY(0); | |
| animation-timing-function: cubic-bezier(0, 0, 0.2, 1); | |
| } | |
| } | |
| .animate-bounce-slow { | |
| animation: bounce 2s infinite; | |
| } | |
| @keyframes pop-up { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.1); } | |
| 100% { transform: scale(1); } | |
| } | |
| .animate-pop { | |
| animation: pop-up 0.5s ease-in-out; | |
| } | |
| @keyframes shake { | |
| 10%, 90% { transform: translate3d(-1px, 0, 0); } | |
| 20%, 80% { transform: translate3d(2px, 0, 0); } | |
| 30%, 50%, 70% { transform: translate3d(-4px, 0, 0); } | |
| 40%, 60% { transform: translate3d(4px, 0, 0); } | |
| } | |
| .animate-shake { | |
| animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both; | |
| } | |
| @keyframes slide-in { | |
| from { transform: translateX(100%); opacity: 0; } | |
| to { transform: translateX(0); opacity: 1; } | |
| } | |
| .animate-slide-in { | |
| animation: slide-in 0.5s ease-out forwards; | |
| } | |
| .correct-answer-button { | |
| background-color: #10b981; | |
| color: white; | |
| animation: pop-up 0.5s ease-in-out; | |
| } | |
| .incorrect-answer-button { | |
| background-color: #ef4444; | |
| color: white; | |
| animation: shake 0.5s ease-in-out; | |
| } | |
| .confetti { | |
| position: absolute; | |
| width: 10px; | |
| height: 10px; | |
| background-color: var(--color); | |
| border-radius: 50%; | |
| opacity: 0; | |
| animation: confetti-fall 2.5s ease-out forwards; | |
| } | |
| @keyframes confetti-fall { | |
| 0% { | |
| transform: translateY(0) rotate(0deg); | |
| opacity: 1; | |
| } | |
| 100% { | |
| transform: translateY(100vh) rotate(720deg); | |
| opacity: 0; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="flex items-center justify-center min-h-screen p-4"> | |
| <div id="app" class="bg-white p-6 rounded-3xl shadow-2xl w-full container text-center transform transition-all duration-500 ease-in-out scale-100"> | |
| <div id="loading-screen" class="space-y-4"> | |
| <h1 class="text-2xl font-bold text-gray-800">Math Whiskers</h1> | |
| <p class="text-gray-600 flex items-center justify-center"> | |
| Загружаем пушистый интеллект... | |
| <span class="animate-bounce-slow ml-2 text-2xl">🐱</span> | |
| </p> | |
| </div> | |
| <div id="settings-screen" class="hidden space-y-6"> | |
| <h1 class="text-3xl font-bold text-gray-800">Math Whiskers 😺</h1> | |
| <p class="text-gray-600">Отточи свои математические навыки с пушистыми друзьями!</p> | |
| <div class="space-y-4"> | |
| <h2 class="text-xl font-semibold text-gray-700">Выбери своего математического зверя</h2> | |
| <div class="flex justify-center space-x-4"> | |
| <label class="cursor-pointer"> | |
| <input type="radio" name="avatar" value="🐱" class="hidden" checked> | |
| <span class="text-5xl transition-transform transform hover:scale-125 duration-200">🐱</span> | |
| </label> | |
| <label class="cursor-pointer"> | |
| <input type="radio" name="avatar" value="🦁" class="hidden"> | |
| <span class="text-5xl transition-transform transform hover:scale-125 duration-200">🦁</span> | |
| </label> | |
| <label class="cursor-pointer"> | |
| <input type="radio" name="avatar" value="🐻" class="hidden"> | |
| <span class="text-5xl transition-transform transform hover:scale-125 duration-200">🐻</span> | |
| </label> | |
| <label class="cursor-pointer"> | |
| <input type="radio" name="avatar" value="🐰" class="hidden"> | |
| <span class="text-5xl transition-transform transform hover:scale-125 duration-200">🐰</span> | |
| </label> | |
| </div> | |
| </div> | |
| <div class="space-y-4"> | |
| <h2 class="text-xl font-semibold text-gray-700">Уровень сложности</h2> | |
| <div class="flex justify-center space-x-4"> | |
| <label class="flex items-center space-x-2"> | |
| <input type="radio" name="difficulty" value="easy" class="form-radio text-blue-600 h-5 w-5" checked> | |
| <span class="text-gray-700">Котёнок (до 10)</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input type="radio" name="difficulty" value="medium" class="form-radio text-blue-600 h-5 w-5"> | |
| <span class="text-gray-700">Кот (до 20)</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input type="radio" name="difficulty" value="hard" class="form-radio text-blue-600 h-5 w-5"> | |
| <span class="text-gray-700">Тигр (до 50)</span> | |
| </label> | |
| </div> | |
| </div> | |
| <div class="space-y-4"> | |
| <h2 class="text-xl font-semibold text-gray-700">Математические операции</h2> | |
| <div class="flex justify-center space-x-4"> | |
| <label class="flex items-center space-x-2"> | |
| <input type="radio" name="operation" value="+" class="form-radio text-blue-600 h-5 w-5" checked> | |
| <span class="text-gray-700">Сложение</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input type="radio" name="operation" value="-" class="form-radio text-blue-600 h-5 w-5"> | |
| <span class="text-gray-700">Вычитание</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input type="radio" name="operation" value="both" class="form-radio text-blue-600 h-5 w-5"> | |
| <span class="text-gray-700">Обе операции</span> | |
| </label> | |
| </div> | |
| </div> | |
| <div class="space-y-4"> | |
| <h2 class="text-xl font-semibold text-gray-700">Количество вопросов</h2> | |
| <div class="flex justify-center space-x-4"> | |
| <label class="flex items-center space-x-2"> | |
| <input type="radio" name="numQuestions" value="10" class="form-radio text-blue-600 h-5 w-5" checked> | |
| <span class="text-gray-700">10</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input type="radio" name="numQuestions" value="15" class="form-radio text-blue-600 h-5 w-5"> | |
| <span class="text-gray-700">15</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input type="radio" name="numQuestions" value="20" class="form-radio text-blue-600 h-5 w-5"> | |
| <span class="text-gray-700">20</span> | |
| </label> | |
| </div> | |
| </div> | |
| <button id="start-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-full shadow-lg transition-all duration-300 transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-blue-300"> | |
| Начать тренировку | |
| </button> | |
| </div> | |
| <div id="quiz-screen" class="hidden space-y-6"> | |
| <div class="flex flex-col items-center mb-4"> | |
| <div id="quiz-avatar-display" class="text-7xl mb-4"></div> | |
| </div> | |
| <h2 id="question-text" class="text-5xl font-bold text-gray-800 mb-6"></h2> | |
| <div id="answer-buttons" class="grid grid-cols-2 gap-4"></div> | |
| <p id="message-text" class="min-h-[2rem] text-xl font-semibold"></p> | |
| <button id="show-hint-btn" class="mt-4 bg-yellow-400 hover:bg-yellow-500 text-white font-bold py-2 px-4 rounded-full shadow-md transition-all duration-300 transform hover:scale-105"> | |
| Подсказка 🐾 | |
| </button> | |
| <div id="emoji-hint-display" class="text-6xl flex items-center justify-center hidden"></div> | |
| <div class="space-y-2 mt-4"> | |
| <p class="text-sm text-gray-600">Correct: <span id="correct-count">0</span> | Incorrect: <span id="incorrect-count">0</span></p> | |
| <div id="score-cells-container" class="flex justify-center flex-wrap gap-1"></div> | |
| </div> | |
| </div> | |
| <div id="results-screen" class="hidden space-y-6"> | |
| <h2 class="text-3xl font-bold text-gray-800">Тренировка завершена!</h2> | |
| <p id="final-feedback" class="text-2xl font-semibold text-gray-700"></p> | |
| <p id="final-score" class="text-xl text-gray-600 mt-2"></p> | |
| <p class="text-sm text-gray-500 mb-2 flex items-center justify-center"> | |
| Ваш профиль: <span id="user-avatar-display" class="ml-2 text-2xl"></span> | |
| </p> | |
| <button id="restart-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-full shadow-lg transition-all duration-300 transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-blue-300"> | |
| Попробовать снова | |
| </button> | |
| </div> | |
| </div> | |
| <div id="confetti-container" class="absolute inset-0 z-50 pointer-events-none"></div> | |
| <script> | |
| // Elements | |
| const loadingScreen = document.getElementById('loading-screen'); | |
| const settingsScreen = document.getElementById('settings-screen'); | |
| const quizScreen = document.getElementById('quiz-screen'); | |
| const resultsScreen = document.getElementById('results-screen'); | |
| const startBtn = document.getElementById('start-btn'); | |
| const restartBtn = document.getElementById('restart-btn'); | |
| const questionText = document.getElementById('question-text'); | |
| const answerButtons = document.getElementById('answer-buttons'); | |
| const messageText = document.getElementById('message-text'); | |
| const correctCount = document.getElementById('correct-count'); | |
| const incorrectCount = document.getElementById('incorrect-count'); | |
| const scoreCellsContainer = document.getElementById('score-cells-container'); | |
| const finalFeedbackText = document.getElementById('final-feedback'); | |
| const finalScoreText = document.getElementById('final-score'); | |
| const userAvatarDisplay = document.getElementById('user-avatar-display'); | |
| const quizAvatarDisplay = document.getElementById('quiz-avatar-display'); | |
| const emojiHintDisplay = document.getElementById('emoji-hint-display'); | |
| const showHintBtn = document.getElementById('show-hint-btn'); | |
| const confettiContainer = document.getElementById('confetti-container'); | |
| // Audio Context | |
| const audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| function playSound(frequency, duration) { | |
| const oscillator = audioContext.createOscillator(); | |
| const gainNode = audioContext.createGain(); | |
| oscillator.type = 'sine'; | |
| oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime); | |
| gainNode.gain.setValueAtTime(0, audioContext.currentTime); | |
| gainNode.gain.linearRampToValueAtTime(1, audioContext.currentTime + 0.01); | |
| gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + duration); | |
| oscillator.connect(gainNode); | |
| gainNode.connect(audioContext.destination); | |
| oscillator.start(audioContext.currentTime); | |
| oscillator.stop(audioContext.currentTime + duration); | |
| } | |
| // State | |
| let currentQuestionIndex = 0; | |
| let score = { correct: 0, incorrect: 0 }; | |
| let questions = []; | |
| let numQuestions = 10; | |
| let difficulty = 'easy'; | |
| let operation = '+'; | |
| let avatar = '🐱'; | |
| let questionResults = []; | |
| // Helper functions | |
| function shuffleArray(array) { | |
| for (let i = array.length - 1; i > 0; i--) { | |
| const j = Math.floor(Math.random() * (i + 1)); | |
| [array[i], array[j]] = [array[j], array[i]]; | |
| } | |
| } | |
| function generateAppleEmojis(count) { | |
| return '🍎'.repeat(count); | |
| } | |
| function generateQuestion(index) { | |
| let startMax, endMax; | |
| if (difficulty === 'easy') { | |
| startMax = 5; | |
| endMax = 10; | |
| } else if (difficulty === 'medium') { | |
| startMax = 10; | |
| endMax = 20; | |
| } else { | |
| startMax = 20; | |
| endMax = 50; | |
| } | |
| const progressionFactor = index / (numQuestions - 1); | |
| const currentMaxNumber = Math.floor(startMax + (endMax - startMax) * progressionFactor); | |
| let a = Math.floor(Math.random() * currentMaxNumber) + 1; | |
| let b = Math.floor(Math.random() * currentMaxNumber) + 1; | |
| let selectedOperation = operation; | |
| if (selectedOperation === 'both') { | |
| selectedOperation = Math.random() > 0.5 ? '+' : '-'; | |
| } | |
| if (selectedOperation === '-') { | |
| if (b > a) { | |
| [a, b] = [b, a]; | |
| } | |
| } | |
| let correctAnswer; | |
| if (selectedOperation === '+') { | |
| correctAnswer = a + b; | |
| } else { | |
| correctAnswer = a - b; | |
| } | |
| let answers = [correctAnswer]; | |
| while (answers.length < 4) { | |
| let incorrectAnswer = correctAnswer + Math.floor(Math.random() * 5) * (Math.random() > 0.5 ? 1 : -1); | |
| if (!answers.includes(incorrectAnswer) && incorrectAnswer >= 0) { | |
| answers.push(incorrectAnswer); | |
| } | |
| } | |
| shuffleArray(answers); | |
| return { | |
| text: `${a} ${selectedOperation} ${b} = ?`, | |
| correctAnswer: correctAnswer, | |
| answers: answers, | |
| a: a, | |
| b: b, | |
| operation: selectedOperation | |
| }; | |
| } | |
| function startTest() { | |
| // Get settings | |
| difficulty = document.querySelector('input[name="difficulty"]:checked').value; | |
| numQuestions = parseInt(document.querySelector('input[name="numQuestions"]:checked').value); | |
| avatar = document.querySelector('input[name="avatar"]:checked').value; | |
| operation = document.querySelector('input[name="operation"]:checked').value; | |
| currentQuestionIndex = 0; | |
| score = { correct: 0, incorrect: 0 }; | |
| questionResults = []; | |
| questions = []; | |
| for (let i = 0; i < numQuestions; i++) { | |
| questions.push(generateQuestion(i)); | |
| } | |
| settingsScreen.classList.add('hidden'); | |
| quizScreen.classList.remove('hidden'); | |
| quizScreen.classList.add('animate-slide-in'); | |
| userAvatarDisplay.textContent = avatar; | |
| quizAvatarDisplay.textContent = avatar; | |
| showQuestion(); | |
| updateScoreCells(); | |
| } | |
| function showQuestion() { | |
| if (currentQuestionIndex >= numQuestions) { | |
| showResults(); | |
| return; | |
| } | |
| messageText.textContent = ''; | |
| answerButtons.innerHTML = ''; | |
| emojiHintDisplay.classList.add('hidden'); | |
| const currentQuestion = questions[currentQuestionIndex]; | |
| questionText.textContent = currentQuestion.text; | |
| questionText.classList.remove('animate-pop', 'animate-shake'); | |
| if (currentQuestion.a <= 10 && currentQuestion.b <= 10) { | |
| const aEmojis = generateAppleEmojis(currentQuestion.a); | |
| const bEmojis = generateAppleEmojis(currentQuestion.b); | |
| const opEmoji = currentQuestion.operation === '+' ? '➕' : '➖'; | |
| emojiHintDisplay.innerHTML = `${aEmojis} ${opEmoji} ${bEmojis} = `; | |
| } else { | |
| emojiHintDisplay.innerHTML = ''; | |
| } | |
| currentQuestion.answers.forEach(answer => { | |
| const button = document.createElement('button'); | |
| button.textContent = answer; | |
| button.classList.add('bg-gray-200', 'hover:bg-gray-300', 'text-gray-800', 'font-bold', 'py-4', 'px-4', 'rounded-xl', 'transition-all', 'duration-200', 'transform', 'hover:scale-105', 'text-2xl', 'focus:outline-none'); | |
| button.onclick = () => checkAnswer(button, answer, currentQuestion.correctAnswer); | |
| answerButtons.appendChild(button); | |
| }); | |
| } | |
| function checkAnswer(selectedButton, selectedAnswer, correctAnswer) { | |
| Array.from(answerButtons.children).forEach(btn => btn.disabled = true); | |
| const isCorrect = selectedAnswer === correctAnswer; | |
| questionResults.push(isCorrect); | |
| if (isCorrect) { | |
| messageText.textContent = `Правильно! Мур-р-р! 🎉`; | |
| messageText.classList.add('text-green-600'); | |
| messageText.classList.remove('text-red-600'); | |
| score.correct++; | |
| playSound(440, 0.2); | |
| questionText.classList.add('animate-pop'); | |
| selectedButton.classList.remove('hover:bg-gray-300'); | |
| selectedButton.classList.add('correct-answer-button'); | |
| } else { | |
| messageText.textContent = `Ошибка... 😿 Правильный ответ: ${correctAnswer}.`; | |
| messageText.classList.add('text-red-600'); | |
| messageText.classList.remove('text-green-600'); | |
| score.incorrect++; | |
| playSound(220, 0.2); | |
| questionText.classList.add('animate-shake'); | |
| selectedButton.classList.remove('hover:bg-gray-300'); | |
| selectedButton.classList.add('incorrect-answer-button'); | |
| Array.from(answerButtons.children).forEach(btn => { | |
| if (parseInt(btn.textContent) === correctAnswer) { | |
| btn.classList.add('border-4', 'border-green-500'); | |
| } | |
| }); | |
| } | |
| updateScoreCells(); | |
| setTimeout(() => { | |
| currentQuestionIndex++; | |
| showQuestion(); | |
| }, 2000); | |
| } | |
| function updateScoreCells() { | |
| scoreCellsContainer.innerHTML = ''; | |
| questionResults.forEach(isCorrect => { | |
| const cell = document.createElement('div'); | |
| cell.classList.add('w-6', 'h-6', 'rounded-full', 'shadow-md'); | |
| if (isCorrect) { | |
| cell.classList.add('bg-green-500'); | |
| } else { | |
| cell.classList.add('bg-red-500'); | |
| } | |
| scoreCellsContainer.appendChild(cell); | |
| }); | |
| const remainingQuestions = numQuestions - questionResults.length; | |
| for (let i = 0; i < remainingQuestions; i++) { | |
| const cell = document.createElement('div'); | |
| cell.classList.add('w-6', 'h-6', 'rounded-full', 'bg-gray-300'); | |
| scoreCellsContainer.appendChild(cell); | |
| } | |
| correctCount.textContent = score.correct; | |
| incorrectCount.textContent = score.incorrect; | |
| } | |
| function startConfettiAnimation() { | |
| const colors = ['#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#03a9f4', '#00bcd4', '#009688', '#4caf50', '#8bc34a', '#cddc39', '#ffeb3b', '#ffc107', '#ff9800', '#ff5722']; | |
| const numberOfConfetti = 50; | |
| for (let i = 0; i < numberOfConfetti; i++) { | |
| const confetti = document.createElement('div'); | |
| confetti.classList.add('confetti'); | |
| confetti.style.left = `${Math.random() * 100}vw`; | |
| confetti.style.animationDelay = `${Math.random() * 2.5}s`; | |
| confetti.style.setProperty('--color', colors[Math.floor(Math.random() * colors.length)]); | |
| confettiContainer.appendChild(confetti); | |
| } | |
| setTimeout(() => { | |
| confettiContainer.innerHTML = ''; | |
| }, 3000); | |
| } | |
| function showResults() { | |
| quizScreen.classList.add('hidden'); | |
| resultsScreen.classList.remove('hidden'); | |
| finalScoreText.textContent = `Your score: ${score.correct} out of ${numQuestions}`; | |
| const percentage = (score.correct / numQuestions) * 100; | |
| let feedbackText = ''; | |
| if (percentage >= 80) { | |
| feedbackText = 'Идеально! Ты математический гений! 🎉'; | |
| startConfettiAnimation(); | |
| } else if (percentage >= 70) { | |
| feedbackText = 'Отличный результат! Продолжай тренироваться! ✨'; | |
| } else { | |
| feedbackText = 'Не переживай - с каждой тренировкой будет лучше! 💪'; | |
| } | |
| finalFeedbackText.textContent = feedbackText; | |
| } | |
| function resetGame() { | |
| resultsScreen.classList.add('hidden'); | |
| settingsScreen.classList.remove('hidden'); | |
| currentQuestionIndex = 0; | |
| score = { correct: 0, incorrect: 0 }; | |
| questions = []; | |
| questionResults = []; | |
| messageText.textContent = ''; | |
| updateScoreCells(); | |
| } | |
| // Event listeners | |
| startBtn.addEventListener('click', startTest); | |
| restartBtn.addEventListener('click', resetGame); | |
| showHintBtn.addEventListener('click', () => { | |
| emojiHintDisplay.classList.remove('hidden'); | |
| }); | |
| document.querySelectorAll('input[name="avatar"]').forEach(radio => { | |
| radio.addEventListener('change', () => { | |
| document.querySelectorAll('input[name="avatar"] + span').forEach(label => { | |
| label.classList.remove('ring-4', 'ring-blue-500', 'ring-offset-2'); | |
| }); | |
| if (radio.checked) { | |
| radio.nextElementSibling.classList.add('ring-4', 'ring-blue-500', 'ring-offset-2'); | |
| } | |
| }); | |
| }); | |
| // Initialize | |
| window.onload = function() { | |
| loadingScreen.classList.add('hidden'); | |
| settingsScreen.classList.remove('hidden'); | |
| document.querySelector('input[name="avatar"]:checked').nextElementSibling.classList.add('ring-4', 'ring-blue-500', 'ring-offset-2'); | |
| }; | |
| </script> | |
| </body> | |
| </html> | |