As mentioned here, the only randomness facility in Lua is math.random
.
What you are asking for is called weighted randomness -- you give weight to particular options to make them more likely.
Simple (Bad) Approach: Repeating items in a list
Picking a random thing from a list is pretty easy:
1 | local list = { "red" , "blue" , "yellow" } |
2 | local choice = list [ math.random(#list) ] |
If we want one to be more likely than another, it can just be in the list more times:
1 | local list = { "red" , "red" , "red" , "blue" , "yellow" } |
2 | local choice = list [ math.random(#list) ] |
For percentages, you should probably make loops to fill your list:
03 | chances [ BrickColor.new( "Purple" ) ] = 20 |
04 | chances [ BrickColor.new( "Bright blue" ) ] = 50 |
05 | chances [ BrickColor.new( "Bright red" ) ] = 30 |
06 | for thing, percentage in pairs (chances) do |
07 | for i = 1 , percentage do |
08 | table.insert(list, thing) |
12 | local choice = list [ math.random(#list) ] |
This is, of course, very silly.
A bigger problem is that you can't easily use fractional percentages (e.g., 12.37% of the time would require 1000 element list while 12.1337% a 100k list), and you can't do irrational percentages (this is actually a problem -- think picking randomly based on distances between things)
A solution
Going back to the list of 5. What exactly does it mean to repeat red to get the right percentage?
1 | list = { "red" , "red" , "red" , "blue" , "yellow" } |
3 | index = math.random(#list) |
- if
index
is 1, 2, or 3, then you get "red"
- if
index
is 4, then you get "blue"
- if
index
is 5, then you get "yellow"
Let's use math.random()
with no arguments, and consider the equivalent:
- if
index
is 0 -- 0.6, then you get "red"
- if
index
is 0.6 -- 0.8, then you get "blue"
- if
index
is 0.8 -- 1, then you get "yellow"
The ranges are not very neat. Let's subtract the start from each:
- if
index - 0
is 0 -- 0.6, you get "red"
- if
index - 0.6
is 0 -- 0.2, you get "blue"
- if
index - 0.8
is 0 -- 0.2, you get "yellow"
This is super convenient, because each range is now [0, chance]
! That means we just need to know what we're subtracting by.
But that's pretty easy to see, too. It's the sum of all of the previous chances. In other words, our loop will look like this:
- for each
chance
and thing
if index < chance then
index = index - chance
alternatively, in a slightly cuter wording,
- for each
chance
and thing
index = index - chance
if index < 0 then
This is example assumed index
was originally between 0
and 1
. The key is that it's between 0 and the sum of all chances.
Thus, the code looks something like this:
02 | for thing, chance in pairs (chances) do |
07 | local index = math.random() * sum |
11 | for thing, chance in pairs (chances) do |
12 | index = index - chance |