← Вернуться в меню

🧠 Урок 5: Игровая логика

Создаем проверку совпадений, подсчет очков и завершение игры

🎯 Цели урока

  • Создать логику проверки совпадений карточек
  • Реализовать подсчет очков и попыток
  • Добавить условие завершения игры
  • Создать обратную связь для игрока
  • Завершить полнофункциональную игру

🔨 Что мы создаем

Заключительную функцию checkForMatch, которая определяет совпадают ли карточки, обновляет счет и проверяет условие победы в игре.

1. Логика проверки совпадений

🔄 Алгоритм проверки:

  1. Получить две перевернутые карточки
  2. Сравнить пути к их изображениям
  3. Если совпадают: отметить как найденную пару
  4. Если не совпадают: перевернуть обратно через секунду
  5. Очистить массив перевернутых карточек
  6. Разрешить новые клики
Шаг 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!

Теперь ваша игра умеет:

  • ✅ Создавать и перемешивать карточки
  • ✅ Реагировать на клики игрока
  • ✅ Проверять совпадения
  • ✅ Подсчитывать очки и попытки
  • ✅ Определять конец игры
  • ✅ Давать обратную связь игроку

Попробуйте поиграть и насладитесь результатом своей работы!