Adding Luck To RNG

Hi, I am trying to add a luck system to my RNG game where the player essentially starts out with 1x luck and can get upgrades to add 0.01x luck or 1x or 2x ect. I am trying to understand how to implement this into the base roll system. I have already created a saving stat for the luck as a number just need to have it applied. This is the script which generates the math.

local module = {
	rewards = {
		{
			Name = 'Common',
			Chance = 2,
			Price = 10
		},
		{
			Name = 'Uncommon',
			Chance = 5,
			Price = 25
		},
		{
			Name = 'Rare',
			Chance = 10,
			Price = 100
		},
		{
			Name = 'Epic',
			Chance = 50,
			Price = 750
		}
	}
}

local MaxNum = 100000000

function module:MakeNewReward()
	local _RandomNum = math.random(1, MaxNum/2) / MaxNum -- Limit to 0.5
	local _ReturnTab = nil
	
	warn('Random Number Choosen: '.._RandomNum)
	
	for i = #module.rewards, 1, -1 do
		local Chance = (1/module.rewards[i].Chance)
		local Price = (module.rewards[i].Price)
		print('Current Reward: '..module.rewards[i].Name..', '..Chance)
		

		
		if _RandomNum <= Chance then _ReturnTab = {
			module.rewards[i].Name,
			module.rewards[i].Chance,
			module.rewards[i].Price
			} 
			warn('Reward choosen, RewardChance '..Chance..' & RandomNum '.._RandomNum.. 'PRICE:'.. Price)
			break else continue end
	end
	
	return _ReturnTab
end

return module
3 Likes

Funny how your probabilities don’t even add up to 100, although that’s what’s usually done. Don’t get me wrong: it’s NOT necessary for it to happen now, but it’s just something that’s usually done. The actual probabilities for each element are:
common: 2/(2+5+10+50)
uncommon: 5(2+5+10+50)
and so on…

Now, how is it done? Simple as that: generate a number between 1 and your max prob. (i.e. 67). If the number is between 1 and 2 you get common, else if it’s between 3 and 5 you get uncommon, and so on.

Also, careful reader could even add to the post that you probabilities make no sense: why is “common” having a probability of 2/67 while epic is 50/67? I suggest you review your code.

1 Like

The chance number corresponds to 1 in X not the total number so Common is 1 in 2, Uncommon is 1 in 5 and so on

1 Like

I don’t quite understand. Why would COMMON be 5 times more rare than RARE? That’s what I got from your definition.

Other way round, 1 in 2 means 50% chance whereas 1 in 10 means 10% chance.

This screenshot may help to understand. basically it rolls a number and if the number is below the printed values you can see it rewards that value

Instead of using the regular chance system try using weighted random. Basically it’s more efficient, and easier. Also allows to implement new rarities and/or change the luck w/o any worries.

Here is an example

local module = {
	rewards = {
		{
			Name = 'Common',
			Chance = 50,
			Price = 10
		},
		{
			Name = 'Uncommon',
			Chance = 10,
			Price = 25
		},
		{
			Name = 'Rare',
			Chance = 5,
			Price = 100
		},
		{
			Name = 'Epic',
			Chance = 2,
			Price = 750
		}
	}
}

--

local MaxNum = 0

for i,v in pairs(module.rewards) do
	MaxNum += v.Chance
end

-- !! The higher the chance, the more often the rarity is picked
-- !! Chance should be an integer
-- !! Luck should not be negative or equal to 0


function module:MakeNewReward(luck : number)
	local _ReturnTab
	local chancePicked = math.random(1, MaxNum / luck)
	-- You can either put division in or out the brackets
	
	local totalChance = 0
	
	warn('Random Number Choosen: '..chancePicked)
	
	for i,v in pairs(module.rewards) do
		totalChance += v.Chance
		
		if totalChance >= chancePicked then
			warn('Reward choosen, RewardChance '..v.Chance..' & RandomNum '.. chancePicked .. 'PRICE:'.. v.Price)
			
			_ReturnTab = table.clone(v)
			break
		end
	end

	return _ReturnTab
end

return module
1 Like

I think you forgot to put the luck to the function :confused:

1 Like

yes i did i just realised that, im just now trying to figure out how i can define the luck

if i have my luck stat as a leaderstat how can I incorporate that into the script?

can’t you just refer to it when calling the function?

like typing
require(script):MakeNewReward(player.leaderstats.Luck.Value)

1 Like

Player is an unknown global as it is in a module script i was having issues earlier today with trying to figure out how to call the player within a module script. This is my first time doing actual work with them

This is what I have changed it to and it doesnt error for getting the player

function module:MakeNewReward(luck : number)
	local player = game:GetService("Players").LocalPlayer

	luck = player.Luck.Luck.Value
	local _ReturnTab
	local chancePicked = math.random(1, MaxNum / luck)

but it does give the error

ReplicatedStorage.Rewards:43: attempt to index nil with 'Luck'

Im not sure why that would happen as player.Luck.Luck.Value is the correct place for the stat

1 Like

Could you send the screenshot of leaderstats?

This is the stats in game:
Screenshot 2025-04-05 214725

Are you sure you are creating those folders on a server script and/or they have correct names?

Yes the Luck stat functions correctly as I have another script which will add to it and it shows the live value of the stat on a GUI

Try calling the function after some delay. Maybe the folders might not be created by that time

That seems to solve the error showing up but unfortunately now nothing functions. I will try to see if i can make it work but im assuming that in the script you made it has some key things different that the other scripts might need

Idk if it will resolve the issue, but the only major thing i did was just swapping rarity chances

You should just add a multiplier to all the values in luck. The exact specifics of this multiplier depends on how exactly you want luck to scale. Note that because of how luck scales so dramatically you will likely want to make the actual luck value non linear in application. And this is just an example of course. And you can make more complex individual multipliers. The important part is that you have to apply the luck multiplier, count up the new luck total and then run the weighted chance algorithm based on the new weights. Here is a quickly written example.

But basically

rewards = {
		{
			Name = 'Common',
			Chance = 50,
			Price = 10,
                        LuckMultiplier = 1.0025
		},
		{
			Name = 'Uncommon',
			Chance = 10,
			Price = 25,
                        LuckMultiplier = 1.005
		},
		{
			Name = 'Rare',
			Chance = 5,
			Price = 100,
                        LuckMultiplier = 1.01
		},
		{
			Name = 'Epic',
			Chance = 2,
			Price = 750,
                        LuckMultiplier = 1.0125
		}
	}



local function pick_value(effective_luck)
    local chances = {}
    local chances_sum = 0
    for i, v in ipairs(rewards) do
        local newChance = v.Chance * math.pow(v.LuckMultiplier, luck) --Remember you may want to transform luck here to not be so dramatic so fast and maybe cap it or go piecewise or whatever
        chances[i] = newChance
        chances_sum += newChance
    end
    
    local r = math.random() * chances_sum
    for i,v in ipairs(chances) do
        r-=v
        if r <= 0 then
            return rewards[i]
        end
    end
    error("This was supposed to have hit 0 at this point, so something went wrong")
end