Научим компьютер выбирать ход. Начнём с простого примера: кнопка выбирает камень, ножницы или бумагу. Для этого освоим массив, номер элемента и случайный целый индекс. В следующем уроке добавим ход игрока и определим победителя.

Открыть эту версию отдельно

Нажми кнопку несколько раз. Один и тот же ход может повториться — это нормально при случайном выборе. Здесь пока нет счёта и победы: результат этого урока — выбор из набора, который можно использовать в разных играх.

Новый проект после кликера

Здесь пригодятся переменные, функции с параметрами и return, условия, DOM и обработчики из предыдущего модуля. Сохрани законченный кликер по уроку Git. Его файлы не заменяем: создай новую папку rps и перенеси в неё файлы из start/ этого комплекта.

Исходники урока

Распакуй ZIP целиком. У каждого этапа своя папка: index.html, style.css и game.js лежат рядом. Открывай HTML двойным щелчком; после правки сохраняй файл и обновляй браузер. Установка, аккаунт и сервер для этого примера не нужны. Инструкция в просмотрщике объясняет версии; solutions/ оставь до самостоятельной попытки.

Скачать ZIP

Открой index.html. Это короткая HTML-оболочка с надписью computer-move и кнопкой choose-button. Скрипт подключён знакомой строкой <script src="game.js" defer></script>. В папке start файл game.js пока пустой, поэтому кнопка пока ничего не делает. Оформление уже готово; в этом уроке займёмся массивами и случайным выбором.

Три значения под одним именем

В game.js замени стартовый комментарий:

const moves = ['камень', 'ножницы', 'бумага'];
console.log(moves[0]);
console.log(moves[2]);
console.log(moves.length);

Массив хранит упорядоченный набор элементов. Квадратные скобки при создании содержат значения, разделённые запятыми. У нас это три строки. Имя moves обозначает весь набор.

Номер позиции называется индексом. В JavaScript он начинается с нуля: moves[0] — камень, moves[1] — ножницы, moves[2] — бумага. Скобки после имени читают один элемент, а не создают новый массив. moves.length сообщает количество элементов. Здесь длина 3, но последний допустимый индекс 2, то есть length - 1.

ИндексВыражениеЗначение
0moves[0]камень
1moves[1]ножницы
2moves[2]бумага

Проверка: открой Console и обнови страницу: увидишь камень, бумага, 3. Предскажи moves[moves.length - 1], затем проверь. Если попробовать прочитать moves[3], получишь undefined: в этой позиции нет элемента. Это не четвёртый игровой ход. При случайном выборе нам важно не получить такой индекс.

Меняем один элемент массива

Ниже добавь короткий опыт:

moves[1] = 'лист';
console.log(moves[1]);
moves[1] = 'ножницы';
console.log(moves[1]);

Знакомое присваивание теперь меняет выбранный элемент. Имя moves продолжает обозначать тот же массив, поэтому const не мешает этой операции: оно запрещает переназначить само имя, а не редактировать элементы. Запись moves = ['другой набор'] для такого const была бы ошибкой. Временное слово «лист» нужно лишь для опыта; мы сразу возвращаем «ножницы», чтобы дальнейшие правила совпадали с ходами.

Это версия step-01/. В ней Console показывает камень, бумага, 3, лист, ножницы. Попробуй предсказать, изменится ли length после замены одного существующего элемента, затем проверь: длина остаётся 3.

Выбираем случайный индекс

Браузер предоставляет функцию Math.random(). Она возвращает псевдослучайное число от 0 включительно до 1, не включая 1. Повтори вызов в Console несколько раз: значения будут дробными. Для наших ходов нужен один из целых индексов 0,1,2.

Умножение на длину массива растягивает диапазон: Math.random() * moves.length для трёх ходов даёт значение от 0 до 3, не включая 3. Функция Math.floor округляет вниз до целого. Для неотрицательных значений в этом опыте 0.8 станет 0, 1.9 станет 1, 2.7 станет 2. Название Math — встроенный набор математических средств; устанавливать библиотеку не нужно.

console.log(Math.floor(0 * 3));      // 0
console.log(Math.floor(0.5 * 3));    // 1
console.log(Math.floor(0.999 * 3));  // 2

