How to create a weighted odds system

Hello, I have a module script with a bunch of different rarities and what their percent of being chosen is. I know I need to make a weighted chance system and I’ve tried looking for an example but haven’t really found anything. Basically what I want to achieve is a function that randomly selects a rarity from the module script and each rarity has a different chance of being chosen. Ex: If rare had a weight of 20 it would have a 20% chance of being chosen or if legendary had a 0.1 weight it would have a 0.1% chance.

Module script:

local Rarities = {
	['Basic'] ={
		weight = 30, 
		multi = 1,
	};
	['Common'] ={
		weight = 10, 
		multi = 1,
	};
	['Uncommon'] ={
		weight = 5, 
		multi = 1,
	};
	['Rare'] ={
		weight = 1, 
		multi = 1,
	};
	['Legendary'] ={
		weight = 0.66, 
		multi = 1,
	};
}

return Rarities
1 Like

ok hi,

a good way to do this is to use a table like this:

local rarities = require(MODULESCRIPT)

local function ChooseWeighted()
       local weightTable = {}
       for t, weight in pairs(rarities) do
              
              
              local multiplier = weight.weight * 100
              for m = 1, multiplier do
                     weightTable[m] = t
              end
       end

       local selection = weightTable[math.random(1, #weightTable)]
       return selection
end

output:

while wait(1) do
	print(ChooseWeighted())
end

image

1 Like

There’s a more performance and memory friendly way to do it that allows any precision (ie you can have a chance of any arbitrarity number, like 1342.352189 if you really wanted to).

In your case it would look like:

local Rarities = {
	['Basic'] ={
		weight = 30, 
		multi = 1,
	};
	['Common'] ={
		weight = 10, 
		multi = 1,
	};
	['Uncommon'] ={
		weight = 5, 
		multi = 1,
	};
	['Rare'] ={
		weight = 1, 
		multi = 1,
	};
	['Legendary'] ={
		weight = 0.66, 
		multi = 1,
	};
}

local random = Random.new()

local function generateRarity()
	local totalWeight = 0
	
	-- get the total weight
	
	for _, rarity in Rarities do
		totalWeight += rarity.weight * rarity.multi
	end
	
	-- choose a weight
	local chosenRarity = random:NextNumber(0, totalWeight)
	
	-- find a rarity that matches said weight
	for rarityName, rarity in Rarities do
		local calculated = rarity.weight * rarity.multi
		
		if chosenRarity <= calculated then
			return rarityName
		end
		
		chosenRarity -= calculated
	end
end

--[[ total weight = 46.66, so with 1 million iterations we can predict that we will get about:
Basic: 642948.993 (30 / 46.66 = 0.642948993 @ 1,000,000 iterations gives us roughly 642948.993)
Common: 214316.331
Uncommon: 107158.165
Rare: 21431.633
Legendary: 14144.877
]]

local generated = {}

for i = 1, 1_000_000 do
	local r = generateRarity()

	generated[r] = generated[r] or 0

	generated[r] += 1
end

print('Basic:', generated.Basic)
print('Common:', generated.Common)
print('Uncommon:', generated.Uncommon)
print('Rare:', generated.Rare)
print('Legendary:', generated.Legendary)

image

2 Likes

That’s so perfect for what I need thank you!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.