pgs_regenerator.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2011 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
// Reference land types
list _landReference = [ "Green", 1, "Black", 2, "Brown", 3 ];
// Reference genomes
list _genotypeReference = [ "BB", 1, "Bb", 2, "bB", 3, "bb", 4 ];
 
vector _iPos = ZERO_VECTOR;
integer _comChannel = 0;
integer _mouseID = 1;
 
string _landType = "";
integer _allelesB = 0;
integer _allelesb = 0;
 
// Owner key.
key _owner = NULL_KEY;
 
// A quicksort, providing a stable map between two sets of elements.
list dualQuicksort(list a, list b) {
 
    if(llGetListLength(a) <= 1) return a+b;
 
    float pivot_a = llList2Float(a, llGetListLength(a)/2);
    string pivot_b = llList2String(b, llGetListLength(b)/2);
 
    a = llDeleteSubList(a, llGetListLength(a)/2, llGetListLength(a)/2);
    b = llDeleteSubList(b, llGetListLength(b)/2, llGetListLength(b)/2);
 
    list less = [];
    list less_b = [];
    list more = [];
    list more_b = [];
 
    integer i = 0;
    do {
        if(llList2Float(a, i) > pivot_a) 
        {
            less += llList2List(a, i, i);
            less_b += llList2List(b, i, i);
        }
        else
        {
            more += llList2List(a, i, i);
            more_b += llList2List(b, i, i);
        }
    } while(++i<llGetListLength(a)*2);
 
    return dualQuicksort(less, less_b) + [ pivot_a ] + [ pivot_b ] + dualQuicksort(more, more_b);
}
 
vector circlePoint() {
    float y = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(6);
    float z = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(6);
    if(llPow(y,2) + llPow(z,2) <= 36)
        return _iPos + <0.2, y, z>;
    return circlePoint();
}
 
// Returns the next random coordinates.
//vector nextCoordinates() {
//    float r = llFrand(10);
//    float a = llFrand(TWO_PI);
//    float b = llFrand(TWO_PI);
//    return <_iPos.x, _iPos.y + r * llCos(a), _iPos.z + r * llSin(b)>;
//    // use circle instead...
//    //return _iPos + <r * llCos(a) * llCos(b), r * llCos(a) * llSin(b), r * llSin(a)>;
//}
 
