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