If you’ve got a list and you want to choose a random element from it uniformly, you can do
local random = Random.new()
...
local function pickRandom(list)
return list[random:NextInteger(1, #list)]
end
...
local picked_item = pickRandom(item_list)
If you’ve got a dictionary of items to item weights, you can pick a random item from that dictionary, weighted according to the associated weight, like this:
local item_weights = {
A = 1,
B = 5,
C = 10,
}
local function table_sum(list)
local result = 0
for _, value in pairs(list) do
result = result + value
end
return result
end
local function pick_random_weighted(dict_item_weight)
local weights_sum = table_sum(dict_item_weight)
local picked_index = random:NextInteger(1, weights_sum)
local picked_item
--Figure out which item and weight interval the picked index lies in
local interval_start = 0
for item, weight in pairs(dict_item_weight) do
local interval_end = interval_start + weight --Update the weight interval
--Check if this item is in the weight interval...
if picked_index > interval_start and picked_index <= interval_end then
picked_item = item -- ... if so, pick it...
break -- ... and stop looking.
end
interval_start = interval_end --Update the weight interval
end
return picked_item
end
--This is how you call the function
pick_random_weighted(item_weights) -- Prints something like 'C' (usually 'C', since it's the most common)
You might want to know the chance that something gets picked:
local function get_weighted_chance(dict_item_weight, item)
local weights_sum = table_sum(dict_item_weight)
local item_weight = dict_item_weight[item]
return item_weight/weights_sum
end
And you can test that it actually works as expected like this:
--Count how many times each item gets picked
local picked_counts = {}
for _ = 1, 100000 do
local picked = pick_random_weighted(item_weights)
picked_counts[picked] = (picked_counts[picked] or 0) + 1
end
--Print how often each item was picked
for k, v in pairs(picked_counts) do
print( ("%s: %d/100000\t(%.0f%%)"):format(tostring(k), v, get_weighted_chance(item_weights, k) * 100) )
end
Which outputs something like
|
|
A: 6387/100000 |
(6%) |
B: 31198/100000 |
(31%) |
C: 62415/100000 |
(62%) |
Since A has a weight of 1 and the weight sum is 16 it should have a 1/16=6.25%
chance of being picked, so everything seems to work.