| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Memory Match Game</title> |
| | <style> |
| | :root { |
| | --card-width: 120px; |
| | --card-height: 150px; |
| | --primary-color: #2c3e50; |
| | --secondary-color: #3498db; |
| | } |
| | |
| | * { |
| | box-sizing: border-box; |
| | margin: 0; |
| | padding: 0; |
| | } |
| | |
| | body { |
| | min-height: 100vh; |
| | background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d); |
| | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | color: white; |
| | } |
| | |
| | .game-container { |
| | margin-top: 2rem; |
| | text-align: center; |
| | } |
| | |
| | .controls { |
| | margin: 1rem 0; |
| | display: flex; |
| | gap: 1rem; |
| | justify-content: center; |
| | align-items: center; |
| | } |
| | |
| | .btn { |
| | padding: 0.5rem 1rem; |
| | border: none; |
| | border-radius: 5px; |
| | background: rgba(255, 255, 255, 0.2); |
| | color: white; |
| | cursor: pointer; |
| | transition: all 0.3s ease; |
| | } |
| | |
| | .btn:hover { |
| | background: rgba(255, 255, 255, 0.3); |
| | } |
| | |
| | .stats { |
| | display: flex; |
| | gap: 2rem; |
| | margin: 1rem; |
| | font-size: 1.2rem; |
| | } |
| | |
| | .board { |
| | display: grid; |
| | gap: 10px; |
| | padding: 20px; |
| | perspective: 1000px; |
| | } |
| | |
| | .card { |
| | width: var(--card-width); |
| | height: var(--card-height); |
| | position: relative; |
| | transform-style: preserve-3d; |
| | transition: transform 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275); |
| | cursor: pointer; |
| | } |
| | |
| | .card.flipped { |
| | transform: rotateY(180deg); |
| | } |
| | |
| | .card.matched { |
| | transform: rotateY(180deg) scale(0.95); |
| | opacity: 0.8; |
| | } |
| | |
| | .card-face { |
| | position: absolute; |
| | width: 100%; |
| | height: 100%; |
| | backface-visibility: hidden; |
| | border-radius: 10px; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | font-size: 2rem; |
| | box-shadow: 0 4px 8px rgba(0,0,0,0.1); |
| | } |
| | |
| | .card-front { |
| | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | transform: rotateY(180deg); |
| | } |
| | |
| | .card-back { |
| | background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%); |
| | } |
| | |
| | .modal { |
| | position: fixed; |
| | top: 50%; |
| | left: 50%; |
| | transform: translate(-50%, -50%); |
| | background: rgba(0, 0, 0, 0.9); |
| | padding: 2rem; |
| | border-radius: 10px; |
| | text-align: center; |
| | display: none; |
| | } |
| | |
| | @keyframes match { |
| | 0%, 100% { transform: rotateY(180deg) scale(1); } |
| | 50% { transform: rotateY(180deg) scale(1.1); } |
| | } |
| | |
| | @keyframes mismatch { |
| | 0%, 100% { transform: rotateY(180deg); } |
| | 20%, 40%, 60%, 80% { transform: rotateY(180deg) translateX(-10px); } |
| | 30%, 50%, 70%, 90% { transform: rotateY(180deg) translateX(10px); } |
| | } |
| | </style> |
| | </head> |
| | <body> |
| | <div class="game-container"> |
| | <h1>Memory Match</h1> |
| | <div class="controls"> |
| | <select id="difficulty" class="btn"> |
| | <option value="8">Easy (4x4)</option> |
| | <option value="18">Medium (6x6)</option> |
| | <option value="32">Hard (8x8)</option> |
| | </select> |
| | <button class="btn" id="start">New Game</button> |
| | </div> |
| | <div class="stats"> |
| | <div>Moves: <span id="moves">0</span></div> |
| | <div>Time: <span id="timer">0:00</span></div> |
| | <div>Score: <span id="score">0</span></div> |
| | </div> |
| | <div class="board" id="board"></div> |
| | </div> |
| | <div class="modal" id="win-modal"> |
| | <h2>Congratulations!</h2> |
| | <p>You completed the game in <span id="final-time"></span></p> |
| | <p>Final Score: <span id="final-score"></span></p> |
| | <button class="btn" onclick="startGame()">Play Again</button> |
| | </div> |
| |
|
| | <script> |
| | const audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
| | let gameState = { |
| | flippedCards: [], |
| | matches: 0, |
| | moves: 0, |
| | score: 0, |
| | timer: 0, |
| | gameStarted: false, |
| | timerInterval: null |
| | }; |
| | |
| | const emojis = ['🎮', '🎲', '🎯', '🎨', '🎭', '🎪', '🎫', '🎬', |
| | '🎤', '🎧', '🎷', '🎸', '🎹', '🎺', '🎻', '🥁', |
| | '⚽️', '🏀', '🏈', '⚾️', '🎾', '🏐', '🏉', '🎱', |
| | '🏓', '🏸', '🥊', '🥋', '🎣', '⛳️', '🏹', '🎽']; |
| | |
| | function createAudioBuffer(frequency, duration) { |
| | const sampleRate = audioContext.sampleRate; |
| | const buffer = audioContext.createBuffer(1, duration * sampleRate, sampleRate); |
| | const data = buffer.getChannelData(0); |
| | |
| | for (let i = 0; i < buffer.length; i++) { |
| | data[i] = Math.sin(2 * Math.PI * frequency * i / sampleRate) * |
| | Math.exp(-5 * i / buffer.length); |
| | } |
| | |
| | return buffer; |
| | } |
| | |
| | const matchSound = createAudioBuffer(440, 0.1); |
| | const mismatchSound = createAudioBuffer(220, 0.1); |
| | |
| | function playSound(buffer) { |
| | const source = audioContext.createBufferSource(); |
| | source.buffer = buffer; |
| | source.connect(audioContext.destination); |
| | source.start(); |
| | } |
| | |
| | function createBoard() { |
| | const board = document.getElementById('board'); |
| | const difficulty = parseInt(document.getElementById('difficulty').value); |
| | const pairs = difficulty; |
| | const size = Math.sqrt(pairs * 2); |
| | |
| | board.style.gridTemplateColumns = `repeat(${size}, var(--card-width))`; |
| | board.innerHTML = ''; |
| | |
| | const selectedEmojis = emojis.slice(0, pairs); |
| | const cards = [...selectedEmojis, ...selectedEmojis] |
| | .sort(() => Math.random() - 0.5); |
| | |
| | cards.forEach((emoji, index) => { |
| | const card = document.createElement('div'); |
| | card.className = 'card'; |
| | card.dataset.value = emoji; |
| | card.dataset.index = index; |
| | card.innerHTML = ` |
| | <div class="card-face card-front">${emoji}</div> |
| | <div class="card-face card-back">?</div> |
| | `; |
| | card.addEventListener('click', flipCard); |
| | board.appendChild(card); |
| | }); |
| | } |
| | |
| | function flipCard() { |
| | if (!gameState.gameStarted) { |
| | startTimer(); |
| | gameState.gameStarted = true; |
| | } |
| | |
| | const card = this; |
| | if (card.classList.contains('flipped') || |
| | gameState.flippedCards.length >= 2) return; |
| | |
| | card.classList.add('flipped'); |
| | gameState.flippedCards.push(card); |
| | |
| | if (gameState.flippedCards.length === 2) { |
| | gameState.moves++; |
| | document.getElementById('moves').textContent = gameState.moves; |
| | checkMatch(); |
| | } |
| | } |
| | |
| | function checkMatch() { |
| | const [card1, card2] = gameState.flippedCards; |
| | const match = card1.dataset.value === card2.dataset.value; |
| | |
| | if (match) { |
| | playSound(matchSound); |
| | card1.classList.add('matched'); |
| | card2.classList.add('matched'); |
| | gameState.matches++; |
| | gameState.score += 100; |
| | card1.style.animation = card2.style.animation = 'match 0.5s ease'; |
| | |
| | if (gameState.matches === parseInt(document.getElementById('difficulty').value)) { |
| | endGame(); |
| | } |
| | } else { |
| | playSound(mismatchSound); |
| | card1.style.animation = card2.style.animation = 'mismatch 0.5s ease'; |
| | gameState.score = Math.max(0, gameState.score - 20); |
| | |
| | setTimeout(() => { |
| | card1.classList.remove('flipped'); |
| | card2.classList.remove('flipped'); |
| | }, 1000); |
| | } |
| | |
| | setTimeout(() => { |
| | card1.style.animation = card2.style.animation = ''; |
| | }, 500); |
| | |
| | document.getElementById('score').textContent = gameState.score; |
| | gameState.flippedCards = []; |
| | } |
| | |
| | function startTimer() { |
| | gameState.timerInterval = setInterval(() => { |
| | gameState.timer++; |
| | const minutes = Math.floor(gameState.timer / 60); |
| | const seconds = gameState.timer % 60; |
| | document.getElementById('timer').textContent = |
| | `${minutes}:${seconds.toString().padStart(2, '0')}`; |
| | }, 1000); |
| | } |
| | |
| | function endGame() { |
| | clearInterval(gameState.timerInterval); |
| | const modal = document.getElementById('win-modal'); |
| | document.getElementById('final-time').textContent = |
| | document.getElementById('timer').textContent; |
| | document.getElementById('final-score').textContent = gameState.score; |
| | modal.style.display = 'block'; |
| | } |
| | |
| | function startGame() { |
| | gameState = { |
| | flippedCards: [], |
| | matches: 0, |
| | moves: 0, |
| | score: 0, |
| | timer: 0, |
| | gameStarted: false, |
| | timerInterval: null |
| | }; |
| | |
| | document.getElementById('moves').textContent = '0'; |
| | document.getElementById('timer').textContent = '0:00'; |
| | document.getElementById('score').textContent = '0'; |
| | document.getElementById('win-modal').style.display = 'none'; |
| | |
| | createBoard(); |
| | clearInterval(gameState.timerInterval); |
| | } |
| | |
| | document.getElementById('start').addEventListener('click', startGame); |
| | document.getElementById('difficulty').addEventListener('change', startGame); |
| | |
| | |
| | startGame(); |
| | </script> |
| | </body> |
| | </html> |