Don't click here unless you want to be banned.

LSL Wiki : LibraryMultidimensionalArray

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are crawl427.us.archive.org
This script allows you to store data in lists as you would in a C++ or Java multidimensional array. It is a bit more flexible than those language's arrays, since it allows you to dynamically resize each dimension; You don't have to define the size of the array before you use it. If you ever ran into the "Runtime Error: Lists cannot contain lists" message and really needed a list within another, this is what you can use.

For example, say you had a chessboard with each square a different color. Your script can refer to each color using a two-dimensional array; one number for the row another for the column of the particular color.
default {
    state_entry() {
        integer SQUARES_ON_SIDE = 12;
        // Randomly assign colors to each square:
        list colorArray;
        integer row;
        integer col;
        for (row = 0; row < SQUARES_ON_SIDE; ++row) {
            for (col = 0; col < SQUARES_ON_SIDE; ++col) {
                vector color = <llFrand(1), llFrand(1), llFrand(1)>;
                colorArray = setArray(colorArray, [row, col], [color]);
            }
        }
        
        // Get the color of the square at row 1, column 2:
        vector squareColor = (vector) llList2String(getArray(colorArray, [0, 1]), 0); // Note that arrays, like LSL's lists, are 0-based; they start counting at 0, not 1.
        // ...

        // Get the colors of all the squares in row 4:
        list rowColors = getArray(colorArray, [3]);
        // ...
    }
}

Getting an element at index [1, 5, 2, 9], in an array "A" translates to:
Get list at index 1 in A, call this B,
Get list at index 5 in B, call this C,
Get list at index 2 in C, call this D,
Return element at index 9 in D.

And here are the functions :)
// Generates a random string of length len 
// from the characters passed to it.
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20; 
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len) {
        integer val;
        do {
            val = (integer)llFrand(60) + 20;
        } while (val == firstChar || val == lastChar);
        ret += llUnescapeURL("%" + (string)val);
    }
    return ret + llUnescapeURL("%"+(string)lastChar);
}

integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomSeperator(SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

list setListElement(list dest, list src, integer index) {
    if (src == []) {
        return llListReplaceList(src, [""], index, index);
    } else {
        integer len = llGetListLength(dest);
        for (; len < index; ++len) {
            dest += "";
        }
        return llListReplaceList(dest, src, index, index + llGetListLength(src) - 1);
    }
}


list getArray(list array, list index){
    if (index == [])
        return array;
    integer numIndicies = llGetListLength(index);
    list src = array;
    integer i;
    for (i = 0; i < numIndicies - 1; ++i) {
        integer listIndex = llList2Integer(index, i);
        string element = llList2String(src, listIndex);
        if (llGetSubString(element, 0, 0) == "l")
            element = llDeleteSubString(element, 0, 0);
        src = parseStringKeepNulls(element);
    }
    string element = llList2String(src, llList2Integer(index, -1));
    if (llGetSubString(element, 0, 0) == "l") {
        // Caller is retreiving a dimension.
        return parseStringKeepNulls(llDeleteSubString(element, 0, 0));
    } else {
        return [element];
    }
}

// To set an element, we need to extract
// each list starting from where exactly the 
// new data will be. In a set of russian dolls, this
// would be equivelant to grabbing the littlest one
// first. then gradually adding back on the layers over it.
// I think its easiest to do this recursively.
list setArray(list array, list index, list data) {
    string element;
    if (llGetListLength(data) > 1) {
        // Data is a new dimension:
        element = "l" + dumpList2String(data);
    } else {
        // Data is a single element
        element = llList2String(data, 0);
    }
    if (llGetListLength(index) > 1) {
        // index is in the form [a,b,c,d]
        // here, we grab the list that d is in.
        list containerIndex = llDeleteSubList(index, -1, -1);
        list dest = getArray(array, containerIndex);
        integer listIndex = llList2Integer(index, -1); // Grab d
        dest = setListElement(dest, [element], listIndex); // replace the element in d's list.
        // Make sure the recursion treats the container like a list, not an element.
        if (llGetListLength(dest) == 1)
            dest += "";
        return setArray(array, containerIndex, dest);
    } else {
        return setListElement(array, [element], llList2Integer(index, 0));
    }
}

Indicies are referred to like this: [1, 2, 3, 4], instead of the standard C++/Java style [1][2][3][4]. It saves the script parsing work :)

Creative Commons License
This work is licensed under a Creative Commons Public Domain License.
Comments [Hide comments/form]
This still needs improvement. I basically poked at it until I got it to work (mostly focusing on the principle of "first make it work, then make it good, then make it fast"), so it warrents some optimization. I also see a significant design flaw: it treats elements different then arrays. Though... Im not so sure its a design flaw as it is necessary for the type of parsing Im using, so Im hesitant to make a distinction.
Revisions welcome!
-- ChristopherOmega (2005-06-11 00:05:45)
There is a problem with generating seperators of 3 or more characters.

say your packing ["---AB","BA---"] and the seperator comes up as "ABA". When you depack you will get ["---","A","---"]. The fastest solution is to make sure your first and last characters are not used any where else in the string

string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    integer first = (integer)(llFrand(numChars));
    if(len > 1)
    {//if (numChars < 2 + (len > 2)){llOwnerSay("error: string too short"); list a = [[""]];}
        integer last;
        do
            last = (integer)(llFrand(numChars));
        while(last == first);
        string ret;
        integer i;
        integer randIndex;
        numChars -= 2;
        for (i = 2; i < len; i++) {
            do
                randIndex = (integer)(llFrand(numChars));
            while(last == randIndex || first == randIndex);
            ret += llGetSubString(chars, randIndex, randIndex);
        }
        return llGetSubString(chars, first, first) + ret + llGetSubString(chars, last, last);
    }
    return llGetSubString(chars, first, first);
}
-- BlindWanderer (2005-06-16 08:32:49)
Thanks for alerting me to that BW :)
I think this one might be faster (less looping required in worst case scenerio).
string randomSeperatorFrom(string chars, integer len) {
    integer numChars = llStringLength(chars);
    integer firstIndex = (integer) llFrand(numChars);
    string first = llGetSubString(chars, firstIndex, firstIndex);
    if (len == 1)
        return first;
    chars = llDeleteSubString(chars, firstIndex, firstIndex);
    --numChars;
    // Generate the last character.
    integer lastIndex = (integer) llFrand(numChars);
    string last = llGetSubString(chars, lastIndex, lastIndex);
    if (len == 2)
        return first + last;
    chars = llDeleteSubString(chars, lastIndex, lastIndex);
    --numChars;
    // Generate the string between the first and last chars.
    string middle;
    integer i;
    for (i = 2; i < len; ++i) {
        integer randIndex = (integer) llFrand(numChars);
        middle += llGetSubString(chars, randIndex, randIndex);
    }
    return first + middle + last;
}
-- ChristopherOmega (2005-07-18 11:35:42)
Apologies, the function I posted in my previous comment doesnt fix the flaw entirely, the start and end characters can still appear in any of the other characters of the seperator. The most recent edit to this page fixes it.
-- ChristopherOmega (2006-07-14 19:26:27)
list setListElement(list dest, list src, integer index)
contains an integer variable named "start".
There's no declaration of it.
-- dslb-084-062-029-078.pools.arcor-ip.net (2007-03-21 13:48:26)
fixed
-- b101-16378.student.imsa.edu (2007-03-22 09:57:50)