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