Math.Random Rarity

You can write your topic however you want, but you need to answer these questions:
Hello! I am currently creating a RNG game. With that being said I made a Rolling System, which I have a very basic version already coded! I am a upcoming programmer so I need help on something I cannot figure out/cannot find anywhere. I’ve seen numerous posts/videos about this topic and none of them go over my situation.

My situation is about a rarity system. I have a rolling system (code below) and I made and table with all the possible characters the player can receive. I already added a value named rarity. Not sure where I was going now that I think about. So if you have any feedback/suggestions please help!

–CODE:–

local RollButton = script.Parent.Roll
local RollValue = script.Parent.RollValue
local RollRarity = script.Parent.RollRarity

local Character = {
{Name = “Piggy”, Rarity = 10},
{Name =“Little Brother”, Rarity = 10},
{Name =“Mother”, Rarity = 10},
{Name =“Father”, Rarity = 10},
{Name =“Grandmother”, Rarity = 10},
{Name =“Sheepy”, Rarity = 10},
{Name =“Pandy”, Rarity = 10},
{Name =“Teacher”, Rarity = 10},
{Name =“Memory”, Rarity = 10},
{Name =“Kitty”, Rarity = 10},
{Name =“Mimi”, Rarity = 10},
{Name =“Dinopiggy”, Rarity = 10},
{Name =“Daisy”, Rarity = 10},
{Name =“Angel”, Rarity = 10},
{Name =“Pony”, Rarity = 10},

{Name =“Memory”, Rarity = 10},
{Name =“Mr.P”, Rarity = 100},
}

RollButton.MouseButton1Click:Connect(function()
local Roll = Character[math.random(#Character)]
RollValue.Text = Roll.Name
if Roll.Rarity == 10 then
RollRarity.Text = “Common”
RollRarity.TextColor3 = Color3.new(0, 1, 1)
elseif Roll.Rarity == 100 then
RollRarity.Text = “Epic”
RollRarity.TextColor3 = Color3.new(0.5, 0, 1)
end
end)

There are several ways to approach handling rarities, otherwise known as weights, through tables similar to yours. I discussed my favorite method a while back:

There are a few key differences though. Firstly, I structure my table with items as keys/indices and weights as values. You can simply reformat your data or modify the function to handle tables organized in that manner.

The behavior shown here pseudorandomly picks an element within your table regardless of weight. Another way to circumvent this that I recommend less than the above method would be to generate a separate table containing duplicate entries of each element depending on their weights. This may be slightly less performant for datasets with huge weights as table manipulation is known to be more intensive than basic arithmetic, and weights cannot be nonintegral:

local delegate = {};

for _, char in characters do -- for every character
    for i = #delegate + 1, #delegate + char.rarity do -- determine what positions the duplicates will occupy in the new table
        delegate[i] = char; -- append character the table
    end
end

local rand = Random.new();
local pick = delegate[rand:NextInteger(1, #delegate)]; --this accommodates for weights since duplicates are present
4 Likes

Awesome thank you for helping me understand this!

After adding this to the code I have some questions, if your able to help.
A.) “characters” is underlines in red
Error Message:
Players.SusNrz.PlayerGui.MainGui.Client:52: attempt to perform arithmetic (add) on number and nil

B.) Where do I put the for loop
I think I have more but it’s sub-questions I already asked.

That’s because I didn’t define the variable; I was going more for pseudocode here. characters is your character table (hence why I index rarity for every value in your character table, see the nested for-loop).

This obviously depends entirely on your code. Running that piece of code (i.e. making a “weighted” table) every time the weights or elements change would be optimal so that scripts can reference it when necessary in a timely manner.

1 Like

Alright that makes a little more sense. And then to ‘fire’ would I just add the pick when I roll?

Also looks like there is another bug
“attempt to perform arithmetic (add) on number and nil”, which is the:
for i = #delegate + 1, #delegate + char.rarity do

Again, the pseudocode simply gives you a suggestion; char.rarity (I probably should have named it char.weight since rarity is denotatively the antonym of weight) should equal the amount of duplicates you need in your weighted table; the more duplicates, the more likely the element is to be selected.

Also, consider the linked post since the function there already works standalone and is not just a framework for your ideas. You simply need to modify the function to accept tables in your desired organization or reformat your current table for the function to work as-is.

2 Likes

Ok thank you, I will look into that post. Sorry this has been like this I’m very new to programming.

Don’t worry about it! I can further break down the behaviors of both approaches for you to better understand them if you’d like. And just to make sure we’re on the same page, I would suggest converting your rarities into weights. With rarities, higher numbers correlate to “rarer” or less probable chances of selection, which is slightly counterintuitive to most people. Weights make more sense since they directly represent the likelihood of selection for their corresponding element (similar to percentages in conventional math).

Once you convert your rarities to weights (this can also be automated in code if necessary, they are typically inversely proportional), you can structure your table such that each entry is a key-value pair of a selectable element and its corresponding weight:

local selection = { -- to calculate odds, divide the element's weight by the total weight in the table
    {name = 'piggy'} = 90; -- odds of selection is 90/100 (exactly 90%)
    {name = 'mr. p'} = 10; -- odds of selection is 10/100 (exactly 10%)
};

A table in this structure works as the argument for the pick() function seen in the other post. Keep in mind that the type notation ({ [string]: number }) for the function’s parameter will create a warning since your keys are tables rather than strings. You can just remove the notation as it was strictly for clarity.

2 Likes

Alright and for the name in the example you sent me you used the name inside square brackets, I need to get the name value so would I just keep that in? (Would it work that same if I kept "name = "

Either method works because the function returns the selected key. I use the term “element” because your table’s keys can be anything. In my original post, I indeed used strings as keys, but you can use tables as shown in my last reply. When pick(selection) returns one of the tables, simply indexing name in the result (i.e. pick(selection).name) will always return the value of name within the table. All selectable tables should have a value for name to ensure that the code never receives nil when attempting to determine a name, or unwanted behavior may occur.

1 Like