Help with factoring in luck in rng systems

To start, I’m pretty bad at math until something is explained to me, so, if anyone is willing to help with my problem I’d also really appreciate an explanation alongside it. I’ve made a lot of weighted tables for drop systems and such but never have I been able to factor in a luck variable that works how other games seem to achieve it.

Right now, I’m trying another type of “loot-table” I guess, similar to how rng games present it but more than ever I’m struggling to add luck into the equation. Here’s my test without any attempt of adding luck:

--//Services
--\\
--//Modules
--\\
--//Const Variables
local random = Random.new(tick())
local rolls = {
	{name = "1 in 1", rarity = 1};
	{name = "1 in 2", rarity = 2};
	{name = "1 in 4", rarity = 4};
	{name = "1 in 8", rarity = 8};
	{name = "1 in 16", rarity = 16};
	{name = "1 in 32", rarity = 32};
	{name = "1 in 64", rarity = 64};
	{name = "1 in 128", rarity = 128};
	{name = "1 in 256", rarity = 256};
	{name = "1 in 512", rarity = 512};
	{name = "1 in 1024", rarity = 1024};
	{name = "1 in 2048", rarity = 2048};
	{name = "1 in 4096", rarity = 4096};
	{name = "1 in 8192", rarity = 8192};
	{name = "1 in 16384", rarity = 16384};
	{name = "1 in 32768", rarity = 32768};
	{name = "1 in 65536", rarity = 65536};
	{name = "1 in 131072", rarity = 131072};
}
--\\
--//Variables
local rarestRoll = 0
--\\
--//Other
--\\
--//Functions
script.Parent.MouseButton1Click:Connect(function()
	local total = 0
	for _, roll in ipairs(rolls) do
		total += 1 / roll.rarity
	end
	
	local randomNumber = random:NextNumber()

	local accumulatedChance = 0
	for i, roll in ipairs(rolls) do
		accumulatedChance = accumulatedChance + (1 / roll.rarity) / total
		if randomNumber <= accumulatedChance then
			if roll.rarity > rarestRoll then
				rarestRoll = roll.rarity
			end
			print(string.format("Rolled: %s | Highest Roll: %i", roll.name, rarestRoll))
			return
		end
	end
end)
--\\

I have tried a few methods of adding in luck, some appear to work but I’m getting 1 in 1s along with 1 in 8ks regularly which isn’t aligning with what I’d want. I’ll present one method I’ve tried with adding luck:

--//Services
--\\
--//Modules
--\\
--//Const Variables
local random = Random.new(tick())
local rolls = {
	{name = "1 in 1", rarity = 1};
	{name = "1 in 2", rarity = 2};
	{name = "1 in 4", rarity = 4};
	{name = "1 in 8", rarity = 8};
	{name = "1 in 16", rarity = 16};
	{name = "1 in 32", rarity = 32};
	{name = "1 in 64", rarity = 64};
	{name = "1 in 128", rarity = 128};
	{name = "1 in 256", rarity = 256};
	{name = "1 in 512", rarity = 512};
	{name = "1 in 1024", rarity = 1024};
	{name = "1 in 2048", rarity = 2048};
	{name = "1 in 4096", rarity = 4096};
	{name = "1 in 8192", rarity = 8192};
	{name = "1 in 16384", rarity = 16384};
	{name = "1 in 32768", rarity = 32768};
	{name = "1 in 65536", rarity = 65536};
	{name = "1 in 131072", rarity = 131072};
}
--\\
--//Variables
local rarestRoll = 0
--\\
--//Other
--\\
--//Functions
script.Parent.MouseButton1Click:Connect(function()
	local luck = 1
	
	local total = 0
	for _, roll in ipairs(rolls) do
		total += 1 / (roll.rarity / luck)
	end
	
	local randomNumber = random:NextNumber()

	local accumulatedChance = 0
	for i, roll in ipairs(rolls) do
		accumulatedChance = accumulatedChance + (1 / (roll.rarity / luck)) / total
		if randomNumber <= accumulatedChance then
			if roll.rarity > rarestRoll then
				rarestRoll = roll.rarity
			end
			print(string.format("Rolled: %s | Highest Roll: %i", roll.name, rarestRoll))
			return
		end
	end
end)
--\\

I’m aware the issue with this is I’m dividing all the chances equally so no matter what I set that “luck” value to it doesn’t technically change any odds at all.

Any help whatsoever getting me in the right direction I’d really appreciate, I’ve spent time going through multiple topics in the forums but the answers never really clicked with me in a way I could understand; which is why I’ve made some test code so hopefully any answer is more understandable to me.

2 Likes

Try:

--//Services
--\\
--//Modules
--\\
--//Const Variables
function roll()
local rand = 1e33/math.random(1,1e33)
rand*=luck --define luck
return random
end
--\\
--//Variables
local rarestRoll = 0
--\\
--//Other
--\\
--//Functions
script.Parent.MouseButton1Click:Connect(function()
	local r = roll()
        local rarity = math.floor(math.log(r,2))
        local rarityname = "1 in "..tostrring(2^rarity)
        if rarity> rarestRoll then
           rarestRoll = rarity
	end
	print(string.format("Rolled: %s | Highest Roll: %i", rarityname, 2^rarestRoll)) --assuming rarestRoll is a number
	end)

