| <!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> |