Weighted “choose” function

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!

Related posts:

6 thoughts on “Weighted “choose” function

  1. 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.

  2. 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.

Leave a Reply to RetroNutcase Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.