#define genetics_init
//Argument0 = path to grammar.txt
//@return true if grammar is loaded correctly, false otherwise
var file;
file = argument0;
if (file_exists(argument0))
    {
        var f, counter;
        counter = 0;
        f = file_text_open_read(file);
        while (!file_text_eof(f))
            {
                global._rule[counter] = file_text_read_string(f);
                file_text_readln(f);
                counter += 1;
            }
        global._glength = counter;
        file_text_close(f);
        for(i = 0; i < counter; i += 1)
            {
                var startindex, foundbegin, sub_rule;
                startindex = 0;
                foundbegin = false;
                sub_rule = 0;
                for(j = 0; j <= string_length(global._rule[i]); j += 1)
                    {
                        if (!foundbegin)
                            {
                                if (string_char_at(global._rule[i],j) == ">")
                                    {
                                        global._rname[i] = string_copy(global._rule[i],0,j);
                                    }
                                if (string_char_at(global._rule[i],j) == "=" && string_char_at(global._rule[i],j-1) == ":" && string_char_at(global._rule[i],j-2) == ":")
                                    {
                                        startindex = j+1;
                                        foundbegin = true;
                                    }
                            }
                        else
                            {
                                if (string_char_at(global._rule[i],j) == "|")
                                    {
                                        global._substitute[i,sub_rule] = string_copy(global._rule[i],startindex,j-startindex);
                                        startindex = j+1;
                                        sub_rule += 1;
                                    }
                                if (j == string_length(global._rule[i]))
                                    {
                                        global._substitute[i,sub_rule] = string_copy(global._rule[i],startindex,(string_length(global._rule[i])-startindex)+1);
                                        startindex = j+1;
                                        sub_rule += 1;
                                        break;
                                    }
                            }
                    }
                global._rlength[i] = sub_rule;
            }
        global._startsign = global._rname[0];
        return true;
    }
return false;

#define genetics_get_phenotype
//Argument0 = genotype
//Argument1 = codonsize
//Argument2 = maxwraps
//@return phenotype of given genotype
var geno, codon_s, counter, codon, j, startval, unknown, codon_counter, out;
geno = "";
codon_s = argument1;
counter = 0;
c_cnt = 0;
j = 0;
startval = 0;
while (j <= argument2)
    {
        geno += argument0;
        j += 1;
    }
for(k = 0; k < ceil(string_length(geno)/codon_s); k += 1)
    {
        codon[k] = "";
    }
for(i = 0; i < string_length(geno); i += 1)
    {
        codon[counter] += string_char_at(geno,i);
        if (string_length(codon[counter]) == codon_s)
            {
                counter += 1;
            }
    }
unknown = 1;
out = global._startsign;
codon_counter = 0;
while(unknown > 0 && codon_counter < counter)
    {
        for(l = 0; l < string_length(out); l += 1)
            {
                if (string_char_at(out,l) == "<")
                    {
                        for(m = l; m <= string_length(out); m += 1)
                            {
                                if (string_char_at(out,m) == ">" && codon_counter < counter)
                                    {
                                        var sub;
                                        sub = genetics_replace_variable(string_copy(out,l,m-l+1),genetics_to_decimal(codon[codon_counter],codon_s));
                                        out = string_copy(out,0,l-1)+sub+string_copy(out,m+1,string_length(out));
                                        unknown = genetics_unbound_number(out);
                                        codon_counter += 1;
                                        break;
                                    }
                            }
                    }
            }
    }
if (codon_counter >= counter || out == " ")
    {
        out = "";
    }
return out;

#define genetics_replace_variable
//Argument0 = rule name
//Argument1 = codon (dec)
//@return the substitute for rule argument0 index number codon
var r_nr, codon;
codon = real(argument1);
for(i = 0; i < global._glength; i += 1)
    {
        if (global._rname[i] == argument0)
            {
                r_nr = i;
                break;
            }
    }
codon = (codon mod global._rlength[r_nr])
return global._substitute[r_nr,codon];

#define genetics_unbound_number
//Argument0 = string
//@return Number of unbound variables in string
return (string_count("<",argument0));

#define genetics_create_individual
//Argument0 = codonsize
//Argument1 = min codons
//Argument2 = max codons
//@return random generated genotype
var amount, size, out;
amount = irandom_range(argument1,argument2);
size = argument0;
out = "";
repeat (amount*size)
    {
        out += string(irandom(1));
    }
