A little post about a little GameMaker script to randomly return one of the values while minding their "weights" - in other words, a "biased" version of choose.
The idea
First, we add up chances from each option.
Then we generate a random number between 0 (incl.) and that number (excl.).
Then, we starting subtracting chances from this number until it reaches 0 or lower, at which point we return the associated item.
If you had a lot of options, it would make sense to perform a binary search instead.
The code
For GameMaker Studio 2.3+:
function choose_weighted() { var n = 0; for (var i = 1; i < argument_count; i += 2) { if (argument[i] <= 0) continue; n += argument[i]; } n = random(n); for (var i = 1; i < argument_count; i += 2) { if (argument[i] <= 0) continue; n -= argument[i]; if (n < 0) return argument[i - 1]; } return argument[0]; }
For older versions:
var n = 0; for (var i = 1; i < argument_count; i += 2) { if (argument[i] <= 0) continue; n += argument[i]; } n = random(n); for (var i = 1; i < argument_count; i += 2) { if (argument[i] <= 0) continue; n -= argument[i]; if (n < 0) return argument[i - 1]; } return argument[0];
If you are sure that you'll never pass in negative chances, you can remove the if (argument[i] <= 0) continue; lines.
The last return statement will never execute (as random(n) cannot exceed n), but it is good practice to return on all paths in your functions.
And that's all!
Thanks for this, You are amazing
Thank you for always including GMS1 versions!
Not sure if my first comment got spam filtered.
I tried this as a scriptf function but it always returns 0.
You have any idea why?
// Execute Script
testroll = script_execute(Script1, “A”, 5, “B”, 15);
// Execute Code
show_debug_message(testroll);
If the script is a copy-paste, then you should be using choose_weighted, not Script1. script_execute is not needed in this context.
Thanks for this excellent function!
Someone recommended it here in this thread: https://forum.yoyogames.com/index.php?threads/ratio-based-chance.95114/
and it works perfectly.
I don’t fully understand how this works. Could you post an example of what you’d put into this when using the function (IE, the argument) and what your output/result would be?
An example of use is demonstrated on the first image in the post – choose_weighted(“A”, 3, “B”, 1) would yield “A” 3/4 of time and “B” 1/4 of time.