Игра «Память» отлично тренирует работу с состоянием интерфейса: карточки открываются, сравниваются попарно, блокируются на время проверки и остаются открытыми при совпадении. В этом уроке разбираем финальную версию на HTML, CSS и JavaScript — ту же, что доступна в разделе «Поиграть».
Готовая браузерная игра «Память»: поле 4×4, случайная раскладка, проверка пар, счётчик ходов, таймер партии, подсчёт найденных пар и кнопка перезапуска.
Сначала попробуйте готовую игру
Сыграйте одну партию до конца и обратите внимание на поведение интерфейса: карточки не дают открыть третью карту во время проверки пары, а статистика обновляется в реальном времени.
Структура проекта
Игра собрана в трёх файлах и не требует библиотек.
pamyat/
├── source.html
├── style.css
└── game.js
HTML: собираем каркас игры
Разметка содержит заголовок, блок статистики, кнопку новой партии, контейнер карточек и строку статуса. JavaScript работает через data-memory-* атрибуты, поэтому структура остаётся прозрачной.
<main class="memory" data-memory-game>
<section class="memory__stats" aria-label="Статистика игры">
<p data-memory-moves>Ходы: 0</p>
<p data-memory-time>Время: 00:00</p>
<p data-memory-pairs>Пары: 0 / 8</p>
</section>
<button class="memory__restart" type="button" data-memory-restart>Новая партия</button>
<div class="memory__board" data-memory-board></div>
<p class="memory__status" data-memory-status aria-live="polite">Найдите все пары карточек.</p>
</main>
CSS: сетка и единый стиль
Поле использует CSS Grid 4×4, карточки имеют состояния is-open и is-matched, а общая палитра и кнопки выдержаны в том же визуальном стиле, что и другие игры проекта.
.memory__board {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
}
.memory__card.is-open {
background: linear-gradient(135deg, #355cae, #2d4f98);
}
.memory__card.is-matched {
background: linear-gradient(135deg, #2a8f86, #216f72);
}
JavaScript: подготовка колоды
Берём восемь символов, дублируем, перемешиваем алгоритмом Фишера-Йетса и превращаем в массив карточек с флагами open и matched.
const icons = ["⚡", "🎯", "🎮", "🧩", "🚀", "💎", "🔥", "🌙"];
function shuffle(values) {
const output = values.slice();
for (let i = output.length - 1; i > 0; i -= 1) {
const j = Math.floor(Math.random() * (i + 1));
[output[i], output[j]] = [output[j], output[i]];
}
return output;
}
JavaScript: логика открытия карточек
Игрок может открыть максимум две карточки за ход. На время сравнения поле блокируется, чтобы нельзя было открыть третью карту и сломать состояние партии.
if (!card || locked || card.open || card.matched) return;
card.open = true;
openIds.push(id);
renderBoard();
if (openIds.length < 2) return;
moves += 1;
locked = true;
Проверка пары, таймер и завершение партии
Если символы совпали, карточки помечаются как найденные и остаются открытыми. Если нет — закрываются с небольшой задержкой. Таймер запускается при первом ходе и останавливается, когда собраны все пары.
if (first.icon === second.icon) {
first.matched = true;
second.matched = true;
matchedPairs += 1;
} else {
window.setTimeout(function () {
first.open = false;
second.open = false;
}, 540);
}
if (matchedPairs === totalPairs) {
stopTimer();
}
Перезапуск игры
Кнопка «Новая партия» полностью сбрасывает состояние: таймер, ходы, пары, открытые карточки и раскладку.
function startGame() {
stopTimer();
buildDeck();
openIds = [];
locked = false;
matchedPairs = 0;
moves = 0;
elapsedSeconds = 0;
renderBoard();
}
restartButton.addEventListener("click", startGame);
Как проверить сборку
| Проверка | Ожидаемый результат |
|---|---|
| Открыть две разные карточки | Обе закрываются обратно после короткой паузы |
| Открыть совпадающую пару | Карточки остаются открытыми и помечаются как найденные |
| Попробовать кликнуть третью карточку во время паузы | Клик игнорируется, пока идёт проверка пары |
| Сделать первый ход | Запускается таймер, статистика начинает обновляться |
| Нажать «Новая партия» | Состояние полностью сбрасывается и колода перемешивается заново |
FAQ
Можно ли изменить размер поля, например на 6×6?
Да. Увеличьте набор символов, количество карточек и настройте grid-template-columns под новую ширину поля.
Как сделать счёт более «игровым»?
Можно начислять бонус за серию совпадений без ошибки и штрафовать за промахи. Текущая архитектура это позволяет без больших переделок.
Где лучше хранить рекорды по времени?
Для локальной версии подойдёт localStorage. Если нужен общий рейтинг между игроками, понадобится серверная часть и API.
Почему карточки рендерятся заново после хода?
Так проще держать интерфейс синхронным с состоянием. Для маленькой сетки 4×4 это быстрый и надёжный подход.
Откройте финальные файлы этой версии: HTML, CSS и JavaScript. Это тот же код, который работает в игре наверху статьи и на отдельной странице «Поиграть».
Книга по JavaScript для начинающих
«JavaScript с нуля: учимся программировать, создавая игры»Пошаговый формат с маленькими практическими задачами: от базового синтаксиса до первых игровых механик без перегруза теорией.
Читать книгу на ЛитРес →