return out;

#define genetics_create_population
//Argument0 = population size
//Argument1 = codonsize
//Argument2 = min codons
//Argument3 = max codons
//@return n random generated genotype in a list
var amount, size, out;
out = "";
repeat(argument0)
    {
        amount = irandom_range(argument2,argument3);
        size = argument1;
        repeat (amount*size)
            {
                out += string(irandom(1));
            }
        out += "|";
    }
return string_copy(out,0,string_length(out)-1);

#define genetics_mutation
//Argument0 = Genotype
//Argument1 = Probability
//@return the new genotype after mutation with probability argument1
if (random(1) <= argument1)
    {
        var geno, new, pos;
        geno = argument0;
        new = irandom(1);
        pos = irandom_range(0,string_length(geno)-1);
        return (string_copy(geno,0,pos)+string(new)+string_copy(geno,pos+2,string_length(geno)));
    }
else
    {
        return argument0;
    }

#define genetics_crossover_splice
//genetics_crossover_splice (cut & splice)
//Argument0 = Parent 1 genotype
//Argument1 = Parent 2 genotype
//Argument2 = Probability
//@return A list containing two new genotypes
if (random(1) <= argument2)
    {
        var geno1, geno2, pos1, pos2, extract1, extract2;
        geno1 = argument0;
        geno2 = argument1;
        pos1 = irandom_range(0,string_length(geno1));
        pos2 = irandom_range(0,string_length(geno2));
        extract1 = string_copy(geno1,pos1,string_length(geno1));
        extract2 = string_copy(geno2,pos2,string_length(geno2));
        return (string_copy(geno1,0,pos1-1)+extract2+"|"+string_copy(geno2,0,pos2-1)+extract1)+"|";
    }
else
    {
        return "";
    }

#define genetics_crossover_twopoint
//Argument0 = Parent 1 genotype
//Argument1 = Parent 2 genotype
//Argument2 = Number of chromosomes to be swapped (-1 for random) ( 0 <= k <= chromosome length)
//Argument3 = Probability
//@return A list containing two new genotypes
if (random(1) <= argument3 || argument2 > string_length(argument1))
    {
        var k, geno1, geno2, pos1, pos2, extract1, extract2;
        geno1 = argument0;
        geno2 = argument1;
        k = argument2;
        if (k < 0)
            {
                k = irandom(string_length(geno1));
            }
        pos1 = irandom_range(0,string_length(geno1)-k);
        pos2 = pos1+k;
        extract1 = string_copy(geno1,pos1,k);
        extract2 = string_copy(geno2,pos1,k);
        return (string_copy(geno1,0,pos1)+extract2+string_copy(geno1,pos2+1,string_length(geno1)) + "|" + string_copy(geno2,0,pos1)+extract1+string_copy(geno2,pos2+1,string_length(geno2)))+"|";
    }
else
    {
        return "";
    }

#define genetics_crossover_uniform_key
//Argument0 = Parent 1 genotype
//Argument1 = Parent 2 genotype
//Argument2 = Bitkey (empty string ("") for random generated)
//Argument3 = Probability
//@return A list containing two new genotypes
if (random(1) <= argument3 || string_length(argument2) != string_length(argument1))
    {
        var key, geno, out;
        geno[0] = argument0;
        geno[1] = argument1;
        out[0] = "";
        out[1] = "";
        key = argument2;
        if (key == "")
            {
                key = genetics_create_individual(1,string_length(argument0),string_length(argument0));
            }
        for(i = 0; i <= string_length(geno[0]); i += 1)
            {
                var P;
                P = real(string_char_at(key,i));
                out[0] += string_char_at(geno[P],i);
                out[1] += string_char_at(geno[!P],i);
            }
        return out[0]+"|"+out[1]+"|";
    }
else
    {
        return "";
    }

#define genetics_crossover_uniform_chance
//Argument0 = Parent 1 genotype
//Argument1 = Parent 2 genotype
//Argument2 = Swap probability
//Argument3 = Probability
//@return A list containing two new genotypes
if (random(1) <= argument3)
    {
        var c, geno, out;
        geno[0] = argument0;
        geno[1] = argument1;
        out[0] = "";
        out[1] = "";
        c = argument2;
        for(i = 0; i <= string_length(geno[0]); i += 1)
            {
                var P;
                P = (random(1) <= c);
                out[0] += string_char_at(geno[P],i);
                out[1] += string_char_at(geno[!P],i);
            }
        return out[0]+"|"+out[1]+"|";
    }
