Недавно мы работали над проектом по созданию игры Jeopardy. Я мог бы целыми днями рассказывать обо всех проблемах, с которыми я столкнулся, обо всех забавных советах и ​​хитростях, которые я почерпнул на этом пути, а также о том, как весело я провел время с Angular.

Но я не буду этого делать.

Вместо этого я расскажу об очень конкретной проблеме, которая на самом деле меня немного интересовала, а именно:

«Создайте 3 случайных двойника в день»

Для простоты предположим, что мы уже продвинулись в проекте и наша доска выглядит примерно так:

Также для простоты предположим, что наши данные в настоящее время существуют в массиве JavaScript с пятью категориями, каждая из которых содержит массив с пятью подсказками, из которых являются объектами. (Я знаю, что на картинке их четыре, но неважно!)

С этим в сторону, давайте немного сломаем это!

Разрушая это

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

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

2: Нам нужно получить ровно 3 из них. Ни больше ни меньше.

Вот и все! Итак, теперь нам нужно начать.

Поскольку мы работаем с объектами, а объекты передаются по ссылке, нам не о чем беспокоиться. Мы согласимся добавить к ним свойство dailyDouble. Это будет логическое значение. Если это ежедневный двойник, для этого свойства будет установлено значение true.

Итак, самое интересное, рандомизация!

Это не должно быть плохо, верно? Давайте начнем с того, что просто добавим каждый отдельный элемент объекта в массив, например так:

let allClues = [];
for( let i = 0; i < data.length; i++) {
  for(let j = 0; j < data[i].length; j++) {
    allClues.push(data[i][j];
  }
}

Это поместит каждый объект в массив, что упростит нам управление. Для рандомизации потребуется немного подумать. Мы *могли* просто создать три случайные переменные, а затем получить эти элементы:

allClues[Math.Floor(Math.random() * allClues.length)].dailyDouble = true;

а затем сделайте это в общей сложности три раза:

for(let I = 0; i < 3; i++) {
   allClues[Math.Floor(Math.random() * allClues.length)].dailyDouble = true;
}

Но тут мы сталкиваемся с проблемой. Как нам избежать дубликатов?

Ну, мы могли бы исправить это, поместив его в массив, а затем проверив, появилось ли значение…

let temp = [];
for(let i = 0; i < 3; i++) {
  let randVal = Math.Floor(Math.random() * allClues.length);
  if(temp.indexOf(randVal) !== -1) {
    allClues[randVal].dailyDouble = true;
  }
}

И это сработает! Мы больше не получаем дубликатов, но по-прежнему можем получать менее трех дубликатов в день. Мы *должны* получить три!

Итак, давайте разберемся с этим:

let temp = [];
while(temp.length < 3) {
  let randVal = Math.Floor(Math.random() * allClues.length);
  if(temp.indexOf(randVal) !== -1) {
    allClues[randVal].dailyDouble = true;
}

И это сработает! То есть, если нам действительно не повезет, и мы будем получать одно и то же значение снова и снова. Хотя это случается редко, существует ненулевая вероятность того, что это может произойти, и мы хотим этого избежать.

Итак, что мы делаем тогда?

Ну, проблема в том, что нам нужны три случайных элемента, так почему бы вместо этого просто не рандомизировать весь массив? В частности, массив не является священным или важным, за исключением того, что он является местом для фактического хранения наших данных, поэтому мы можем делать с ним все, что захотим.

Для этого мы можем использовать так называемую перетасовку Дюрстенфилда. Он зацикливается назад и рандомизирует элементы.

function shuffleArray(array) {
  for (var i = array.length — 1; i > 0; i — ) {
    var j = Math.floor(Math.random() * (i + 1));
    var temp = array[i];
    array[i] = array[j];
    array[j] = temp;
  }
}
// Taken from stackOverflow 

Так как это работает?

Используйте алгоритм перетасовки для рандомизации массива, а затем просто возьмите первые три значения этого списка и установите для каждого из них значение dailydouble равным true.

let dailyDoubles = [];
let temp;
for(let I = 0; I < allClues.length — 1; i — ) {
  let j = Math.floor(Math.random() * (i + 1));
  let temp = allClues[i];
  allClues[i] = allClues[j];
  allClues[j] = temp;
}

А потом:

for(let I = 0; I < 3; i++) {
  allClues[i].dailyDouble = true;
}

И вуаля! Были сделаны! У нас есть 3 ежедневных двойных свойства по трем случайным подсказкам.

Резюмируя, идея была такой:

Вместо того, чтобы пытаться создавать случайные значения и использовать их, просто рандомизируйте свой список значений и выберите количество x из этого списка.

Вот и все! Дайте мне знать, что вы думаете в комментариях!