Так мы проверим формулу с заранее выбранными числами, не дожидаясь случайного результата. Не используй округление до ближайшего целого: крайние индексы тогда получают другие шансы, а верхняя граница может выйти за массив. И не прибавляй 1: индексы начинаются с нуля.

Отделяем выбор от его отображения

Оставь в game.js объявление исходного массива и замени опыт с элементами следующим кодом:

function chooseComputerMove() {
  const randomIndex = Math.floor(Math.random() * moves.length);
  return moves[randomIndex];
}

console.log(chooseComputerMove());
console.log(chooseComputerMove());
console.log(chooseComputerMove());

Вызов chooseComputerMove вычисляет новый индекс, читает элемент и возвращает строку. Локальный randomIndex существует внутри функции. Сам массив функция не изменяет. Берём длину из массива через length, чтобы формула работала и с другим количеством ходов. В массиве обязательно должен быть хотя бы один элемент: из пустого списка выбрать нечего.

Проверка step-02: каждый из трёх выводов должен быть одним из известных ходов. Они могут совпасть. Три одинаковых результата не доказывают ошибку; проверь формулу и допустимые индексы. undefined указывает на другую проблему: неверный индекс или элемент, которого нет.

Подключаем выбор к кнопке

Удали три диагностических console.log из game.js. Сохрани массив и chooseComputerMove. Сразу после массива, до функций, добавь состояние и ссылки на элементы:

let computerMove = 'ещё не выбран';
const computerMoveElement = document.getElementById('computer-move');
const chooseButton = document.getElementById('choose-button');

После функции выбора добавь знакомую связку «действие → состояние → вывод»:

function render() {
  computerMoveElement.textContent = computerMove;
}

function handleComputerChoice() {
  computerMove = chooseComputerMove();
  render();
}

chooseButton.addEventListener('click', handleComputerChoice);
render();

Это final/. Каждый клик вызывает выбор ровно один раз, сохраняет его в computerMove и отображает. Если вызывать chooseComputerMove отдельно для надписи и отдельно для будущего правила, можно получить два разных хода. Поэтому храним результат одного вызова. Передача handleComputerChoice в событие по-прежнему без круглых скобок.

Проверка: сначала надпись «ещё не выбран», после нажатия — допустимый ход. Проверь кнопку через Tab и Enter. Перезагрузка возвращает начальную надпись. Десять нажатий не обязаны дать все три варианта, но не должны давать пустой или неизвестный ход.

Самостоятельно: случайная реплика персонажа

Скопируй final в отдельную папку. Вместо хода компьютера сделай кнопку реплики персонажа. Создай другой массив replies из четырёх коротких фраз. Напиши функцию chooseReply, возвращающую случайную фразу, и подключи её к действию. Формула должна использовать длину нового массива, а не число 3 из прежней игры.

До случайных проверок объясни допустимые индексы и прочитай четвёртую фразу напрямую. Затем выбери мысленно дробь 0.999: какой индекс получится при длине 4? Добавь пятую фразу и убедись, что алгоритм менять не приходится. Теперь та же функция выбирает из пяти фраз без изменения формулы.

Подсказка 1: используй новый массив

Ищи все места, где читается moves. Для другого набора нужны и его length, и чтение элемента по индексу. Если изменить только надпись кнопки, программа останется прежней.

Подсказка 2: проверь последнюю позицию

Для четырёх элементов последний индекс 3. Выражение Math.floor(0.999 * replies.length) должно дать 3, а replies[3] — четвёртую фразу.

Разбор после собственной попытки

Рабочий пример с четырьмя репликами и объяснение в папке solutions скачанного архива. Если пришлось смотреть код, закрой его и восстанови выбор из другого массива без готовой функции.

Проверь, что ты освоил

Ты можешь назвать длину и последний индекс массива, изменить существующий элемент, объяснить floor и получить только допустимое значение. Для самопроверки покажи, что твоя функция выбирает реплику и из четырёх, и из пяти фраз.

Сохрани свой вариант отдельно. Для продолжения возьми базовый final с тремя ходами: его файлы совпадают со start следующего урока.

Справочники по использованным средствам: Math.random, Math.floor, length массива. Читать их целиком до продолжения не требуется.