Управляемый random в JavaScript

в 18:56, , рубрики: javascript, rpg, метки: , ,

«Алгоритм» для случайной выборки значений из array без их повторения. Конкретнее, в рамках обучения JS, я использовал его для генерации классической RPG-группы персонажей (варвар, маг, вор, рыцарь, священник), без повторения классов и имен.

image

Принцип предельно простой, но он может быть полезен таким же новичкам в JS как и я. Привязка к RPG исключительно символическая — сейчас я активно пытаюсь сменить профиль с маркетинга на IT (понял, что душа лежит), а практиковаться в игровой форме гораздо интереснее.

1. Создаем шаблон

Прежде чем генерировать группу персонажей, нужно задать шаблон для их генерации. Собственно, вот:

function GamePlayer(n, r, l, p) {
    this.nick = n;
    this.role = r;
    this.level = l;
    this.portrait = p;
}

Фактически, эта функция будет создавать персонажей из переменных, через которые она будет вызвана. Например:

var player1 = new GamePlayer("Power Ranger","barbarian","64","img/barbarian.jpg")

Теперь в переменной player1 хранится варвар Power Ranger 64 уровня с определенным портретом; мы можем отображать любые его параметры в теле страницы используя player1.nick, player1.level и т.п.

Значения (n, r, l, p) из GamePlayer отвечают за прием и порядок приема данных в функцию. Если в примере поменять n и r местами, то в player1 сохранится могучий рейнджер Barbarian, что не совсем соответствует задаче.
 

2. Задаем массивы

Чтобы не создавать персонажей самостоятельно, а почти случайно генерировать их (как и обещалось в заголовке), необходимы массивы из которых мы будем брать параметры этих самых персонажей. Как уже описывалось выше, параметров у нас всего 4: имя персонажа, класс, уровень и портрет.
 

Массив для имени:

var playerNames = ['Rabbit Helpless', 'Warm Dreaded Foal', 'Desire Kit', 'Angel Dusty', 'Sweety Frozen', 'Silver Heavy Wombat', 'Lost Puma', 'Vital Panda', 'Rolling Sun', 'Steel Runny', 'Young Fox', 'Needless Ruthless Volunteer', 'Chipmunk Cult', 'Indigo Puppy'];

Можно было бы пойти дальше, и генерировать имена из 2-3 составляющих, но алгоритм такого улучшения не содержит ничего нового (тот же рандом), а потом просто усложнил бы учебный процесс.
 

Массив для класса:

var playerRoles = ['barbarian', 'mage', 'rogue', 'knight', 'priest'];

Все так же очевидно. Несколько string, из которых мы потом будем выбирать значения для отображения на странице.
 

Массив для уровня:

В конкретном примере, я хотел, чтобы все члены группы были от 60 до 70 уровня. Но, так как условия могут поменяться, нужны было создать массив с 0 по 80 уровень, из которого потом выбирать нужные значения. Создавал циклом:

var playerLevels = [];

for (i = 0;i <= 80;i++) {
    console.log(i);

    playerLevels[i] = i;
}

В итоге получается массив playerLevels, каждая ячейка которого содержит int со своим же номером.
 

Массив для портретов:

var playerPortraits = ['img/barbarian.jpg', 'img/mage.jpg', 'img/rogue.jpg', img/'knight.jpg', 'img/priest.jpg'];

Тот же принцип, но вместо текста используем ссылки на картинки. Далее, мы сможем подставлять их в параметр background-image нужного div (или в параметр src нужной картинки, кому как удобнее).

Важно, чтобы порядок картинок в массиве playerPortraits был идентичен порядку классов в массиве playerRoles, тогда для их генерации (чтобы картинка совпадала с классом) мы сможем использовать одну и ту же random-переменную.
 

3. Генерируем персонажей

Как я уже говорил, в группе должно быть 5 персонажей. Потому, создаем цикл:

