Weighted crate system not choosing an item of the same rarity as another

Hi, I made a crate system and everything works, but I’ve noticed if two or more items have the same rarity/weight it only chooses the first one found in the table. I’m not too sure how to make it work with more than one item being a certain rarity/weight, so any ideas?

Here is my code:

local crateItems = {}
local totalWeight = 0
for i, currentChance in pairs(crateData[item][7]) do
	if currentChance ~= 0 then
		totalWeight = totalWeight + currentChance[1]
		for i, currentItem in pairs(crateData[item][6]) do
			local realItem = itemData[currentItem]
			if realItem[8][4] == currentChance[2] then
				table.insert(crateItems, #crateItems + 1, {currentItem, currentChance[1]})
			end
		end
	end
end
local chance = math.random() * totalWeight
local chosenItemId
for i, weight in pairs(crateItems) do
	if chance < weight[2] then
		chosenItemId = weight[1]
		break
	else
		chance = chance - weight[2]
	end
end
local itemToGive = itemData[chosenItemId]
if PlayerOwnsItem(playerData, itemToGive[2]) then
	for i, v in pairs(playerData.items) do
        if v[1] == itemToGive[2] then
            v[3] = v[3] + 1
            break
        end
    end
else
	--Give item
end
1 Like

https://medium.com/@peterkellyonline/weighted-random-selection-3ff222917eb6 Should hopefully help you.

I already looked at that and it didn’t work for me. I later found the solution I’m using now, but as I said it doesn’t choose items of the same rarity.

Ever tried using math.randomseed()? I usually do math.randomseed(tick()) for maximum randomness.

Like this?

math.randomseed(tick())
local chance = math.random() * totalWeight

Yes. Always somewhere before using math.random().


If it still doesn’t work, I have some code that I can possibly extract from my game. Have you checked this tutorial before?

Warning: I am currently unable to obtain the code as I am away on vacation from Studio

After looking at the tutorial, I’ve noticed they did their system a little different. Instead of having one table containing all outcomes with the item’s name and weight, they have a few tables for each rarity. It goes like this:

First, choose a rarity depending on how rare it is to get that rarity.
Second, choose a random thing in that chosen rarity without weight.

I’ll try to implement this and see the outcome.

You still use randomseed? Wowie. Have you not been introduced to my friend, the superior object-based Random API? This allows you to create a random number generator with a seed that’s local to that object, while randomseed produces a globally random seed.

Random.new(tick()):NextNumber() * totalWeight

This object also has a clone method, which you can use to branch results.

3 Likes

Don’t worry about passing in tick() as a seed argument; it uses a better entropy source by default.

The seed parameter is for passing in fixed seeds like 42 so you get deterministic results over multiple runs.

Internally, math.random uses a default-constructed Random object based on that internal entropy source. Since math.random state is shared by the script’s VM, you’re never going to get it completely deterministic because other scripts will call it and interfere with the results.

Because of that, randomseed is useless at this point and probably safe to deprecate. If you need predictability then you need a Random object, and if you don’t need predictability then you don’t call randomseed at all.

1 Like