Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
1

Doing things by chance rather than math.random?

Asked by 8 years ago

Let's say I had a block, and I wanted that block to be made into a random color. But I would like some colors to be more likely than others (this done accomplished through a table).

Purple_Chance = 20
Blue_Chance = 50
Red_Chance = 30
--paint block based on chance

Any help is appreciated (Wikis, other Q&A's) NOTE:The lua block is not meant to be taken too seriously, it's just to give an example to people who like their visuals.

1
Don't mind me, I'm just bookmarking this post cuz it's too useful Konethorix 197 — 6y

1 answer

Log in to vote
8
Answered by
BlueTaslem 18071 Moderation Voter Administrator Community Moderator Super Administrator
8 years ago

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:

local list = {"red", "blue", "yellow"}
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:

local list = {"red", "red", "red", "blue", "yellow"}
local choice = list[math.random(#list)] -- 60% chance for red

For percentages, you should probably make loops to fill your list:

local list = {}
local chances = {}
chances[BrickColor.new("Purple")] = 20
chances[BrickColor.new("Bright blue")] = 50
chances[BrickColor.new("Bright red")] = 30
for thing, percentage in pairs(chances) do
    for i = 1, percentage do
        table.insert(list, thing)
    end
end
-----
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?

list = {"red", "red", "red", "blue", "yellow"}

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
      • pick thing
    • index = index - chance

alternatively, in a slightly cuter wording,

  • for each chance and thing
    • index = index - chance
    • if index < 0 then
      • pick thing

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:

local sum = 0
for thing, chance in pairs(chances) do
    sum = sum + chance
end


local index = math.random() * sum
-- note: NOT math.random(sum). That will truncate the fractional part of sum

local choice
for thing, chance in pairs(chances) do
    index = index - chance
    if index <= 0 then
        choice = thing
        break
    end
end
0
For an example code, in your answer could you include a fully functional script that colors a part in the workspace with a random color? (Percentage-Based, Personal Request, Easier understanding of how to implement your answer) magiccube3 115 — 8y
0
It would simply be `part.BrickColor = choice` after `choice` has been selected BlueTaslem 18071 — 8y
0
Where exactly in the script would that go? Like, in the last for loop after ''choice = thing'', or out of the for loop itself? drew1017 330 — 8y
0
But "work", but it's obviously better to do it all the way *after* -- at the very end -- since computing something and using that computation should be separate. BlueTaslem 18071 — 8y
View all comments (3 more)
0
And another question: Could you embed this without breaking anything? Like, lets say im using a random item spawning system with this script, and there's a chest in the choices. After the last for loop, theres an if statement checking if the choice was a chest, and if it was, this code is run again to determine what it contains. Would that work (if I of course changed the variable names)? drew1017 330 — 8y
0
Probably? It would be cleaner to turn this into a function and use that function. BlueTaslem 18071 — 8y
0
Yeah probably should have thought of that :p drew1017 330 — 8y
Ad

Answer this question