Здравейте всички! Докато работех върху последната си статия (Разбъркване на стойности на масив във 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 в следното репо:



Това ще го направи засега със статиите за RNG. След това работя върху някои демонстрации на Vue + Supabase... така че следете!