How would I create a weighted RNG system?

I have tried several methods but still cannot get the results I want.

Basically, I am transforming different blocks into a variety of materials, some materials need to be commonly spawned and others more rare.

So I have all the blocks in a group, I use a for loop to assign a material to each block, I just haven’t been able to reliably get the rarities to work.

What is the best way to go about this?

1 Like

I made a module before to help out with this:

local WeightService = {}

local rand = Random.new()

function WeightService.normalize(weightTable)
	local sum = 0
	local normalized = {}
	for index, weight in ipairs(weightTable) do
		sum += weight
	end
	for index, weight in ipairs(weightTable) do
		local newWeight = weight / sum
		normalized[index] = newWeight
	end
	return normalized
end

function WeightService.nextAction(weightTable)
	local RandomNumber = rand:NextNumber()
	local currentSum = 0
	for index, weight in ipairs(weightTable) do
		if weight < 0 then
			warn("A weight was detected to be negative. The values will be safely normalized again.")
			weightTable[index] = 0.01
			weightTable = WeightService.normalize(weightTable)
		end
		currentSum += weight
		if RandomNumber < currentSum then
			return index
		end
	end
end

function WeightService.nextActionByBestWeight(weightTable)
	local bestIndex = 0
	local highestWeight = 0
	for index, weight in ipairs(weightTable) do
		if weight > highestWeight then
			highestWeight = weight
			bestIndex = index
		end
	end
	return bestIndex
end

return WeightService

What this does, is you have a weight table with all the weights corresponding to the item in the real array. Then, randomly, a number will be picked. The more probable result will have more range, so the random value from 0 to 1 is more likely to pick it up.

You can use this module if you want to, or you can create something similar. This is just the way I discovered how.

2 Likes