In the last part, you have seen how we favored crypto.getRandomValues() than Math.random(). In this part, we'll be building a logic to provide us with weighted randomness using crypto.getRandomValues(),
For this, I started my logic by using Uint8Array, but during research for a perfect algorithm, I came across a term known as aliasing.
Though, I am still testing and researching on aliasing, but some discussions suggest that using low sample values while calculating the random value will impact the ability of the solution to be truly random.
Therefore, I proceeded with using Uint32Array
First, we'll be calculating a random value:
const cryptoArray = new Uint32Array(1);
window.crypto.getRandomValues(cryptoArray);
const randomFloat = cryptoArray[0] / (2**32);
Here, for the sake of optimization, we can precalculate 2*32 to be 4294967296 or create a constant that will hold the value of 2*32.
Second, let's define the items as an array of JSON:
const slices = [
{ id: 1, label: "Grand Prize", weight: 5 },
{ id: 2, label: "Better Luck Next Time", weight: 40 },
{ id: 3, label: "5% Discount", weight: 25 },
{ id: 4, label: "Free Shipping", weight: 30 }
];
Third, we'll be calculating the total weight of all the items:
const totalWeight = slices.reduce((sum, i) => sum + i.weight, 0);
To decide the winner, we are going to use the cumulative method.
How does it work?
Imagine we have a ruler or line of 100 units, where each item occupies its own length, respectively, of its weight.
Starting from 0, if the weight of the item is 5, then it will take 5 units of length on the scale, and its starting point and ending point become 0-5.
For the next item, we have a weight of 40; we'll be adding the same to the ending point of the previous item. The next item's starting point and ending point will become 5-45, and so on, as defined below:
- 0-5 -> Grand Prize,
- 5-45 -> Better Luck Next Time,
- 45-70 -> 5% Discount,
- 70-100 -> Free Shipping.
Now, we'll calculate a targetValue by multiplying the randomFloat (from First Step) by the totalWeight (from the third step), so that it comes between 0 and 100.
const targetValue = randomFloat * totalWeight;
If randomFloat comes out to be 0.42 and totalWeight to be 100, then our targetValue would be 42. Now we'll check where the 42 lies on the scale. It lies between 5-45, i.e., "Better Luck Next Time".
let cumulativeWeight = 0;
let winner = null;
let startWeight = 0;
for (const item of items) {
startWeight = cumulativeWeight;
cumulativeWeight += item.weight;
if (targetValue <= cumulativeWeight) {
winner = item;
break;
}
}
What is Next?
In the next parts, I'll be converting this logic to canvas based SpinWheel, and then we'll be creating a visual builder to customize the SpinWheel according to the requirements.
Hopefully, if time allows, we'll be converting the same to the React Library, Shopify App and Wordpress Plugin.

Top comments (0)