else
    {
        return "";
    }

#define genetics_crossover
//Argument0 = Parent 1 genotype
//Argument1 = Parent 2 genotype
//Argument2 = Probability
//@return A list containing two new genotypes
if (random(1) <= argument2)
    {
        var geno1, geno2, pos1, extract1, extract2;
        geno1 = argument0;
        geno2 = argument1;
        pos = irandom_range(0,string_length(geno1));
        extract1 = string_copy(geno1,pos,string_length(geno1));
        extract2 = string_copy(geno2,pos,string_length(geno2));
        return (string_copy(geno1,0,pos-1)+extract2+"|"+string_copy(geno2,0,pos-1)+extract1)+"|";
    }
else
    {
        return "";
    }

#define genetics_crossover_threeparent
//Argument0 = Parent 1 genotype
//Argument1 = Parent 2 genotype
//Argument2 = Parent 3 genotype
//Argument3 = Probability
//@return A genotype with three parent crossover derived from P1, P2 and P3
if (random(1) <= argument3)
    {
        var geno, out;
        geno[0] = argument0;
        geno[1] = argument1;
        geno[2] = argument2;
        out = "";
        for(i = 0; i <= string_length(argument0); i += 1)
            {
                if (string_char_at(geno[0],i) == string_char_at(geno[1],i))
                    {
                        out += string_char_at(geno[0],i);
                    }
                else
                    {
                        out += string_char_at(geno[2],i);
                    }
            }
        return out;
    }
else
    {
        return "";
    }

#define genetics_to_decimal
//Argument0 = Binary genotype
//Argument1 = Codonsize
//@return a string of binary genotype in decimals
//Editted from: http://www.gmlscripts.com/script/dec_to_bin
var geno, codon_s, output;
geno = argument0;
codon_s = argument1;
output = "";
for(i = 0; i < string_length(geno); i += codon_s)
    {
        var bin,dec,l,p;
        bin = string_copy(geno,i,codon_s);
        dec = 0;
        l = string_length(bin);
        for(p = 1; p <= l; p += 1) 
            {
                dec = dec << 1;
                if (string_char_at(bin,p)=="1") dec = dec | 1;
            }
        output += string(dec);
    }
return output;

#define genetics_list_element
//Argument0 = list
//Argument1 = element number (starting with 0)
//@return nth element of list
var _L, N, _E, start, i;
_L = argument0+"|";
N = argument1;
_E = 0;
start = 0;
for(i = 0; i <= string_length(_L); i += 1)
    {
        if (string_char_at(_L,i) == "|")
            {
                if (_E < N)
                    {
                        start = i+1;
                        _E += 1;
                    }
                else
                    {
                        return string_replace_all(string_copy(_L,start,i-start),"|","");
                    }
            }
    }
return _L;

#define genetics_list_length
//Argument0 = list
//@return the number of elements in the list
return string_count("|",argument0);

#define genetics_list_add
//Argument0 = list
//Argument1 = element to add
//@return the list with the new element added at the end
var l, e;
l = argument0;
e = string(argument1);
if (string_char_at(l,string_length(l)) == "|" || l == "")
    {
        return l + e + "|";
    }
else
    {
        return l + "|" + e + "|";
    }

#define genetics_list_max
//Argument0 = A genetic list containing numbers
//@return the index of the maximum value in the list as a real
var mx, i, len, mxi;
mx = real(genetics_list_element(argument0,0));
mxi = 0;
len = genetics_list_length(argument0);
for(i = 0; i < len; i += 1)
    {   
        if (real(genetics_list_element(argument0,i)) > mx)
            {
                mx = real(genetics_list_element(argument0,i));
                mxi = i;
            }
    }
return mxi;

#define genetics_get_borderlist
//Argument0 = fitnesslist
//@return the new borderlist
var popsize, newlist, i;
popsize = genetics_list_length(argument0);
newlist = "";
for(i = 0; i < popsize; i += 1)
    {
        if (i > 0)
            {
                newlist = genetics_list_add(newlist,real(genetics_list_element(newlist,i-1))+real(genetics_list_element(argument0,i)));
            }
        else
            {
                newlist = genetics_list_add(newlist,genetics_list_element(argument0,i));
            }
    }
