🎯 Цели урока
- Создать логику проверки совпадений карточек
- Реализовать подсчет очков и попыток
- Добавить условие завершения игры
- Создать обратную связь для игрока
- Завершить полнофункциональную игру
🔨 Что мы создаем
Заключительную функцию checkForMatch,
которая определяет совпадают ли карточки, обновляет счет и
проверяет условие победы в игре.
1. Логика проверки совпадений
🔄 Алгоритм проверки:
- Получить две перевернутые карточки
- Сравнить пути к их изображениям
- Если совпадают: отметить как найденную пару
- Если не совпадают: перевернуть обратно через секунду
- Очистить массив перевернутых карточек
- Разрешить новые клики
Шаг 1
Основная функция checkForMatch
// Проверка совпадения карточек
function checkForMatch() {
const [card1, card2] = flippedCards;
if (card1.imgSrc === card2.imgSrc) {
// НАШЛИ ПАРУ!
handleMatch(card1, card2);
} else {
// Не совпали - переворачиваем обратно
handleMismatch(card1, card2);
}
}
Функция использует деструктуризацию массива для получения двух карточек
и разделение ответственности - отдельные функции для каждого случая.
[card1, card2] = flippedCards - извлекает первые два элемента массива
card1.imgSrc === card2.imgSrc - сравнивает пути к изображениям
Шаг 2
Обработка совпадения
// Обработка найденной пары
function handleMatch(card1, card2) {
// Отмечаем карточки как найденные
card1.isMatched = true;
card2.isMatched = true;
// Убираем класс flipped и добавляем matched
card1.element.classList.remove('flipped');
card2.element.classList.remove('flipped');
card1.element.classList.add('matched');
card2.element.classList.add('matched');
// Увеличиваем счетчик найденных пар
matchedPairs++;
scoreElement.textContent = matchedPairs;
// Очищаем массив перевернутых карточек
flippedCards = [];
canFlip = true;
// Проверяем, закончилась ли игра
checkGameEnd();
}
При совпадении карточки получают специальное визуальное оформление
(зеленый фон и рамку через CSS класс matched).
Шаг 3
Обработка несовпадения
// Обработка несовпадения
function handleMismatch(card1, card2) {
// Переворачиваем карточки обратно через секунду
setTimeout(() => {
card1.element.classList.remove('flipped');
card2.element.classList.remove('flipped');
card1.isFlipped = false;
card2.isFlipped = false;
flippedCards = [];
canFlip = true;
}, 1000);
}
Задержка в 1 секунду дает игроку время запомнить расположение карточек.
Это важная часть игрового опыта в Memory играх.
2. Проверка окончания игры
Шаг 4
Функция checkGameEnd
// Проверка окончания игры
function checkGameEnd() {
if (matchedPairs === cardImages.length) {
// Игра закончена! Все пары найдены
setTimeout(() => {
showGameEndMessage();
}, 500);
}
}
Игра заканчивается, когда количество найденных пар равно
количеству уникальных изображений в массиве cardImages.
Шаг 5
Сообщение о победе
// Показать сообщение о завершении игры
function showGameEndMessage() {
const message = `🎉 Поздравляем!
Вы нашли все пары!
Попыток: ${attempts}
Ваш результат: ${getScoreRating(attempts)}`;
alert(message);
}
// Оценка результата игрока
function getScoreRating(attempts) {
if (attempts <= 12) return "Отлично! 🌟";
if (attempts <= 16) return "Хорошо! 👍";
if (attempts <= 24) return "Неплохо! 🙂";
return "Можно лучше! 💪";
}
Функция дает оценку игроку на основе количества попыток.
Минимальное количество попыток для победы - 8 (если игрок угадывает все пары с первого раза).
💡 Система оценки:
- 8-12 попыток: Отличный результат
- 13-16 попыток: Хороший результат
- 17-24 попытки: Средний результат
- 25+ попыток: Есть куда расти
3. Полная функция checkForMatch
Шаг 6
Объединенная версия (как в рабочем коде)
// Проверка совпадения карточек - полная версия
function checkForMatch() {
const [card1, card2] = flippedCards;
if (card1.imgSrc === card2.imgSrc) {
// НАШЛИ ПАРУ - как в первом варианте!
card1.isMatched = true;
card2.isMatched = true;
// Убираем класс flipped и добавляем matched
// matched в CSS показывает картинку всегда
card1.element.classList.remove('flipped');
card2.element.classList.remove('flipped');
card1.element.classList.add('matched');
card2.element.classList.add('matched');
matchedPairs++;
scoreElement.textContent = matchedPairs;
// Очищаем массив
flippedCards = [];
canFlip = true;
// Проверяем, закончилась ли игра
if (matchedPairs === cardImages.length) {
setTimeout(() => {
alert(`🎉 Поздравляем! Вы нашли все пары!\nПопыток: ${attempts}`);
}, 500);
}
} else {
// Не совпали - переворачиваем обратно через секунду
setTimeout(() => {
card1.element.classList.remove('flipped');
card2.element.classList.remove('flipped');
card1.isFlipped = false;
card2.isFlipped = false;
flippedCards = [];
canFlip = true;
}, 1000);
}
}
Эта компактная версия объединяет всю логику в одной функции
и соответствует коду в рабочей игре.
4. Полный код игры
🎮 Итоговый script.js
// Элементы на странице
const gameBoard = document.getElementById('game-board');
const scoreElement = document.getElementById('score');
const attemptsElement = document.getElementById('attempts');
const totalPairsElement = document.getElementById('total-pairs');
const restartButton = document.getElementById('restart-btn');
// Картинки для игры (из папки img)
const cardImages = [
'img/dog.png',
'img/kat.png',
'img/rabbit.png',
'img/fox.png',
'img/bear.png',
'img/panda.png',
'img/koala.png',
'img/lion.png'
];
let cards = [];
let flippedCards = [];
let matchedPairs = 0;
let attempts = 0;
let canFlip = true;
// Инициализация игры
function initGame() {
gameBoard.innerHTML = '';
cards = [];
flippedCards = [];
matchedPairs = 0;
attempts = 0;
canFlip = true;
scoreElement.textContent = matchedPairs;
attemptsElement.textContent = attempts;
const gameCards = [...cardImages, ...cardImages];
totalPairsElement.textContent = cardImages.length;
shuffleArray(gameCards);
for (let i = 0; i < gameCards.length; i++) {
const card = document.createElement('div');
card.classList.add('card');
card.dataset.index = i;
const front = document.createElement('div');
front.classList.add('card-front');
const img = document.createElement('img');
img.src = gameCards[i];
img.alt = `Картинка`;
front.appendChild(img);
const back = document.createElement('div');
back.classList.add('card-back');
back.textContent = '?';
card.appendChild(front);
card.appendChild(back);
card.addEventListener('click', flipCard);
gameBoard.appendChild(card);
cards.push({
element: card,
imgSrc: gameCards[i],
isFlipped: false,
isMatched: false
});
}
}
// Функция перемешивания массива
function shuffleArray(array) {
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 flipCard() {
const clickedCard = this;
const cardIndex = clickedCard.dataset.index;
const cardData = cards[cardIndex];
if (!canFlip || cardData.isFlipped || cardData.isMatched) {
return;
}
clickedCard.classList.add('flipped');
cardData.isFlipped = true;
flippedCards.push(cardData);
if (flippedCards.length === 2) {
canFlip = false;
attempts++;
attemptsElement.textContent = attempts;
setTimeout(checkForMatch, 500);
}
}
// Проверка совпадения карточек
function checkForMatch() {
const [card1, card2] = flippedCards;
if (card1.imgSrc === card2.imgSrc) {
card1.isMatched = true;
card2.isMatched = true;
card1.element.classList.remove('flipped');
card2.element.classList.remove('flipped');
card1.element.classList.add('matched');
card2.element.classList.add('matched');
matchedPairs++;
scoreElement.textContent = matchedPairs;
flippedCards = [];
canFlip = true;
if (matchedPairs === cardImages.length) {
setTimeout(() => {
alert(`🎉 Поздравляем! Вы нашли все пары!\nПопыток: ${attempts}`);
}, 500);
}
} else {
setTimeout(() => {
card1.element.classList.remove('flipped');
card2.element.classList.remove('flipped');
card1.isFlipped = false;
card2.isFlipped = false;
flippedCards = [];
canFlip = true;
}, 1000);
}
}
// Запуск игры при загрузке страницы
document.addEventListener('DOMContentLoaded', initGame);
// Обработчик кнопки перезапуска
restartButton.addEventListener('click', initGame);
5. Ключевые принципы игровой логики
Конечные автоматы (State Machines)
Игра имеет четкие состояния:
- Ожидание клика - игрок может переворачивать карточки
- Проверка совпадения - блокировка новых кликов
- Показ несовпадения - карточки переворачиваются обратно
- Завершение игры - все пары найдены
Управление состоянием
Переменная canFlip предотвращает
нарушение логики игры при быстрых кликах.
Обратная связь с игроком
Визуальная (изменение цвета карточек) и текстовая (счетчики)
обратная связь помогает игроку понимать текущее состояние.
Асинхронность
setTimeout создает паузы для
лучшего пользовательского опыта без блокировки интерфейса.
6. Поздравления! 🎉
✅ Вы создали полнофункциональную игру Memory!
Теперь ваша игра умеет:
- ✅ Создавать и перемешивать карточки
- ✅ Реагировать на клики игрока
- ✅ Проверять совпадения
- ✅ Подсчитывать очки и попытки
- ✅ Определять конец игры
- ✅ Давать обратную связь игроку
Попробуйте поиграть и насладитесь результатом своей работы!