default
{
    state_entry()
    {
        _allelesB = 0;
        _allelesb = 0;
        _mouseID = 1;
        _owner = llGetOwner();
        _iPos = llGetPos();
        _comChannel = ((integer)("0x"+llGetSubString(_owner,-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        llListen(_comChannel+3, "", "[K] Brown Moth", "");
        llListen(_comChannel+3, "", "[K] Black Moth", "");
    }
    link_message(integer sender_num, integer num, string str, key id) {
        list opt = llParseString2List(str, [",", ":"], []);
        integer itra = llGetListLength(opt)-1;
        do {
            if(llList2String(opt, itra) == "PGS_STOP") {
                _allelesB = 0;
                _allelesb = 0;
                _mouseID = 1;
                jump next_msg;
            }
            if(llList2String(opt, itra) == "LAND") {
                _landType  = llList2String(opt, itra+1);
                jump next_msg;
            }
            if(llList2String(opt, itra) == "GENOME") {
                list alleles = llParseString2List(llList2String(opt, itra+1), [], ["B", "b"]);
                integer itrb=llGetListLength(alleles)-1;
                do {
                    if(llList2String(alleles, itrb) == "b") {
                        ++_allelesb;
                        jump next_allele;
                    }
                    if(llList2String(alleles, itrb) == "B") {
                        ++_allelesB;
                        jump next_allele;
                    }
@next_allele;
                } while(--itrb>=0);
                jump next_msg;
            }
@next_msg;
        } while(--itra>=0);
    }
    listen(integer channel, string name, key id, string message)
    {
        list opt = llParseString2List(message, [",", ":"], []);
        if(llList2String(opt, 0) == "BLK_DEATH" || llList2String(opt, 0) == "BWN_DEATH") {
            integer itra = llGetListLength(opt)-1;
            do {
                if(llList2String(opt, itra) == "GENOME") {
                    list alleles_death = llParseString2List(llList2String(opt, itra+1), [], ["B", "b"]);
                    integer itrb=llGetListLength(alleles_death)-1;
                    do {
                        if(llList2String(alleles_death, itrb) == "b") {
                            --_allelesb;
                            jump next;
                        }
                        if(llList2String(alleles_death, itrb) == "B") {
                            --_allelesB;
                            jump next;
                        }
@next;
                    } while(--itrb>=0);
                }
            } while(--itra>=0);
            float p = (float)((float)_allelesB/ (float)(_allelesB + _allelesb));
            float q = (float)((float)_allelesb/(float)(_allelesB + _allelesb));
            list sortedp = dualQuicksort([ llPow(p, 2), p*q, p*q, llPow(q, 2) ], [ "BB", "Bb", "bB", "bb" ]);
 
            list alleles = llList2ListStrided(llDeleteSubList(sortedp, 0, 0), 0, llGetListLength(sortedp)-1, 2);
            list alleles_prob = llList2ListStrided(sortedp, 0, llGetListLength(sortedp)-1, 2);
            //llSay(0, llDumpList2String(alleles, "|"));
            //llSay(0, llDumpList2String(alleles_prob, "|"));  
            //llSay(0, "Probabilities add up to: " + (string)llListStatistics(LIST_STAT_SUM, alleles_prob));
            float rnd = llFrand(1);
            //llSay(0, "Random number is: " + (string)rnd);
            float cum = 0;
            string genotype = "";
            itra = llGetListLength(alleles)-1;
            do {
                cum += llList2Float(alleles_prob, itra);
                if(cum >= rnd) {
                    genotype = llList2String(alleles, itra);
                    jump draw;
                }
            } while(--itra>-1);
@draw;
            //llSay(0, "Cumultative draw value was: " + (string)cum);
            //llSay(0, "Drew: " + genotype);
            if(genotype == "bb") {
                llRezObject("[K] Brown Moth", circlePoint(), ZERO_VECTOR, ZERO_ROTATION, (integer)("5" + (string)(llList2Integer(_genotypeReference, llListFindList(_genotypeReference, (list)genotype)+1)) + "6" + (string)(llList2Integer(_landReference, llListFindList(_landReference, (list)_landType)+1)) + (string)_mouseID));
                llMessageLinked(LINK_SET, 0, "BWN_BIRTH:" + (string)_mouseID + ",GENOME:" + genotype + ",LAND:" + _landType + ",CURRENT_P:" + (string)p + ",CURRENT_Q:" + (string)q, "");
                ++_mouseID;
                llRegionSay(_comChannel+1, (string)_iPos);
                return;
            }
            llRezObject("[K] Black Moth", circlePoint(), ZERO_VECTOR, ZERO_ROTATION, (integer)("5" + (string)(llList2Integer(_genotypeReference, llListFindList(_genotypeReference, (list)genotype)+1)) + "6" + (string)(llList2Integer(_landReference, llListFindList(_landReference, (list)_landType)+1)) + (string)_mouseID));
            llMessageLinked(LINK_SET, 0, "BLK_BIRTH:" + (string)_mouseID + ",GENOME:" + genotype + ",LAND:" + _landType + ",CURRENT_P:" + (string)p + ",CURRENT_Q:" + (string)q, "");
            ++_mouseID;
            llRegionSay(_comChannel+1, (string)_iPos);
            return;
        }
    }
 
    changed(integer change) {
        if(change & CHANGED_OWNER) llResetScript();
 
    }  
    on_rez(integer num) {
        llResetScript();
    }
}