<div class="container">
<h1 class="text-center mt-5 mb-4">JavaScript Memory Game</h1>
<h3 class="text-center mb-4" id="timer">Time left: 1:00</h3>
<p class="text-center mt-4">
Click on the cards to reveal their numbers and find matching pairs. If
you find a match, the cards stay open; otherwise, they will be hidden
again. The game ends when all pairs are matched. Watch the timer to
track your time. Good luck!
</p>
<div class="wrapper" id="wrapper">
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
<div class="card">
<div class="card-front"></div>
<div class="card-back"></div>
</div>
</div>
<div class="d-flex flex-column justify-content-center align-items-center position-absolute w-100 h-100" id="gameOverContainer">
<h2 class="gameOver"></h2>
<button class="btn btn-primary mt-4" id="restart">Play Again</button>
</div>
</div>
@import url("https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap");
body {
background-color: rgb(247, 248, 249);
font-family: "DM Mono", monospace;
}
.wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
max-width: 400px;
margin: 50px auto;
}
.card {
width: 100px;
height: 100px;
perspective: 1000px;
cursor: pointer;
position: relative;
transform-style: preserve-3d;
transition: transform 0.6s;
}
.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;
border: 1px solid #000;
font-size: 3rem;
font-weight: 800;
}
.card-front {
background-color: #fff;
transform: rotateY(180deg);
}
.card-back {
background-color: #3498db;
}
#gameOverContainer {
visibility: hidden;
opacity: 0;
background-color: rgba(0, 0, 0, 0.5);
top: 0;
left: 0;
}
#gameOverContainer.show {
visibility: visible;
opacity: 1;
transition: opacity 0.7s linear;
}
h2 {
font-size: 3rem;
}
document.addEventListener("DOMContentLoaded", (event) => {
const restart = document.getElementById("restart");
const timerElement = document.getElementById("timer");
const gameOverContainer = document.getElementById("gameOverContainer");
const gameOverMessage = document.querySelector(".gameOver");
let timerStarted = false;
let clickedCards = [];
let matchedCards = 0;
const numbers = [...Array(2)].flatMap(() => [1, 2, 3, 4, 5, 6, 7, 8]);
const cards = document.querySelectorAll(".card");
let timer;
let timeLeft = 60;
restart.addEventListener("click", restartGame);
function startTimer() {
timer = setInterval(() => {
timeLeft--;
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerElement.textContent = `Time left: ${minutes}:${
seconds < 10 ? "0" : ""
}${seconds}`;
if (timeLeft <= 0) {
clearInterval(timer);
endGame(false);
}
}, 1000);
}
function restartGame() {
gameOverContainer.classList.remove("show");
timeLeft = 60;
timerElement.textContent = "Time left: 1:00";
clearInterval(timer);
timerStarted = false;
cards.forEach((card, index) => {
card.querySelector(".card-front").textContent =
shuffledNumbers[index];
card.classList.remove("flipped");
});
clickedCards = [];
matchedCards = 0;
}
function endGame(isWin) {
console.log("endGame called with isWin:", isWin);
gameOverMessage.textContent = isWin
? "Congratulations! You won!"
: "You have failed!";
gameOverMessage.style.color = isWin ? "#FFD700" : "red";
gameOverContainer.classList.add("show");
}
const shuffledNumbers = [...numbers].sort(() => 0.5 - Math.random());
cards.forEach((card, index) => {
card.querySelector(".card-front").textContent = shuffledNumbers[index];
});
cards.forEach((card) => {
card.addEventListener("click", () => {
if (!timerStarted) {
startTimer();
timerStarted = true;
}
card.classList.add("flipped");
clickedCards.push(card);
if (clickedCards.length === 2) {
if (
clickedCards[0].querySelector(".card-front").textContent ===
clickedCards[1].querySelector(".card-front").textContent
) {
clickedCards = [];
matchedCards += 2;
if (matchedCards === cards.length) {
clearInterval(timer);
endGame(true);
}
} else {
setTimeout(() => {
clickedCards.forEach((card) => card.classList.remove("flipped"));
clickedCards = [];
}, 1000);
}
}
});
});
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.