// Configuration const BOARD_HEIGHT = 4; const BOARD_WIDTH = 7; const FLIP_DELAY = 800; // ms before non‑matching cards flip back // SVG icons – fourteen distinct graphics (you can replace these with your own) const SVG_ICONS = [ ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, `` ]; // 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; } /* -------------------------------------------------------------------------- */ /* Pre‑load background music */ /* -------------------------------------------------------------------------- */ const bgMusic = new Audio('./background.mp3'); bgMusic.preload = 'auto'; // ensure the file is loaded early bgMusic.loop = true; // optional – keep playing while the game runs const flip = new Audio('./flip.mp3'); flip.preload = 'auto'; const match = new Audio('./match.mp3'); match.preload = 'auto'; /* -------------------------------------------------------------------------- */ /* Click handling */ /* -------------------------------------------------------------------------- */ function onCardClick(event) { if (lockBoard) return; const card = event.currentTarget; if (card.classList.contains('flipped')) return; // ignore already revealed cards flip.play(); // Start timer on the very first flip if (moves === 0 && seconds === 0) { startTimer(); bgMusic.play(); // ← play pre‑loaded audio } 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; match.play(); // Check for win condition if (matched === BOARD_HEIGHT * BOARD_WIDTH) { stopTimer(); setTimeout(() => { alert(`Congratulations! You completed the game in ${moves} moves and ${seconds} seconds.`); }, 300); bgMusic.stop(); } } 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();