for (i = 0;i<=4;i++) { }

Перед циклом важно заявить массив для наших будущих персонажей:

var players = [ ];

Генерация имен

Далее, создаем рандомную переменную для случайной выборки имени:

var namerand = Math.floor(Math.random() * playerNames.length)

  • Marh.random() используется для генерации случайного числа;
  • Умножение на playerNames.length (длинна массива playerNames) — для ограничения рандома количеством имен в массиве;
  • Math.floor — для превращения получившегося числа в целое.

Нюанс в том, что Math.floor округляет в меньшую сторону, но так как нумерация в массивах идет с 0, то нас это устраивает.
 

Генерация классов

Принцип и реализация та же:

var rolerand = Math.floor(Math.random() * (playerRoles.length));

Единственное различие — для классов мы используем массив playerRoles.
 

Генерация уровней

var levelrand = Math.floor(Math.random() * (70 - 60 + 1) + 60);

Расчет random в определенном интервале происходит по формуле Math.random() * (max - min) + min.

В примере мы получаем рандом от 0 до 10, а затем добавляем к нему 60, получая интервал от 60+0 до 60+10 (нам это и нужно). Добавление единицы необходимо из-за использования Math.floor (читай выше).
 

Генерация персонажей

Предфинальный шаг. Для формирования персонажа, нам нужно объединить все его параметры в одну переменную, как в первом примере. Выглядит это так:

players[i] = new GamePlayer(playerNames[namerand], playerRoles[rolerand], playerLevels[levelrand], playerPortraits[rolerand]);

Фактически, каждый персонаж становится элементом массива players со своим порядковым номером. Его параметры:

  • playerNames[namerand] — имя, случайный выбор из имен (ячейка под номером namerand в playerNames);
  • playerRoles[rolerand] — класс, случайный выбор из классов;
  • playerLevels[levelrand] — класс, случайный выбор уровня в интервале 60-70;
  • playerPortraits[rolerand] — портрет, случайный выбор из портретов.

Как я замечал выше, массивы портретов и классов должны быть идентичны, чтобы «картинка совпадала»; потому мы можем использовать один и тот же random в обоих случаях.
 

Управляемый random

Финал. Если оставить все так, как есть, оно будет работать. Однако, мы получим группу персонажей самых разных классов (3 мага и 2 вора, например) с одинаковыми именами. Для того, чтобы избежать этого, достаточно пары простых действий:

players[i] = new GamePlayer(playerNames[namerand], playerRoles[rolerand], playerLevels[levelrand], playerPortraits[rolerand]);

playerNames.splice(namerand,1);
playerRoles.splice(rolerand,1);
playerPortraits.splice(rolerand,1);

Сразу после того, как мы присваиваем персонажу параметры, мы удаляем их ячейки из соответствующих массивов.

Конкретнее, playerNames.splice(namerand,1) удаляет ячейку под номером namerand из массива playerNames с помощью операции splice. Единица после запятой показывает сколько ячеек нужно удалить начиная с указанной; нам нужно удалить только одну, саму указанную ячейку.

Далее, цикл повторяется снова, и мог бы выдать undefined, если бы наткнулся на последнюю ячейку массива (мы ведь сократили его на 1). Но, так как наш Math.random использует playerNames.length и др., то он напрямую зависит от длинны массива, и будет выдавать только новые, не повторяющиеся значения.
 

4. Заключение

Дополнительно можно описать взаимодействие этого скрита со страницей: отображение «карточек» персонажей, подгрузка их портретов и т.п., но это достаточно очевидный процесс, связанный с основными функциями JS. Кроме того, я и так уже немного затянул свой нехитрый мануал. Так что, визуализацию процесса вы можете посмотреть в примере.

Демонстрация реализации (схожая, немного другие классы)

Надеюсь, данный материал будет кому-то полезен и пригодится в решении интересных задач. Успехов в JS!

Автор: sortarage

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js