How to make a chance system with very rare chances

ah so as i said in my last reply to outcharm, i just multiply until its a whole number, thatll be accurate?

Not necessarily because if it is already a whole number, the weighing would be unbalanced because all of a sudden all the numbers a whole. You should use a common multiplier on all the numbers to keep the proportionality.

Modified Code:

function percentToWeight(percent)
	return percent * 100 --Change this if needed, I assumed 100 because you are only going to the 100ths place
end

local Items = {
	{"Table", percentToWeight(0.01)},
	{"Lamp", percentToWeight(0.5)},
	{"Chair", percentToWeight(0.25)},
}
local TotalWeight = 0

for _,ItemData in pairs(Items) do
	TotalWeight = TotalWeight + ItemData[2]
end

local function chooseRandomItem()
	local Chance = math.random(1,TotalWeight)
	local Counter = 0
	for _,ItemData in pairs(Items) do
		Counter = Counter + ItemData[2]
		if Chance <= Counter then
			return ItemData[1]
		end
	end
end

for i = 1, 20 do
	print(chooseRandomItem())
end
1 Like

You just have to multiply it by the right number yes, like in the example I provided, I just multiplied everything by 10 so they were all whole numbers:

.1 → 1, 99.9 → 999

If you want to use smaller decimals, such as .01, you’d need to multiply them by 100, and for .001 by 1000 and so on.

well the 0.01 was just an example, the core of the game has to do with rng so itd be up to the millions or however id like the rarest to be

also, there will be a lot of changes from the rarest in the egg, so how would i be able to make it check how much is necessary

You can use functions to calculate total weights so you can have very rare chances, such as .001% and very common chances, such as 5%.

See here (you’d need to modify this code to work for yourself obviously):


local eggs = {}

eggs.Egg1 = {
	Dog = 45; -- 45 % chance
	Cat = 45; -- 45 % chance
	Dinosaur = 10; -- 10% chance
}

eggs.Egg2 = {
	Cow = 99999; -- 99.999% chance
	Rainbow_Cow = 1; -- 0.001% chance
}


function GetChanceOfEgg(egg)
	local chance = 0
	
	for eggName,eggChance in pairs(egg) do
		chance += eggChance
	end
	
	return chance
end


function PickRandomFromEgg(egg)
	
	local chance = GetChanceOfEgg(egg)
	
	local chance = math.random(0, chance)
	
	for eggName,eggChance in pairs(egg) do
		chance -= eggChance
		
		if chance <= 0 then
			return eggName
		end
	end	
end



local pet = PickRandomFromEgg(eggs.Egg2)


print(pet)


-- Extra Code To Test Probability:

local n = 0

repeat chosenPet = PickRandomFromEgg(eggs.Egg2) n += 1 until
chosenPet == "Rainbow_Cow"

print("It took " .. n .. " tries until you got a " .. chosenPet)

2 Likes
local Items = {
	{"Table", 2},
	{"Lamp", 0.5},
	{"Chair", 0.25},
}

local multiplier = 1

for _, item in pairs(Items) do --Essentially edits the multiplier so that even the smallest decimal will become whole
	local function zeros(amount)
		local total = "1"
		for i = 1, amount do
			total = total.. "0"
		end
		return total
	end
	local split = string.split(tostring(item[2]), ".")
	if split[2] ~= nil then
		if tonumber(zeros(string.len(split[2]))) > multiplier then
			multiplier = tonumber(zeros(string.len(split[2])))
		end
	end
end
print(multiplier)
for _, item in pairs(Items) do
	item[2] = item[2] * multiplier
end

local TotalWeight = 0

for _,ItemData in pairs(Items) do
	TotalWeight = TotalWeight + ItemData[2]
end

local function chooseRandomItem()
	local Chance = math.random(1,TotalWeight)
	local Counter = 0
	for _,ItemData in pairs(Items) do
		Counter = Counter + ItemData[2]
		if Chance <= Counter then
			return ItemData[1]
		end
	end
end

for i = 1, 20 do
	print(chooseRandomItem())
end
20 Likes

well ive tested this with lamp and table, table being 0.01% and i got it at a reasonable attempt so its all looking good so far, thanks

3 Likes

Glad @outcharm and I could help :smile:! Reply if you come across anything else relating to the code that was sent.

4 Likes

hello, so ive made this implementing the system into mine

function multiplyToWhole(egg)
	local multiplier = 1

	for _, pet in pairs(eggModule.Eggs[egg].Rarities) do --Essentially edits the multiplier so that even the smallest decimal will become whole
		local function zeros(amount)
			local total = "1"
			for i = 1, amount do
				total = total.. "0"
			end
			return total
		end
		local split = string.split(tostring(pet["Chance"]), ".")
		if split[2] ~= nil then
			if tonumber(zeros(string.len(split[2]))) > multiplier then
				multiplier = tonumber(zeros(string.len(split[2])))
			end
		end
	end
	print(multiplier)
	for _, pet in pairs(eggModule.Eggs[egg].Rarities) do
		pet["Chance"] = pet["Chance"] * multiplier
	end
end

