Разработчикам скучно штудировать чистую теорию: хочется законченный мини‑проект, который можно показать коллеге и почувствовать, что JavaScript уже слушается. Игра «Камень‑ножницы‑бумага» на JavaScript делает именно это: за один вечер вы увидите, как логика отделяется от UI, потренируете DOM‑события и испытаете приятный всплеск дофамина, когда победите браузер.
- Скелет из трёх файлов: index.html, style.css, game.js — чистая модульность.
- Лаконичная функция compare(choiceA, choiceB) — сердце логики (см. раздел «Жизненный цикл раунда»).
- Событие ‘click’ на кнопке → обновление счёта без перезагрузки (→ к разделу «Связываем UI и JavaScript»).
- Читаемый код ≤ 80 строк подходит для ревью на любом мит‑апе.
Как организовать структуру проекта?
Если начать с папки, где всё лежит в одном файле, быстро возникнет хаос. Отделите представление (HTML/CSS) от поведения (JS) сразу — так читатель кода мгновенно поймёт, где править стили, а где править игру. Такой подход облегчает дебаг и подготовит почву для будущего тестирования. Почему бы не сделать всё в React? Потому что цель — потренироваться на базовом DOM без абстракций, чтобы любая библиотека потом казалась родной.
- Создайте папку rps.
- Сгенерируйте index.html с базовой разметкой.
- Добавьте style.css и подключите в <head>.
- Создайте game.js и подключите перед </body>.
- Откройте индекс в браузере и проверьте консоль.
<!-- index.html -->
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>RPS Game</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Камень–ножницы–бумага</h1>
<div id="score"></div>
<button data-choice="rock">Камень</button>
<button data-choice="paper">Бумага</button>
<button data-choice="scissors">Ножницы</button>
<script src="game.js"></script>
</body>
</html>
Вы уже отделили логику от представления и создали основу для чистого кода.

- ПОКАЖЕМ, КАК РАЗВЕРНУТЬ МОДЕЛЬ DEEPSEEK R1 ПРЯМО НА СВОЁМ КОМПЬЮТЕРЕ
- Где и как применять? Потестируем модель после установки на разных задачах
- Как дообучить модель под себя?
Жизненный цикл раунда: алгоритм победы
Сама игра сводится к единственной функции сравнения, но вокруг неё вращается весь цикл: выбор игрока, выбор компьютера, определение результата и обновление счёта. Компактность кода не мешает ясности, если правильно назвать функции и разнести ответственность.
- Создайте массив CHOICES = [‘rock’,’paper’,’scissors’].
- Функция getComputerChoice() возвращает случайный элемент.
- compare(a, b) возвращает ‘win’, ‘lose’ или ‘draw’.
- playRound(playerChoice) связывает всё вместе.
- Послерезультатаобновитесчёти UI.
// game.js (фрагмент)
const CHOICES = ['rock', 'paper', 'scissors'];
function getComputerChoice() {
const idx = Math.floor(Math.random() * CHOICES.length);
return CHOICES[idx];
}
function compare(a, b) {
if (a === b) return 'draw';
const wins = { rock: 'scissors', paper: 'rock', scissors: 'paper' };
return wins[a] === b ? 'win' : 'lose';
}
function playRound(playerChoice) {
const bot = getComputerChoice();
return compare(playerChoice, bot);
}
Чёткая логика в одной функции избавляет от багов и ускоряет ревью.
Связываем UI и JavaScript: события DOM
Теперь пора оживить кнопки. Слушайте события ‘click’, извлекайте выбор игрока из атрибута data-choice и передавайте в playRound. Здесь важно отделить работу с DOM (получить значение, вывести результат) от самой логики; иначе в будущем трудно будет писать тесты. Удивительно, но всего несколько строк создают ощущение «настоящей» игры.
- document.querySelectorAll(‘button’) → NodeList.
- Для каждого элемента addEventListener(‘click’, handler).
- В handler: const choice = e.target.dataset.choice.
- const result = playRound(choice).
- Обновите #score.textContent.
// game.js (UI binding)
const scoreEl = document.getElementById('score');
let player = 0, bot = 0;
document.querySelectorAll('button').forEach(btn => {
btn.addEventListener('click', e => {
const userChoice = e.target.dataset.choice;
const outcome = playRound(userChoice);
if (outcome === 'win') player++;
if (outcome === 'lose') bot++;
scoreEl.textContent = `${player} : ${bot}`;
});
});
Тонкая прослойка из слушателей связывает чистую логику с интерфейсом — и код остаётся прозрачным.
Попробуйте сами: добавьте простую анимацию кнопок и поделитесь репозиторием сообществу.
Счёт, анимация и рефакторинг: доводим до ума
Последний штрих — улучшить пользовательский опыт. Мелкие детали, такие как плавная анимация при нажатии и отображение победителя матча до трёх очков, превращают учебный прототип в мини‑игру, которой не стыдно поделиться. Рефакторинг вынесёт повторяющийся код в вспомогательные функции; тесты появятся почти автоматически.
- Добавьте классы .win и .lose в style.css с transition.
- В playRound возвращайте объект {result, botChoice}.
- Играйте до 3 побед и выводите alert с чемпионом.
- Вынесите генератор случайного числа в util.js.
- Покройте compare() юнит‑тестами в Jest.
// style.css (фрагмент)
button.win { transform: scale(1.1); }
button.lose { opacity: 0.6; }
// game.js (анимация)
const flash = (btn, cls) => {
btn.classList.add(cls);
setTimeout(() => btn.classList.remove(cls), 300);
};
Крошечные улучшения UI и рефакторинг делают игру гибкой для любых экспериментов.
Чек‑лист: от идеи до готовой игры
| Шаг | Действие |
| 1 | Создать HTML/CSS/JS файлы |
| 2 | Реализовать логику compare() |
| 3 | Подключить обработчики ‘click’ |
| 4 | Обновлять счёт в DOM |
| 6 | Провести рефакторинг и тесты |
- Освой Perplexity и узнай, как пользоваться функционалом остальных ИИ в одном
- УЧАСТВОВАТЬ ЗА 0 РУБ.
- Расскажем, как получить подписку (240$) бесплатно
- ПОКАЖЕМ, КАК РАЗВЕРНУТЬ МОДЕЛЬ DEEPSEEK R1 ПРЯМО НА СВОЁМ КОМПЬЮТЕРЕ