# Random blocks not being 'random'

Note, this isn’t my code. I’ve just found it from the toolbox and made a few edits to clean it up.

``````local function RandomChance(number)
if type(number) == 'number' and number < 100 then
if math.random(number, 1000) < 500 then
if math.random(number, 100) == math.random(number, 100) then
return true
else
return false
end
else
if math.random(1, 1337) == 777 then
return true
else
return false
end
end
else
return false
end
end

local function GetBlock(position)
local NonLayerChance = 100

if position > LayerLimits.Dirt then
NonLayerChance = 90
Add = tonumber(math.abs(math.floor((position - LayerLimits.Surface) / 100)) + math.random(1, 10)) or 1
elseif position > LayerLimits.Stone then
NonLayerChance = 80
Add = tonumber(math.abs(math.floor((position-LayerLimits.Surface) / 100)) + math.random(15, 30)) or 1
elseif position > LayerLimits.Marble then
NonLayerChance = 70
Add = tonumber(math.abs(math.floor((position-LayerLimits.Surface) / 100)) + math.random(25, 50)) or 1
elseif position > LayerLimits.Granite then
NonLayerChance = 60
Add = tonumber(math.abs(math.floor((position-LayerLimits.Surface) / 100)) + math.random(35, 70)) or 1
elseif position > LayerLimits.Obsidian then
NonLayerChance = 50
Add = tonumber(math.abs(math.floor((position-LayerLimits.Surface) / 100)) + math.random(60, 120)) or 1
else
NonLayerChance = 40
Add = tonumber(math.abs(math.floor((position-LayerLimits.Surface) / 100)) + math.random(75, 150)) or 1
end
else
NonLayerChance = 100
end

if math.random(1, GetChance(11, (23 - Add))) == 1 and (position > LayerLimits.Granite) then
return Blocks['Copper']
elseif math.random(1, GetChance(11, 27 - Add)) == 1 and (position > LayerLimits.Obsidian) then
return Blocks['Silver']
elseif math.random(1, GetChance(11, 32 - Add)) == 1 and (position < LayerLimits.Dirt) then
if RandomChance(7) then
return Blocks['Amethyst']
else
return Blocks['Gold']
end
elseif math.random(1, GetChance(18, 35 - Add)) == 1 and (position < LayerLimits.Stone) then
if RandomChance(8) then
return Blocks['Amethyst']
else
return Blocks['Ruby']
end
elseif math.random(1, GetChance(14, 32 - Add)) == 1 and (position < LayerLimits.Stone) then
if RandomChance(9) then
return Blocks['Amethyst']
else
return Blocks['Sapphire']
end
end
end
``````

Looking at these lines

``````if math.random(1, GetChance(11, (23 - Add))) == 1 and (position > LayerLimits.Granite) then
return Blocks['Copper']
elseif math.random(1, GetChance(11, 27 - Add)) == 1 and (position > LayerLimits.Obsidian) then
return Blocks['Silver']
elseif math.random(1, GetChance(11, 32 - Add)) == 1 and (position < LayerLimits.Dirt) then
if RandomChance(7) then
return Blocks['Amethyst']
else
return Blocks['Gold']
end
elseif math.random(1, GetChance(18, 35 - Add)) == 1 and (position < LayerLimits.Stone) then
if RandomChance(8) then
return Blocks['Amethyst']
else
return Blocks['Ruby']
end
end
``````

As every ore I find is either Copper, Silver, or Ruby. So I’m guessing the math.random isn’t that random, as it’s never getting furhter into the script to give off other ores. Is there a better to do this? I feel it’s too clunk but I don’t know how to successfully have it be random, but still increase odds of finding rare ores the further down a player is

2 Likes

I don’t really know how to make it more “random”, but what I do know is that there is no true randomness from a computational perspective.

Well is there a way for me to set that chances of ores spawning? LIke a percentage, and have the percentages increase/decrease based on the players depth

You can make a table with items based on their chance. Example:

``````local chanceTable = {"ruby", "ruby", "ruby", "diamond"}
``````

So use math.random to get a random component in the table. Here, the chance of getting “ruby” is 75%, and “diamond” 25%.

What happens if I wanted an ore to be like 1/5000? I’d have to have 4999 other options

Hi. Are you sure you used `math.randomseed` in this script or another one? This could be making the script return similar results everytime.
Also `math.random(1, 5000) == 1` could represent a chance in 5000 of returning the desired value. In fact that’s not true (math.random returns a pseudo-random number), but this doesn’t mean you can’t use this method to set specific chances. Statistically, the real chance will be similar to 1/5000 using different seeds.

Well cause if I did

``````if math.random(1, 10) == 1 then do
print('Copper')
elseif math.random(1, 25) == 1 then do
print('Silver')
elseif math.random(1, 50) == 1 then do
print('Ruby')
elseif math.random(1, 5000) == 1 then do
print('Fire Crystal')
end
``````

That’s not technically a 1 in 5000 shot. As it would have to get through the other random above it first, before the 1 in 5000

Well, I’ve always worked with probabilities and math.random using this method (not necessarily the best, but that’s all I needed; maybe a more experienced user can help you):

``````local RandomNumber = math.random(1, 5000)
if RandomNumber <= 500 then --10%
print('Copper')
elseif RandomNumber > 500 and RandomNumber <= 700 then --4%
print('Silver')
elseif RandomNumber > 700 and RandomNumber <= 800 then --2%
print('Ruby')
elseif RandomNumber == 5000 then --0.02%
print('Fire Crystal')
end
``````

Note that this wouldn’t work in case the sum of probabilities is bigger than 1.

