Help on a random chance feature [Egg hatching system]

For a while now I have been looking into making an egg hatching system like Bubble Gum Simulator but I am not sure how they do their chances below 1. For example, lets say the most common pet is 35% chance but rarest is 0.001% chance. How would I go about that?

I have tried solutions such as a random number 1,100k and the 0.001 is equal to 1 and make it so if the random number is equal to 1 itll pick that pet but i dont know if its effective and works well because i have had some parts where a pet could be one in one thousand and you could open ten thousand but none hatched

My code:

math.randomseed(tick())
local petModule = {}
petModule.pets = {
	
	["Secret3"] = {
		"Neon Unicore"
	};
	
	["Secret2"] = {
		"Ultimate Frog"
	};
	
	["Secret1"] = {
		"Party Bell"
	};
	
	["Legendary3"] = {
		"Sweet Dragon"
	};
	
	["Legendary2"] = {
		"Chocolate Cake"
	};
	
	["Legendary1"] = {
		"Piñata"
	};
	
	["Epic1"] = {
		"Party Bear"
	};
	
	["Rare1"] = {
		"Neon Bunny"
	};
	
	["Uncommon1"] = {
		"Party Cat"
	};
	
	["Common1"] = {
		"Neon Doggy"
	};
}

petModule.rarities = {
	
	["Secret3"] = 1;

	["Secret2"] = 3;

	["Secret1"] = 10;

	["Legendary3"] = 100;

	["Legendary2"] = 500;

	["Legendary1"] = 2500;

	["Epic1"] = 50000;

	["Rare1"] = 300000;

	["Uncommon1"] = 1646886;

	["Common1"] = 3000000;
}

