Pseudo-randomly Generate Unique Indices

From Second Life Wiki
Jump to navigation Jump to search

Pseudo-randomly Generate Unique Indices

Ordinarily when a collection of data needs to be retrieved in a random order, it can simply be reshuffled by randomly swapping elements and then iterating through the resulting, shuffled, collection. However there are cases where this is infeasible, especially in LSL, and as such the following functions can be used to generate indices in a pseudo-random way that should be "good enough" for many purposes.

integer _prui_counter = 1;
integer _prui_limit = 0;

integer _prui_start = 0;
integer _prui_stride = 0;
integer _prui_next = 0;

// Configures the random number generator to generate values between 0 (inclusive) and limit (exclusive) and initialises
prui_init(integer limit) {
    _prui_limit = limit;
    _prui_start = (integer)llFrand((float)limit);
    _prui_stride = (integer)llFrand((float)limit - 1.0) + 1;
    _prui_next = _prui_start;
}

// Call this each time the next random value is required.
// When configured with a limit of N, every discreet batch of N calls to this function will return each possible index only once.
integer prui_next() {
    if (!_prui_counter) prui_init(_prui_limit);

    integer result = _prui_next;
    _prui_next = (_prui_next + _prui_stride) % _prui_limit;
    if (_prui_next == _prui_start) {
        _prui_start = (_prui_start + 1) % _prui_limit;
        _prui_next = _prui_start;
    }
    return result;
}

Examples

List

The following script will spit out a random entry from a list when touched. In this example the list contains letters of the alphabet and as such the first 26 touches will produce a unique letter, the following 26 touches will likewise produce no duplicates and so-on.

// Add the above prui variables and functions here

list myList = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
integer myListLength;

default {
    state_entry() {
        prui_init(myListLength = llGetListLength(myList));
        llSetText("Touch me for a complimentary letter of the alphabet!", <1.0, 1.0, 1.0>, 1.0);
    }
    
    touch_start(integer x) { llRegionSayTo(llDetectedKey(0), PUBLIC_CHANNEL, llList2String(myList, prui_next())); }
}

String

This is the same example rewritten to more efficiently use a string for storing the letters of the alphabet:

// Add the above prui variables and functions here

string myString = "abcdefghijklmnopqrstuvwxyz";
integer myStringLength;

default {
    state_entry() {
        prui_init(myStringLength = llStringLength(myString));
        llSetText("Touch me for a complimentary letter of the alphabet!", <1.0, 1.0, 1.0>, 1.0);
    }
    
    touch_start(integer x) { llRegionSayTo(llDetectedKey(0), PUBLIC_CHANNEL, llGetSubString(myString, x = prui_next(), x)); }
}

Notecard

Finally, this example performs the same incredible feat using lines in a notecard (one for each letter of the alphabet):

// Add the above prui variables and functions here

key myNotecard = "a62213ba-381d-2347-7f7b-818f15f12d83";
integer myNotecardLength = -1;
key talkTo = NULL_KEY;

default {
    state_entry() { llGetNumberOfNotecardLines(myNotecard); }
    dataserver(key id, string data) {
        if (myNotecardLength > 0) llRegionSayTo(talkTo, PUBLIC_CHANNEL, data);
        else prui_init(myNotecardLength = (integer)data);
    }
    
    touch_start(integer x) {
        talkTo = llDetectedKey(0);
        llGetNotecardLine(myNotecard, prui_next());
    }
}