This is a very inefficient way of programming this which mishajones made clear in his post. Every time you want to add a small item to be generated you have to calculate the math and add another elseif

The reason math.random is yielding results that seem non random is because that’s what the method does. Math.Random is a pseudo-random generator. This means there’s an underlying seed that causes the results to created in a “random” pattern.

The weight table is by far the best option in this situation. It’s easy to add and remove options, and it’s fairly easy to understand and manipulate.

https://devforum.roblox.com/t/random-blocks-not-being-random/425000/6?u=mrasync

1 Like

Using the weight table tho, I can’t see a simplified way to edit the values based on a players depth (increasing and decreasing odds of ore spawns)

I wonder if you should try using Random.new() with a seed and then try out nextInteger. That might yield very good results.

Secondly, what does the GetChance function do? I can’t find it in the provided code.

What I would personally do is use a weight table, “convert” it into an array, and use that as a randomizer. I can’t guarantee efficiency though.

``````local chances = {
Gold = 20; -- 20/5000
Silver = 60; -- 60/5000
Bronze = 4919; -- 4919/5000
Diamond = 1; -- 1/5000
}
local new_chances = {}

function updateChances()
new_chances = {}
for i, v in pairs(chances) do
for a = 1, v do
new_chances[#new_chances+1] = i
end
end
end
updateChances()

math.randomseed(os.time())

print(new_chances[math.random(1, #new_chances)])

-- lets say i want to set new weight factors
chances.Gold = 99
chances.Silver = 1
chances.Bronze = 0
updateChances()

print(new_chances[math.random(1, #new_chances)])
``````

Simple have a table like this

``````local thingsToSpawn = {
Gold = 20;
Silver = 60;
Bronze = 4919;
Diamond = 1;
}
``````

This will allow you to easily add a remove values.

But what if I wanted to have specific depths value etc?

Like a minimum depth for spawning and max depth for spawning? Setting different values for each layer, etc.

``````local chances = {
Gold = 20; -- 20/5000
Silver = 60; -- 60/5000
Bronze = 4919; -- 4919/5000
Diamond = 1; -- 1/5000
}

local new_chances = {}

function updateChances()
new_chances = {}
for i, v in pairs(chances) do
for a = 1, #v do -- ERROR HERE
new_chances[#new_chances+1] = i
end
end
end

local function GetBlock(position)
updateChances()
math.randomseed(os.time())

print(new_chances[math.random(1, #new_chances)])
end
``````

attempt to get length of a number value

Subtables would be you answer. You would have to edit your algorithm to pick but you can do something like this,

``````	local thingsToSpawn = {
Bronze = {
maxChance = 100,
minChance = 80
},
Silver = {
maxChance = 70,
minChance = 50
},
Gold = {
maxChance = 40,
minChance = 20
},
Diamond = {
maxChance = 10,
minChance = 1
}
}
``````

For different layers you would have different tables, or if you are OK with having large tables you could have one large table. You could do something like this.

``````	local thingsToSpawn = {
ThisLayer = {
Bronze = {
maxChance = 100,
minChance = 80
},
Silver = {
maxChance = 70,
minChance = 50
},
Gold = {
maxChance = 40,
minChance = 20
},
Diamond = {
maxChance = 10,
minChance = 1
}
},
ThatLayer = {
Bronze = {
maxChance = 100,
minChance = 80
},
Silver = {
maxChance = 70,
minChance = 50
},
Gold = {
maxChance = 40,
minChance = 20
},
Diamond = {
maxChance = 10,
minChance = 1
}
}
}
``````

I have mine create an array with all the choices. Then If I have just chosen and entry, I remove it from the array.

You can have it add more entries of a certain type the less it’s chosen, giving it more of a chance to be picked.

You also should change your random seed based on the os.time() if the choices are the same every game.

But really it’s a hat of names. If a name is picked, it’s removed from the hat. If one isn’t picked enough, I add more bits of paper with that name on it.

If I want to be strict, I can just tell it how many times to add each name. So maybe out of 100 or so entries, 20 are Gold, 30 are Silver, and 50 are copper. etc. But you can reduce the work by reducing the numbers and keeping the same chance. Eg, 2, 3, and 5 is the same chance as 20, 30, 50.

If it is crazy amounts 5000+ entries, you could just use a check.

If a drop, then is it a good drop? Value 1 in 5.

If it’s a good drop, is it a really good drop? 1 in 5.

If it’s a really good drop, is it a super good drop? 1 in 5.

If it’s a super good drop, is it an epic drop? 1 in 5.

Same principle though. 4 of the 5 entries are “NormalDrop” the 5th is, “GoodDrop”

If it equals “GoodDrop”, etc.

Or if you are just putting out blocks everywhere, just dump 4000 of bronze at random positions, then dump 300 copper, then 100 silver, then 20 gold.

My blocks are procedurally generated

Not sure where the idea of inefficiency of having many items in a table came from but I’ve heard it before. It’s no more efficient that any other way of doing storing many objects.

There’s no need to have 10 duplicate values? Why would you ever need to do that? I was just posing an example without an regards to actual efficiency between the lines. The only other easy way to do this efficiently is…

``````local thingsToSpawn = {
Bronze = {
ThisLayer = {
minChance = 10,
maxChance = 20
},
ThatLayer = {
minChance = 50,
maxChance = 150
}
},
Gold = {
ThisLayer = {
minChance = 10,
maxChance = 20
},
ThatLayer = {
minChance = 50,
maxChance = 150
}
}
}
``````

I’m unsure of any other alternatives to this.