return newlist;

#define genetics_get_random_agent
//Argument0 = List with genotypes
//Argument1 = List with borders (in which list element n is the border for agent n in argument0)
//@return a random agent from the list with phenotypes, based on the borderlist
var pL, bL, r, len, i;
pL = argument0;
bL = argument1;
len = genetics_list_length(bL);
r = random(real(genetics_list_element(bL,len-1)));
for(i = 0; i < len-1; i += 1)
    {
        if (r <= real(genetics_list_element(bL,i)))
            {
                return genetics_list_element(pL,i);
                break;
            }
    }
return genetics_list_element(pL,len-1);

#define genetics_evolve
//Argument0 = List with genotypes
//Argument1 = List with fitnesses (in which list element n is the fitness of agent n in argument0)
//Argument2 = Chance of crossover
//Argument3 = Crossover type (0 = splice, 1 = twopoint, 2 = uniform_key, 3 = uniform_chance, 4 = normal crossover, 5 = threeparent)
//Argument4 = Chance of mutation
//Argument5 = Chance of duplication
//@return a new population with evolved agents, according to evolution rules
var pL, fL, pCross, c_type, pMut, pDup, bL, p_size, newpop;
pL = argument0;
fL = argument1;
pCross = argument2;
c_type = argument3;
pMut = argument4;
pDup = argument5;
bL = genetics_get_borderlist(fL);
p_size = genetics_list_length(pL);
newpop = "";
newpop = genetics_list_add(newpop,genetics_list_element(pL,genetics_list_max(fL)));
while (genetics_list_length(newpop) < p_size)
    {
        var r;
        r = random(pCross + pMut + pDup);
        if (r <= pCross)
            {
                var P1, P2;
                P1 = genetics_get_random_agent(pL,bL);
                P2 = genetics_get_random_agent(pL,bL);
                switch (c_type)
                    {
                        case 0: var l;
                                l = genetics_crossover_splice(P1,P2,1);
                                newpop = genetics_list_add(newpop,genetics_list_element(l,0));
                                if (genetics_list_length(newpop) < p_size)
                                    {
                                        newpop = genetics_list_add(newpop,genetics_list_element(l,1));
                                    }
                                break;
                                
                        case 1: var l;
                                l = genetics_crossover_twopoint(P1,P2,-1,1);
                                newpop = genetics_list_add(newpop,genetics_list_element(l,0));
                                if (genetics_list_length(newpop) < p_size)
                                    {
                                        newpop = genetics_list_add(newpop,genetics_list_element(l,1));
                                    }
                                break;
                                
                        case 2: var l;
                                l = genetics_crossover_uniform_key(P1,P2,"",1);
                                newpop = genetics_list_add(newpop,genetics_list_element(l,0));
                                if (genetics_list_length(newpop) < p_size)
                                    {
                                        newpop = genetics_list_add(newpop,genetics_list_element(l,1));
                                    }
                                break;
                                
                        case 3: var l;
                                l = genetics_crossover_uniform_chance(P1,P2,0.2,1);
                                newpop = genetics_list_add(newpop,genetics_list_element(l,0));
                                if (genetics_list_length(newpop) < p_size)
                                    {
                                        newpop = genetics_list_add(newpop,genetics_list_element(l,1));
                                    }
                                break;
                                
                        case 5: var l, P3;
                                P3 = genetics_get_random_agent(pL,bL);
                                l = genetics_crossover_threeparent(P1,P2,P3,1);
                                newpop = genetics_list_add(newpop,l);
                                break;
                                
                        default: var l;
                                 l = genetics_crossover(P1,P2,1);
                                 newpop = genetics_list_add(newpop,genetics_list_element(l,0));
                                 if (genetics_list_length(newpop) < p_size)
                                     {
                                         newpop = genetics_list_add(newpop,genetics_list_element(l,1));
                                     }
                                 break;
                    }
            }
        else
            {
                if (r-pCross <= pMut)
                    {
                        newpop = genetics_list_add(newpop,genetics_mutation(genetics_get_random_agent(pL,bL),1));
                    }
                else
                    {
                        newpop = genetics_list_add(newpop,genetics_get_random_agent(pL,bL));
                    }
            }
    }
return newpop;

