Всем привет! Пока я работал над своей последней статьей (Перемешивание значений массива в Vue 3), я создал серию демонстраций Генератора случайных чисел, которые в конечном итоге не вошли в число лучших. для финального поста. Одной из демонстраций, которую я изначально запланировал для этой публикации, было приложение для игровых автоматов Vue.js, которое предполагало случайный цикл по массиву значений. Позже, просматривая демонстрацию, я обнаружил проблему: при циклическом просмотре случайного набора значений слотов слоты заикались по крайней мере один раз в два цикла. Присмотревшись к журналу, стало ясно, что виновниками были повторяющиеся значения подряд:

Это беспокоило меня… поэтому я решил это исправить. После некоторых проб и ошибок и консультаций по StackOverflow я считаю, что у меня есть по крайней мере одно потенциальное решение. Давайте проверим это!

Для этой статьи см. компонент под названием IndexRepeatPrevention.vue в следующем репозитории:



Для начала поговорим о компоненте, который изначально выглядел так, когда я только начинал:

<template>
  <div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
    <div class="px-4 py-6 sm:px-0">
      <div class="flex items-center justify-center py-16">

        <QuestionMarkCircleIcon v-if="slots.length < 1" class="w-96 h-96"/>

        <div v-else v-for="slot in slots" :key="slot.id" class="w-96 h-96 m-2">
          <div class="flex justify-center items-center w-full h-full p-8 text-9xl rounded-md shadow-lg border-solid border-2 border-sky-500">
            <!-- <img :src="slot.img"> -->
            {{ slot }}
          </div>
        </div>
      </div>

      <div class="flex justify-center">
        <button @click="randomize" class="group relative w-48 flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
          Randomize
        </button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from "@vue/reactivity";
import { onMounted } from "@vue/runtime-core";
import { QuestionMarkCircleIcon } from "@heroicons/vue/outline"

const slots = ref([])
  
const numberArray = ref(Array.from({length: 9}, (e, i)=> i + 1))

const sleep = (milliseconds) => {
  return new Promise(resolve => setTimeout(resolve, milliseconds))
}

const randomize = async () => {
  const list = numberArray.value.sort(() => Math.random() - 0.5)

  for (let i = 0; i < list.length; i++) {
    const index = Math.floor(Math.random() * list.length)
    await sleep(100)
    slots.value = list.filter(r => r === (index + 1))
    console.log(slots.value)
  }
}
</script>

Приведенная выше функция рандомизации получает numberArray, а затем присваивает его списку после сортировки и рандомизации. Затем внутри цикла for мы создаем новый случайный индекс для каждого цикла. Исходный список затем фильтруется с использованием каждого вновь созданного индекса, а затем повторно назначается slots.value. Используя функцию sleep, мы можем замедлить цикл цикла, чтобы увидеть каждый отдельный результат, назначенный slots.value.

Эта функциональность работает с точки зрения генерации случайных последовательностей, но она не принимает во внимание возможность последовательного повторения случайных чисел. После некоторых онлайн-копаний и консультаций я реорганизовал функцию рандомизации следующим образом:

let prevIndex = null
const generateRandIndex = max => {
  let index
  do {
    index = Math.floor(Math.random() * max)
  } while (index === prevIndex)
  prevIndex = index
  return index
}
  
const randomize = async () => {
  const list = numberArray.value.sort(() => Math.random() - 0.5)

  for (let i = 0; i < 10; i++) {
    const index = generateRandIndex(list.length)
    await sleep(100)
    slots.value = list.filter(r => r === index + 1)
  }
}

Внутри функции рандомизации мы вызовем другую функцию с именем generateRandIndex(), которая использует цикл do-while для повторного создания индекса, если значение в заданном цикле совпадает с предыдущим.

Окончательная настройка скрипта должна выглядеть так:

<script setup>
import { ref } from "@vue/reactivity";
import { onMounted } from "@vue/runtime-core";
import { QuestionMarkCircleIcon } from "@heroicons/vue/outline"

const slots = ref([])
  
const numberArray = ref(Array.from({length: 9}, (e, i)=> i + 1))

const sleep = (milliseconds) => {
  return new Promise(resolve => setTimeout(resolve, milliseconds))
}

let prevIndex = null
const generateRandIndex = max => {
  let index
  do {
    index = Math.floor(Math.random() * max)
  } while (index === prevIndex)
  prevIndex = index
  return index
}
  
const randomize = async () => {
  const list = numberArray.value.sort(() => Math.random() - 0.5)

  for (let i = 0; i < 10; i++) {
    const index = generateRandIndex(list.length)
    await sleep(100)
    slots.value = list.filter(r => r === index + 1)
    console.log(slots.value)
  }
}
</script>

Давайте посмотрим, как теперь выглядит вывод:

Вот и все... никаких повторных правонарушителей!

Еще раз проверьте эту функциональность в компоненте IndexRepeatPrevention.vue в следующем репозитории:



На этом пока хватит статей о ГСЧ. Я работаю над некоторыми демонстрациями Vue + Supabase… так что следите за обновлениями!