eggModule.ChoosePetTest = function(eggToHatch)
	multiplyToWhole(eggToHatch)
	local TotalWeight = 0

	for _,PetData in pairs(eggModule.Eggs[eggToHatch].Rarities) do
		TotalWeight = TotalWeight + PetData["Chance"]
	end


	local Chance = math.random(1,TotalWeight)
	local Counter = 0
	for i,PetData2 in pairs(eggModule.Eggs[eggToHatch].Rarities) do
		Counter = Counter + PetData2["Chance"]
		if Chance <= Counter then
			local rarityTab = eggModule.Eggs[eggToHatch].Pets[i]
			local chosenPet = rarityTab[1] or rarityTab
			
			local cost = eggModule.Eggs[eggToHatch].Cost
			
			local rarity = PetData2["Rarity"]
			local percent = PetData2["Percent"]
			
			local legendtier
			if rarity == "Legendary" then
				legendtier = PetData2["Tier"]
			else
				legendtier = 1
			end

			local divinetier
			if rarity == "Divine" then
				divinetier = PetData2["Tier"]
			else
				divinetier = 1
			end
			
			return chosenPet, rarity, percent, cost, legendtier, divinetier
		end
	end
end

just wanted to know if this will work.
also just in case, i have my table setup like this:

["Common Egg"] = {
		Cost = 500,
		Secrets = {"Giant Kitty","TV"},
		Rarities = {
			["Doggy"] = {["Chance"] = 37.38595, ["Rarity"] = "Common",["Percent"] = "37.3"},["Kitty"] = {["Chance"] = 27.5, ["Rarity"] = "Uncommon",["Percent"] = "27.5"},
			["Bunny"] = {["Chance"] = 25, ["Rarity"] = "Rare",["Percent"] = "25"},["Bear"] = {["Chance"] = 10, ["Rarity"] = "Epic",["Percent"] = "10"},
			["Deer"] = {["Chance"] = 0.1, ["Rarity"] = "Legendary",["Percent"] = "0.1",["Tier"] = 1},["Dragon"] = {["Chance"] = 0.01, ["Rarity"] = "Legendary",["Percent"] = "0.01",["Tier"] = 2},
			["Totem (A)"] = {["Chance"] = 0.002, ["Rarity"] = "Legendary",["Percent"] = "0.002",["Tier"] = 3},["Totem (B)"] = {["Chance"] = 0.002, ["Rarity"] = "Legendary",["Percent"] = "0.002",["Tier"] = 3},
			["Giant Kitty"] = {["Chance"] = 0.00004, ["Rarity"] = "Secret",["Percent"] = "4e-05"},["TV"] = {["Chance"] = 0.00001, ["Rarity"] = "Secret",["Percent"] = "1e-05"}
		},
		Pets = {
			["Doggy"] = {"Doggy"}, ["Kitty"] = {"Kitty"},
			["Bunny"] = {"Bunny"},
			["Bear"] = {"Bear"}, ["Deer"] = {"Deer"}, ["Dragon"] = {"Dragon"}, ["Totem (A)"] = {"Totem (A)"}, ["Totem (B)"] = {"Totem (B)"}, ["Giant Kitty"] = {"Giant Kitty"}, ["TV"] = {"TV"}
		}
	},

nvm, it works fine,
how can i implement a x2 luck thing, everything im trying isnt working

On this line, you may be able to modify it so that it would look like this:

if plrOwnsMultiplierPass then
    --Another if statement to see if the rarity counts in the multiplier. Otherwise, the probability will remain the same.
   Counter = Counter + PetData2["Chance"] * 2
else
    Counter = Counter + PetData2["Chance"]
end

ah let me give that a try (chars)

wel actually, would this multiply all the chances (no difference), what if i want just the epics+ to be x2 only

nvm, i just added an if statement to check its rarity, thanks

im getting an error where it says “invalid argument #2 to random” (caused by ‘TotalWeight’ being 0

code:

local TotalWeight = 0

	for _,PetData in pairs(eggModule.Eggs[eggToHatch].Rarities) do
		TotalWeight = TotalWeight + PetData["Chance"]
	end


	local Chance = random:NextInteger(1,TotalWeight)

If the total weight has a value of 0 when the random is being called, you will get an error because the minimum is greater than the maximum. (1>0)

random:NextInteger(min,max)

yea, im aware of that but what i dont understand is why the total isnt over 1, it should be like way over 1

You have probably figured this out by now but The reason is because some of the total Weights end up below 1 when the chance for the item is lower than 1 since you are just basically setting the total weight to equal the chance there (TotalWeight = TotalWeight + PetData["Chance"]) due to the TotalWeight Starting as 0. I would either set TotalWeight To 1 or The Min Random Value to 0.

edit: oh wait random wont generate a decimal.
edit2: you could instead use Random.new():NextNumber(Min,Max) which can return decimals. Also, For Random.new():NextNumber(Min,Max) the Min and max can be reversed, it just returns a random number between the two inputed values.

May i ask,
This is me being a bit stupid but is the 2 for table 2%? or is it 20? or 0.2? sounds like a dumb question but yeah

1 Like

It’s the table index, basically the second item in that table, you mean the ‘ItemData[2]’ right?