petModule.choosePet = function(player)
	local luck = 1
	
	
	local randomNumber = math.random(1, 5000000)
	if randomNumber <= 1 * luck then
		local rarity = "Secret"
		local rarityTab = petModule.pets["Secret3"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	elseif randomNumber <= 3 * luck then
		local rarity = "Secret"
		local rarityTab = petModule.pets["Secret2"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	elseif randomNumber <= 10 * luck then
		local rarity = "Secret"
		local rarityTab = petModule.pets["Secret1"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	elseif randomNumber <= 100 * luck then
		local rarity = "Legendary"
		local rarityTab = petModule.pets["Legendary3"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	elseif randomNumber <= 500 * luck then
		local rarity = "Legendary"
		local rarityTab = petModule.pets["Legendary2"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	elseif randomNumber <= 2500 * luck then
		local rarity = "Legendary"
		local rarityTab = petModule.pets["Legendary1"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	elseif randomNumber <= 50000 * luck then
		local rarity = "Epic"
		local rarityTab = petModule.pets["Epic1"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	elseif randomNumber <= 300000 * luck then
		local rarity = "Rare"
		local rarityTab = petModule.pets["Rare1"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	elseif randomNumber <= 1646886 * luck then
		local rarity = "Uncommon"
		local rarityTab = petModule.pets["Uncommon1"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	elseif randomNumber > 1646886 * luck then
		local rarity = "Common"
		local rarityTab = petModule.pets["Common1"]
		local chosenPet = rarityTab[1]

		return chosenPet, rarity
	end
end

return petModule
4 Likes

I assume it’s a rarity thing as you said. If you just print the number after its rolled you can see if you ever get a number, another way which I don’t know if its better or worse would be to have 2 random number generators and if they equal each other than legend?

local Num1 = math.random(1,1000)
local Num2 = math.random(1,1000)
if Num1 = Num2 then-- 1 / 1 000 000 Chance
--thing
end

yes ive done the printing the number thing but the main issue is the getting accurate and efficient chances for me

This is a valid method and as you said it would be chance based so it is possible to unlock 10000 without getting the 0.001% chance pet. Although unlikely you could technically unlock millions and never get it.

If you wanted to make it more likely to get the pet with the more eggs you hatched then you could try increasing the chance of unlocking the pet each time you hatch an egg that does not result in that pet.

So lets say,

The rarest pet is 1/1M
I make the random number go from 1,1M and if the number lands on 1 it chooses that pet.
Now, lets say the second rarest would be 1/100K
If i were to divide 1m by 100k id get 10 so i do if the number is 10 or lower it will pick that one and so on

Would that be valid?
Also i want to know if there is any other way to approach the modules because currently i am using 1 module for every individual egg which could get messy and unorganized

This is the right idea however if you wanted exactly 1/100k, since the number 1 is used in the 1/1M chance you would have to use the numbers 2-11 to have exactly 10 chances.

Depending on how much information you’re storing about each egg you could just store them in a dictionary in a module or something, that way you could access individual eggs with eggsDictionary[EggName] to get their information.

i don’t get why it has to be 2-11, it would check if the number is 1 then it’ll check if it’s 10 or lower and so on

also do you have any clue as to how bgs or popular games do their chances for pets

So if you use the number 1 as the 1/1M chance then up to now if the random number lands on 1 then you get that pet otherwise you get something else.

If you then ask if the number is less than or equal to 10 for the next rarity then its asking if its equal to:

1,2,3,4,5,6,7,8,9,10

Which is 10 numbers however since the number 1 is used for the 1/1M chance, its actually only checking then following numbers:

2,3,4,5,6,7,8,9,10

Which is only 9 numbers. 1M / 9 = 111111.111111 which is roughly a 1/111111 chance instead of a 1/100k.

I haven’t seen any examples of how they do it exactly but I would presume they would just use a similar system to this. If I find anything about it though I’ll let you know.

NOTE: you could use a more complex system by creating some kind of dictionary of rarities like you have but then picking random numbers out of the 1000000 chances and adding them to that rarity which would link that specific number to each egg type.

This may be overly complex though for no good reason. Ill run some tests and see if it makes much of a difference given that there are no true random numbers.

ah so that’s why the pets would seem harder than it should be, thanks

Yes please do let me know if you find anything on that because for bgs they have some extremely high chances like millions and billions and it all seems really accurate so i wanted to know how they did it

also, the original way i did it was that the random number would be 1,100 and each of the chances would have to be equal to 100 (just a weighted selection) would it be more accurate to do that? also if so how would i do it with decimal chances (the example 0.001 pet)

Either way should be roughly equal but up to you which one you implement.

If you want to work with decimals you could just pick a random number between 1 and max and then divide it by something e.g. 1 / 1000 = 0.001

I see,

So on the quote before where you talked about how it should be 11 instead of 10 how would i do that with a higher number like how should i replace 100,000 with its accurate chance (random number is 1,1M)

Also, i’ve heard of something called random.new so would it be easier to make a chances system with that?

So this is not the only way to do this but you could try something like this.

local rarities = {
    ["Secret3"] = {["Chance"] = 1, ["Rarity"] = "Secret"};
    ["Secret2"] = {["Chance"] = 3, ["Rarity"] = "Secret"};
    ["Secret1"] = {["Chance"] = 10, ["Rarity"] = "Secret"};
    ["Legendary3"] = {["Chance"] = 100, ["Rarity"] = "Legendary"};
    ["Legendary2"] = {["Chance"] = 500, ["Rarity"] = "Legendary"};
    ["Legendary1"] = {["Chance"] = 2500, ["Rarity"] = "Legendary"};
    ["Epic1"] = {["Chance"] = 50000, ["Rarity"] = "Epic"};
    ["Rare1"] = {["Chance"] = 300000, ["Rarity"] = "Rare"};
    ["Uncommon1"] = {["Chance"] = 1646886, ["Rarity"] = "Uncommon"};
    ["Common1"] = {["Chance"] = 3000000, ["Rarity"] = "Common"};
}

You can then use a function to get the total chance based from all rarities in the dictionary.

function GetRarityTotalChance()
	local total = 0
	for k, v in next, rarities do
		total += v["Chance"]
	end
	return total
end

local TotalChance = GetRarityTotalChance()

Now to get the correct Min and Max values for each number e.g. 1/1M was 1 and 1/100K was 2 - 11 we need to run a function to calculate this. The following function does that.

function SetRandomChanceMinMax()
	local currentMin = 0
	local currentMax = 0
	for k, v in next, rarities do
		currentMin = currentMax + 1
		currentMax = currentMax + v["Chance"]
		rarities[k]["Min"] = currentMin
		rarities[k]["Max"] = currentMax
		--print(k .. "    " .. rarities[k]["Min"] .. "     " .. currentMin .. "     " .. rarities[k]["Max"] .. "     " .. currentMax)
	end
end

SetRandomChanceMinMax()

All that’s left is to pick a random pet, this function picks a random value and checks if it is between the Min and Max of each pet rarity. Once it finds the correct one it will return the values from each dictionary.

function choosePet()
	
	local randomChance = math.random(1, TotalChance) --Chance for specific pet
	
	for k, v in next, rarities do
		if randomChance >= v["Min"] and randomChance <= v["Max"] then
			local rarity = v["Rarity"]
			--local rarityTab = petModule.pets[k]
			--local chosenPet = rarityTab[1]
			return k, rarity --Replace K with chosen pet
		end
	end
end

NOTE: these will need adding or editing to suit your module. Also the choosePet Function will need k removing from the return and changing out for your chosenPet value. Since I didn’t have this value I just returned K for testing purposes

Either should work fine for your purpose. If you’re interested in more detail however this discussion about it may be useful.

math.random() is a global random object and you may make use of it in many different places. This may lead to what appears to be inaccurate Random Chances as the next pseudo random has already been called.

Random.new() creates a new random object with a given seed e.g. tick() and can be used to return pseudorandom integers with :NextInteger(min, max) or number (decimal) (double) using :NextNumber(min,max).
Using Random.new() ensures that you will get the right distribution of rarities as no other script can call it unless you allow it.

2 Likes

I see,
A few questions i have is i don’t know what for k, v in next is
and if i were to put all the egg modules into one module would i just have to do something like petModule.CommonEgg = {

}

i also see that it says that random.new is better for fairness so wouldn’t it be better for my case

So for k, v in next, rarities is a loop through the rarities dictionary.

rarities = {
    ["Common"] = 0;
    ["Uncommon"] = 1;
}

for k, v in next, rarities do
    --k is the key so in this loop it would pass over Common and Uncommon
    --v is the value so for common it would be 0 and for uncommon it would be 1
end

NOTE: Do keep in mind that dictionaries have no specific order so it will not always do common first. Just be aware of this when expanding on code and loops involving these

you could do it like that or you could do something like this:

petModule.Eggs = {
    ["CommonEgg"] = {
        --Common Eggs Here
    }
}

Just whatever you find the easiest to maintain.

Yeah so if you plan to use math.random() in other places then random.new would be better however if you don’t then it wont make much of a difference.

NOTE: Probably worth going with with random.new from the start as it will make the game easier to expand later on.

okay, thank you for all your help and i will test this out once i get the chance and reach out if i have any following questions

No problem :slightly_smiling_face:

Good Luck with your project.

i know this isn’t related but,
how would i go about making a viewport frame into like an image

for example. i have a viewport frame that shows a pet and stuff but i want to store all of that into a image into a module script so i can just access any pets image from anywhere

I’m not totally sure, I haven’t really used Viewport Frames but this discussion might help you.

would this work?

petModule.Eggs = {
        [“Common Egg”] = { 
                Cost = {“Coins”,500},
                Secrets = {“Giant Kitty”,”TV”},
                Rarities = {
                        [“Doggy”] = {[“Chance”] = 000, [”Rarity”] = “Common”}, [“Kitty”] = {[“Chance”] = 000, [”Rarity”] = “Uncommon”}
                }
        }
}

i’d keep going