This system is much better than weighted system, and you don’t have to add values for each rarity since next rarity is 2 times rarer than the previous.

1 Like

Sorry if I wasn’t clear, that rarity table was just an example as I’m currently working with dummy code but I wanted to define elements with exact rarities

1 Like

You can make the rarity names somewhere in a module and refer to a rarity name as rarityNames[rarity] (rarityNames is the table that contains rarity names). By the way, did I solve the problem?

1 Like

Not exactly as I meant for the table of possible rolls to contain elements with varying rarities, the multiples of 2 again was just for dummy code

1 Like

For some additional context, I’ve tried several luck implementations I’ve found across various sources, the most common I see uses an exponent but if I use my own code above as an example it furthest from working as I’d like.

Take the rarest entry in my table, 131072. The equation I’ve seen used is

131072 ^ (1 / luck)

say luck is 2, that changes the rarity from 131072 to 362 and, with more luck, everything comes down basically to 1.

Maybe my current approach is entirely wrong, I’ve seen many games seemingly nail this but I can’t fathom how to replicate it. For example, some games, with enough luck, you’ll outright stop seeing 1 in 1s and consistently get much rarer outputs.

The correct formula for that is 100/x
1/131072 would give you a 7.63e-6% chance.

After many hours of trial and error I’ve finally made something that aligns with what I was originally asking for. I still really appreciate people trying to help me, it was my fault for being unclear.

The code I ended on (simplified into 1 script for ease of sharing):

--//Services
--\\
--//Modules
--\\
--//Const Variables
local random = Random.new(tick())
local luck = 1
--\\
--//Variables
local highestRoll = 0
--\\
--//Other
--\\
--//Functions
local function Rarities()
	return	{
		{name = "1 in 4", rarity = 4};
		{name = "1 in 5", rarity = 5};
		{name = "1 in 8", rarity = 8};
		{name = "1 in 10", rarity = 10};
		{name = "1 in 16", rarity = 16};
		{name = "1 in 18", rarity = 18};
		{name = "1 in 20", rarity = 20};
		{name = "1 in 32", rarity = 32};
		{name = "1 in 36", rarity = 36};
		{name = "1 in 50", rarity = 50};
		{name = "1 in 64", rarity = 64};
		{name = "1 in 71", rarity = 71};
		{name = "1 in 80", rarity = 80};
		{name = "1 in 100", rarity = 100};
		{name = "1 in 111", rarity = 111};
		{name = "1 in 143", rarity = 143};
		{name = "1 in 154", rarity = 154};
		{name = "1 in 192", rarity = 192};
		{name = "1 in 200", rarity = 200};
		{name = "1 in 200", rarity = 200};
		{name = "1 in 250", rarity = 250};
		{name = "1 in 303", rarity = 303};
		{name = "1 in 909", rarity = 909};
		{name = "1 in 1k", rarity = 1000};
		{name = "1 in 1.25k", rarity = 1250};
		{name = "1 in 1.25k", rarity = 1250};
		{name = "1 in 1.33k", rarity = 1330};
		{name = "1 in 1.5k", rarity = 1500};
		{name = "1 in 2k", rarity = 2000};
		{name = "1 in 2.5k", rarity = 2500};
		{name = "1 in 5k", rarity = 5000};
		{name = "1 in 6.9k", rarity = 6900};
		{name = "1 in 7.5k", rarity = 7500};
		{name = "1 in 8k", rarity = 8000};
		{name = "1 in 11.1k", rarity = 11100}
	}
end

script.Parent.MouseButton1Click:Connect(function()
	local updatedRolls = {}
	
	local totalWeight = 0
	for index, roll in Rarities() do
		local weight = roll.rarity
		local updatedWeight = math.floor(weight * (1 / luck))
		
		if updatedWeight < 1 then continue end
		
		totalWeight += 1 / updatedWeight
		
		updatedRolls[index] = {
			name = roll.name;
			rarity = 1 / updatedWeight
		}
	end
	
	local randomNumber = random:NextNumber(0, totalWeight)
	
	local accumulatedWeight = 0
	for index, roll in updatedRolls do
		accumulatedWeight += roll.rarity
		
		if randomNumber <= accumulatedWeight then
			if Rarities()[index].rarity > highestRoll then highestRoll = Rarities()[index].rarity end
			
			print(string.format("Rolled %s | Highest Roll: %i", roll.name, highestRoll))
			
			break
		end
	end
end)
--\\

If you were to say set luck to 1000 you’d have (1000*100)% or 100,000% increased luck. As things become too common their entry is ignored in the completed table of possible rolls.

Hopefully this helps anyone who comes across the same issue I’ve been having, thanks again!

Edit: Jumped the gun and pasted the wrong code, resolved now.

2 Likes

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