Научим компьютер выбирать ход. Начнём с простого примера: кнопка выбирает камень, ножницы или бумагу. Для этого освоим массив, номер элемента и случайный целый индекс. В следующем уроке добавим ход игрока и определим победителя.
Нажми кнопку несколько раз. Один и тот же ход может повториться — это нормально при случайном выборе. Здесь пока нет счёта и победы: результат этого урока — выбор из набора, который можно использовать в разных играх.
Новый проект после кликера
Здесь пригодятся переменные, функции с параметрами и return, условия, DOM и обработчики из предыдущего модуля. Сохрани законченный кликер по уроку Git. Его файлы не заменяем: создай новую папку rps и перенеси в неё файлы из start/ этого комплекта.
Распакуй ZIP целиком. У каждого этапа своя папка: index.html, style.css и game.js лежат рядом. Открывай HTML двойным щелчком; после правки сохраняй файл и обновляй браузер. Установка, аккаунт и сервер для этого примера не нужны. Инструкция в просмотрщике объясняет версии; solutions/ оставь до самостоятельной попытки.
Открой 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.
| Индекс | Выражение | Значение |
|---|---|---|
| 0 | moves[0] | камень |
| 1 | moves[1] | ножницы |
| 2 | moves[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 массива. Читать их целиком до продолжения не требуется.