Pseudo-randomly Generate Unique Indices
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
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());
}
}