Разработчикам скучно штудировать чистую теорию: хочется законченный мини‑проект, который можно показать коллеге и почувствовать, что JavaScript уже слушается. Игра «Камень‑ножницы‑бумага» на JavaScript делает именно это: за один вечер вы увидите, как логика отделяется от UI, потренируете DOM‑события и испытаете приятный всплеск дофамина, когда победите браузер.

  • Скелет из трёх файлов: index.html, style.css, game.js — чистая модульность.
  • Лаконичная функция compare(choiceA, choiceB) — сердце логики (см. раздел «Жизненный цикл раунда»).
  • Событие ‘click’ на кнопке → обновление счёта без перезагрузки (→ к разделу «Связываем UI и JavaScript»).
  • Читаемый код ≤ 80 строк подходит для ревью на любом мит‑апе.

Как организовать структуру проекта?

Если начать с папки, где всё лежит в одном файле, быстро возникнет хаос. Отделите представление (HTML/CSS) от поведения (JS) сразу — так читатель кода мгновенно поймёт, где править стили, а где править игру. Такой подход облегчает дебаг и подготовит почву для будущего тестирования. Почему бы не сделать всё в React? Потому что цель — потренироваться на базовом DOM без абстракций, чтобы любая библиотека потом казалась родной.

  1. Создайте папку rps.
  2. Сгенерируйте index.html с базовой разметкой.
  3. Добавьте style.css и подключите в <head>.
  4. Создайте game.js и подключите перед </body>.
  5. Откройте индекс в браузере и проверьте консоль.
html
<!-- 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 ЛОКАЛЬНО НА СВОЕМ КОМПЬЮТЕРЕ
ЧТО БУДЕТ НА ОБУЧЕНИИ?
  • ПОКАЖЕМ, КАК РАЗВЕРНУТЬ МОДЕЛЬ DEEPSEEK R1 ПРЯМО НА СВОЁМ КОМПЬЮТЕРЕ
  • Где и как применять? Потестируем модель после установки на разных задачах
  • Как дообучить модель под себя?

Жизненный цикл раунда: алгоритм победы

Сама игра сводится к единственной функции сравнения, но вокруг неё вращается весь цикл: выбор игрока, выбор компьютера, определение результата и обновление счёта. Компактность кода не мешает ясности, если правильно назвать функции и разнести ответственность.

  1. Создайте массив CHOICES = [‘rock’,’paper’,’scissors’].
  2. Функция getComputerChoice() возвращает случайный элемент.
  3. compare(a, b) возвращает ‘win’, ‘lose’ или ‘draw’.
  4. playRound(playerChoice) связывает всё вместе.
  5. Послерезультатаобновитесчёти UI.
javascript
// 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 (получить значение, вывести результат) от самой логики; иначе в будущем трудно будет писать тесты. Удивительно, но всего несколько строк создают ощущение «настоящей» игры.

  1. document.querySelectorAll(‘button’) → NodeList.
  2. Для каждого элемента addEventListener(‘click’, handler).
  3. В handler: const choice = e.target.dataset.choice.
  4. const result = playRound(choice).
  5. Обновите #score.textContent.
javascript
// 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}`;
  });
});

Тонкая прослойка из слушателей связывает чистую логику с интерфейсоми код остаётся прозрачным.

Попробуйте сами: добавьте простую анимацию кнопок и поделитесь репозиторием сообществу.

Счёт, анимация и рефакторинг: доводим до ума

Последний штрих — улучшить пользовательский опыт. Мелкие детали, такие как плавная анимация при нажатии и отображение победителя матча до трёх очков, превращают учебный прототип в мини‑игру, которой не стыдно поделиться. Рефакторинг вынесёт повторяющийся код в вспомогательные функции; тесты появятся почти автоматически.

  1. Добавьте классы .win и .lose в style.css с transition.
  2. В playRound возвращайте объект {result, botChoice}.
  3. Играйте до 3 побед и выводите alert с чемпионом.
  4. Вынесите генератор случайного числа в util.js.
  5. Покройте compare() юнит‑тестами в Jest.
css
// style.css (фрагмент)
button.win { transform: scale(1.1); }
button.lose { opacity: 0.6; }
javascript
// 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
ПОКАЖЕМ НА КОНКРЕТНЫХ КЕЙСАХ
  • Освой Perplexity и узнай, как пользоваться функционалом остальных ИИ в одном
  • УЧАСТВОВАТЬ ЗА 0 РУБ.
  • Расскажем, как получить подписку (240$) бесплатно
Участвовать бесплатно
ОНЛАЙН-ПРАКТИКУМ
ЗАПУСК DEEPSEEK R1 ЛОКАЛЬНО НА СВОЕМ КОМПЬЮТЕРЕ
ЧТО БУДЕТ НА ОБУЧЕНИИ?
  • ПОКАЖЕМ, КАК РАЗВЕРНУТЬ МОДЕЛЬ DEEPSEEK R1 ПРЯМО НА СВОЁМ КОМПЬЮТЕРЕ
Участвовать бесплатно