| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Memory Match</title> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| body { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| min-height: 100vh; |
| background: linear-gradient(45deg, #1a1a1a, #2d2d2d); |
| font-family: 'Segoe UI', sans-serif; |
| color: #fff; |
| } |
| |
| .game-container { |
| padding: 2rem; |
| } |
| |
| .controls { |
| display: flex; |
| gap: 2rem; |
| margin-bottom: 2rem; |
| align-items: center; |
| } |
| |
| .stats { |
| background: rgba(255, 255, 255, 0.1); |
| padding: 1rem 2rem; |
| border-radius: 10px; |
| font-size: 1.2rem; |
| } |
| |
| select { |
| padding: 0.5rem 1rem; |
| border-radius: 5px; |
| background: #3a3a3a; |
| color: #fff; |
| border: none; |
| cursor: pointer; |
| } |
| |
| .grid { |
| display: grid; |
| gap: 1rem; |
| perspective: 1000px; |
| } |
| |
| .card { |
| width: 100px; |
| height: 100px; |
| position: relative; |
| transform-style: preserve-3d; |
| transition: transform 0.6s; |
| cursor: pointer; |
| } |
| |
| .card.flipped { |
| transform: rotateY(180deg); |
| } |
| |
| .card-front, .card-back { |
| position: absolute; |
| width: 100%; |
| height: 100%; |
| backface-visibility: hidden; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 2rem; |
| border-radius: 10px; |
| box-shadow: 0 0 10px rgba(0,0,0,0.3); |
| } |
| |
| .card-front { |
| background: linear-gradient(135deg, #4a90e2, #357abd); |
| } |
| |
| .card-back { |
| background: white; |
| transform: rotateY(180deg); |
| color: #333; |
| } |
| |
| .matched { |
| animation: match 0.5s ease-in-out; |
| } |
| |
| @keyframes match { |
| 0%, 100% { transform: scale(1) rotateY(180deg); } |
| 50% { transform: scale(1.1) rotateY(180deg); } |
| } |
| |
| button { |
| padding: 0.5rem 1rem; |
| border: none; |
| border-radius: 5px; |
| background: #4a90e2; |
| color: white; |
| cursor: pointer; |
| transition: background 0.3s; |
| } |
| |
| button:hover { |
| background: #357abd; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="game-container"> |
| <div class="controls"> |
| <div class="stats"> |
| <span>Score: <span id="score">0</span></span> |
| <span style="margin-left: 1rem">Time: <span id="timer">0:00</span></span> |
| </div> |
| <select id="difficulty"> |
| <option value="easy">Easy (4x4)</option> |
| <option value="medium">Medium (6x6)</option> |
| <option value="hard">Hard (8x8)</option> |
| </select> |
| <button id="newGame">New Game</button> |
| </div> |
| <div class="grid" id="grid"></div> |
| </div> |
|
|
| <script> |
| const audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
| |
| const createSound = (frequency, type = 'sine') => { |
| const oscillator = audioContext.createOscillator(); |
| const gainNode = audioContext.createGain(); |
| oscillator.type = type; |
| oscillator.frequency.value = frequency; |
| oscillator.connect(gainNode); |
| gainNode.connect(audioContext.destination); |
| return { oscillator, gainNode }; |
| }; |
| |
| const playSound = (frequency, duration = 0.1) => { |
| const sound = createSound(frequency); |
| sound.gainNode.gain.setValueAtTime(0.1, audioContext.currentTime); |
| sound.oscillator.start(); |
| sound.oscillator.stop(audioContext.currentTime + duration); |
| }; |
| |
| class MemoryGame { |
| constructor() { |
| this.grid = document.getElementById('grid'); |
| this.scoreElement = document.getElementById('score'); |
| this.timerElement = document.getElementById('timer'); |
| this.difficultySelect = document.getElementById('difficulty'); |
| this.newGameBtn = document.getElementById('newGame'); |
| |
| this.cards = []; |
| this.flippedCards = []; |
| this.matchedPairs = 0; |
| this.score = 0; |
| this.timer = 0; |
| this.timerInterval = null; |
| |
| this.setupEventListeners(); |
| this.startNewGame(); |
| } |
| |
| setupEventListeners() { |
| this.newGameBtn.addEventListener('click', () => this.startNewGame()); |
| this.difficultySelect.addEventListener('change', () => this.startNewGame()); |
| } |
| |
| startNewGame() { |
| this.grid.innerHTML = ''; |
| this.cards = []; |
| this.flippedCards = []; |
| this.matchedPairs = 0; |
| this.score = 0; |
| this.scoreElement.textContent = '0'; |
| clearInterval(this.timerInterval); |
| this.timer = 0; |
| this.updateTimer(); |
| |
| const difficulty = this.difficultySelect.value; |
| const size = difficulty === 'easy' ? 4 : difficulty === 'medium' ? 6 : 8; |
| |
| this.grid.style.gridTemplateColumns = `repeat(${size}, 100px)`; |
| |
| const pairs = (size * size) / 2; |
| const symbols = this.generateSymbols(pairs); |
| this.createCards(symbols); |
| |
| this.startTimer(); |
| } |
| |
| generateSymbols(pairs) { |
| const emojis = ['๐จ', '๐ฎ', '๐ฒ', '๐ญ', '๐ช', '๐จ', '๐ฏ', '๐ฑ', '๐ณ', '๐ผ', |
| '๐ต', '๐น', '๐บ', '๐ธ', '๐ป', '๐ฌ', '๐ค', '๐ง', '๐', '๐', |
| '๐', '๐', '๐ฝ', '๐ฅ', '๐บ', '๐ท', '๐ธ', '๐น', '๐ผ', '๐', |
| '๐', '๐', '๐', '๐', '๐ซ', '๐ ', '๐ก', '๐ข']; |
| |
| const selectedEmojis = [...emojis].sort(() => Math.random() - 0.5).slice(0, pairs); |
| return [...selectedEmojis, ...selectedEmojis].sort(() => Math.random() - 0.5); |
| } |
| |
| createCards(symbols) { |
| symbols.forEach((symbol, index) => { |
| const card = document.createElement('div'); |
| card.className = 'card'; |
| card.innerHTML = ` |
| <div class="card-front">?</div> |
| <div class="card-back">${symbol}</div> |
| `; |
| card.addEventListener('click', () => this.flipCard(card, symbol)); |
| this.grid.appendChild(card); |
| this.cards.push(card); |
| }); |
| } |
| |
| flipCard(card, symbol) { |
| if (this.flippedCards.length === 2 || card.classList.contains('flipped') || |
| card.classList.contains('matched')) return; |
| |
| card.classList.add('flipped'); |
| playSound(440); |
| |
| this.flippedCards.push({ card, symbol }); |
| |
| if (this.flippedCards.length === 2) { |
| this.checkMatch(); |
| } |
| } |
| |
| checkMatch() { |
| const [card1, card2] = this.flippedCards; |
| |
| if (card1.symbol === card2.symbol) { |
| setTimeout(() => { |
| card1.card.classList.add('matched'); |
| card2.card.classList.add('matched'); |
| playSound(880, 0.2); |
| this.matchedPairs++; |
| this.score += 10; |
| this.scoreElement.textContent = this.score; |
| |
| if (this.matchedPairs === this.cards.length / 2) { |
| this.endGame(); |
| } |
| }, 500); |
| } else { |
| setTimeout(() => { |
| card1.card.classList.remove('flipped'); |
| card2.card.classList.remove('flipped'); |
| playSound(220, 0.2); |
| this.score = Math.max(0, this.score - 5); |
| this.scoreElement.textContent = this.score; |
| }, 1000); |
| } |
| |
| this.flippedCards = []; |
| } |
| |
| startTimer() { |
| this.timerInterval = setInterval(() => { |
| this.timer++; |
| this.updateTimer(); |
| }, 1000); |
| } |
| |
| updateTimer() { |
| const minutes = Math.floor(this.timer / 60); |
| const seconds = this.timer % 60; |
| this.timerElement.textContent = |
| `${minutes}:${seconds.toString().padStart(2, '0')}`; |
| } |
| |
| endGame() { |
| clearInterval(this.timerInterval); |
| setTimeout(() => { |
| alert(`Congratulations! You won!\nScore: ${this.score}\nTime: ${this.timerElement.textContent}`); |
| }, 500); |
| } |
| } |
| |
| new MemoryGame(); |
| </script> |
| </body> |
| </html> |