intial commit
This commit is contained in:
commit
36828b276e
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SVG Match Game</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>SVG Memory Match</h1>
|
||||
<div class="stats">
|
||||
<span id="moves">Moves: 0</span>
|
||||
<span id="timer">Time: 0s</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section id="board" class="board"></section>
|
||||
</main>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
// Configuration
|
||||
const BOARD_SIZE = 4; // 4x4 grid → 16 cards (8 pairs)
|
||||
const FLIP_DELAY = 800; // ms before non‑matching cards flip back
|
||||
|
||||
// SVG icons – eight distinct graphics (you can replace these with your own)
|
||||
const SVG_ICONS = [
|
||||
`<svg viewBox="0 0 64 64" fill="#ff9800"><polygon points="32,4 39,24 60,24 42,38 48,58 32,46 16,58 22,38 4,24 25,24"/></svg>`,
|
||||
`<svg viewBox="0 0 64 64" fill="#4caf50"><circle cx="32" cy="32" r="28"/></svg>`,
|
||||
`<svg viewBox="0 0 64 64" fill="#2196F3"><rect x="12" y="12" width="40" height="40"/></svg>`,
|
||||
`<svg viewBox="0 0 64 64" fill="#e91e63"><path d="M32 4 L39 24 H60 L42 38 L48 58 L32 46 L16 58 L22 38 L4 24 H25z"/></svg>`,
|
||||
`<svg viewBox="0 0 64 64" fill="#9c27b0"><ellipse cx="32" cy="32" rx="28" ry="14"/></svg>`,
|
||||
`<svg viewBox="0 0 64 64" fill="#ff5722"><line x1="8" y1="8" x2="56" y2="56" stroke-width="8"/></svg>`,
|
||||
`<svg viewBox="0 0 64 64" fill="#03a9f4"><polygon points="32,8 40,24 58,24 44,36 50,54 32,44 14,54 20,36 6,24 24,24"/></svg>`,
|
||||
`<svg viewBox="0 0 64 64" fill="#8bc34a"><polygon points="32,4 39,24 60,24 42,38 48,58 32,46 16,58 22,38 4,24 25,24"/></svg>`
|
||||
];
|
||||
|
||||
// Game state variables
|
||||
let firstCard = null; // The previously flipped card
|
||||
let lockBoard = false; // Prevent interaction while cards are animating
|
||||
let moves = 0; // Number of attempts (pairs flipped)
|
||||
let matched = 0; // Number of cards that have been matched
|
||||
let timer = null; // Interval ID for the game timer
|
||||
let seconds = 0; // Elapsed seconds
|
||||
|
||||
// Cached DOM references
|
||||
const board = document.getElementById('board');
|
||||
const movesEl = document.getElementById('moves');
|
||||
const timerEl = document.getElementById('timer');
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Utility functions */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
function shuffle(array) {
|
||||
// Fisher‑Yates shuffle – in‑place
|
||||
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]];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function updateMoves() {
|
||||
movesEl.textContent = `Moves: ${moves}`;
|
||||
}
|
||||
|
||||
function updateTimer() {
|
||||
timerEl.textContent = `Time: ${seconds}s`;
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
if (timer) return; // already running
|
||||
timer = setInterval(() => {
|
||||
seconds++;
|
||||
updateTimer();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function stopTimer() {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Card creation */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
function createCard(svgMarkup, id) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card';
|
||||
card.dataset.id = id; // same id for the two matching cards
|
||||
|
||||
const inner = document.createElement('div');
|
||||
inner.className = 'card-inner';
|
||||
|
||||
const front = document.createElement('div');
|
||||
front.className = 'card-face card-front';
|
||||
front.innerHTML = svgMarkup; // SVG goes here
|
||||
|
||||
const back = document.createElement('div');
|
||||
back.className = 'card-face card-back';
|
||||
// back can stay empty or contain a pattern
|
||||
|
||||
inner.appendChild(front);
|
||||
inner.appendChild(back);
|
||||
card.appendChild(inner);
|
||||
|
||||
card.addEventListener('click', onCardClick);
|
||||
return card;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Click handling */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
function onCardClick(event) {
|
||||
if (lockBoard) return;
|
||||
const card = event.currentTarget;
|
||||
if (card.classList.contains('flipped')) return; // ignore already revealed cards
|
||||
|
||||
// Start timer on the very first flip
|
||||
if (moves === 0 && seconds === 0) startTimer();
|
||||
|
||||
card.classList.add('flipped');
|
||||
|
||||
if (!firstCard) {
|
||||
// This is the first card of the pair
|
||||
firstCard = card;
|
||||
return;
|
||||
}
|
||||
|
||||
// Second card of the pair
|
||||
moves++;
|
||||
updateMoves();
|
||||
|
||||
const isMatch = firstCard.dataset.id === card.dataset.id;
|
||||
if (isMatch) {
|
||||
// Keep both cards flipped
|
||||
matched += 2;
|
||||
firstCard = null;
|
||||
// Check for win condition
|
||||
if (matched === BOARD_SIZE * BOARD_SIZE) {
|
||||
stopTimer();
|
||||
setTimeout(() => {
|
||||
alert(`Congratulations! You completed the game in ${moves} moves and ${seconds} seconds.`);
|
||||
}, 300);
|
||||
}
|
||||
} else {
|
||||
// Not a match – flip back after a short delay
|
||||
lockBoard = true;
|
||||
setTimeout(() => {
|
||||
firstCard.classList.remove('flipped');
|
||||
card.classList.remove('flipped');
|
||||
firstCard = null;
|
||||
lockBoard = false;
|
||||
}, FLIP_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Game initialization */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
function initGame() {
|
||||
// Reset UI and state variables
|
||||
board.innerHTML = '';
|
||||
firstCard = null;
|
||||
lockBoard = false;
|
||||
moves = 0;
|
||||
matched = 0;
|
||||
seconds = 0;
|
||||
updateMoves();
|
||||
updateTimer();
|
||||
stopTimer();
|
||||
|
||||
// Build a deck: duplicate each SVG, assign an id, then shuffle
|
||||
const deck = [];
|
||||
SVG_ICONS.forEach((svg, idx) => {
|
||||
deck.push({svg, id: idx});
|
||||
deck.push({svg, id: idx}); // second copy for the pair
|
||||
});
|
||||
shuffle(deck);
|
||||
|
||||
// Create card elements and append them to the board
|
||||
deck.forEach(item => {
|
||||
const cardEl = createCard(item.svg, item.id);
|
||||
board.appendChild(cardEl);
|
||||
});
|
||||
}
|
||||
|
||||
// Kick‑off the game when the script loads
|
||||
initGame();
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* Basic reset */
|
||||
* { box-sizing: border-box; margin:0; padding:0; }
|
||||
body { font-family: Arial, sans-serif; background:#fafafa; display:flex; flex-direction:column; align-items:center; }
|
||||
|
||||
/* Header */
|
||||
header { text-align:center; margin:20px; }
|
||||
.stats { margin-top:8px; font-size:0.9rem; }
|
||||
|
||||
/* Board */
|
||||
.board {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||||
}
|
||||
|
||||
/* Card */
|
||||
.card {
|
||||
perspective: 1000px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.card-inner {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 100%; /* square */
|
||||
transform-style: preserve-3d;
|
||||
transition: transform 0.6s;
|
||||
}
|
||||
.card.flipped .card-inner { transform: rotateY(180deg); }
|
||||
|
||||
.card-face {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
backface-visibility: hidden;
|
||||
border-radius: 8px;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
}
|
||||
.card-back {
|
||||
background:#2196F3;
|
||||
}
|
||||
.card-front {
|
||||
background:#fff;
|
||||
transform: rotateY(180deg);
|
||||
border: 2px solid #ddd;
|
||||
}
|
||||
Loading…
Reference in New Issue