Changeset 1227 for cpp/frams/genetics/f4


Ignore:
Timestamp:
04/27/23 04:04:06 (19 months ago)
Author:
Maciej Komosinski
Message:

Improvements in f4:

  • fixed a bug where newly created cells in a given development step were not counted as in-active-development (overlooked), and if they were the only in-active-development cells, the development of an organism would stop
  • added one extra development step (#ifdef EXTRA_STEP_CELL_DEVELOPMENT) so that cells that became not in-active-development ("halted" or yielding, usually due to waiting for neurons to develop in other cells) would get a chance to continue development (important when we don't want to ignore invalid neuron connections, #ifdef TREAT_BAD_CONNECTIONS_AS_INVALID_GENO)
  • ensured that all connections in a cell are processed (earlier they could be skipped if the development of the cell was "halted" and all cells became not in-active-development)
  • got rid of neuron connection syntax [sensor:weight], now all neuron classes are handled in a uniform way and their [connections] too; the only allowed syntax is [input_index:weight]
  • unified handling of all neuroclasses during parsing, conversion and mutation
  • more correct syntax coloring
  • got rid of general-purpose fields (i1, i2, f1, s1) in class f4_node - now separate fields serve their individual purpose
  • rewritten creating and modifying neuron connections - it is more deliberate to satisfy neuron input/output preferences
  • some invalid neuron connections make a genotype invalid (previously invalid neuron connections were ignored and the genotype was considered valid)
  • added (surprisingly missing) simple debug printout functions to see the f4_Node tree structure and the developing f4_Cells
  • more informative variable and constant names
  • improved performance
Location:
cpp/frams/genetics/f4
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • cpp/frams/genetics/f4/f4_conv.cpp

    r1212 r1227  
    9090        if (cells) delete cells;
    9191        cells = new f4_Cells(geno, 0);
    92         if (GENOPER_OK != cells->geterror())
    93         {
    94                 error = cells->geterror();
    95                 errorpos = cells->geterrorpos();
     92        if (GENOPER_OK != cells->getErrorCode())
     93        {
     94                error = cells->getErrorCode();
     95                errorpos = cells->getErrorPos();
    9696                //delete cells;
    9797                return error;
     
    9999
    100100        cells->simulate();
    101         if (GENOPER_OK != cells->geterror())
    102         {
    103                 error = cells->geterror();
    104                 errorpos = cells->geterrorpos();
     101        if (GENOPER_OK != cells->getErrorCode())
     102        {
     103                error = cells->getErrorCode();
     104                errorpos = cells->getErrorPos();
    105105                return error;
    106106        }
    107107
    108108        // reset recursive traverse flags
    109         for (i = 0; i < cells->nc; i++)
     109        for (i = 0; i < cells->cell_count; i++)
    110110                cells->C[i]->recProcessedFlag = 0;
    111111
     
    114114        // process every cell
    115115        int res;
    116         for (i = 0; i < cells->nc; i++)
     116        for (i = 0; i < cells->cell_count; i++)
    117117        {
    118118                res = buildModelRec(cells->C[i]);
    119119                if (res)
    120120                {
    121                         logMessage("f4_Model", "buildModelRec", 2, "Error in building Model");
     121                        logMessage("f4_Model", "buildFromF4", LOG_ERROR, "Error in building a Model");
    122122                        error = res;
    123123                        break;
     
    135135f4_Cell* f4_Model::getStick(f4_Cell *C)
    136136{
    137         if (T_STICK4 == C->type) return C;
     137        if (C->type == CELL_STICK) return C;
    138138        if (NULL != C->dadlink)
    139139                return getStick(C->dadlink);
    140140        // we have no more dadlinks, find any stick
    141         for (int i = 0; i < cells->nc; i++)
    142                 if (cells->C[i]->type == T_STICK4)
     141        for (int i = 0; i < cells->cell_count; i++)
     142                if (cells->C[i]->type == CELL_STICK)
    143143                        return cells->C[i];
    144144        // none!
    145         logMessage("f4_Model", "getStick", 2, "Not a single stick");
     145        logMessage("f4_Model", "getStick", LOG_ERROR, "Not a single stick");
    146146        return NULL;
    147147}
    148148
    149149
    150 /// updated by MacKo to follow new SDK standards (no more neuroitems)
    151150int f4_Model::buildModelRec(f4_Cell *C)
    152151{
     
    164163        // make sure parent is a stick
    165164        if (NULL != C->dadlink)
    166                 if (C->dadlink->type != T_STICK4)
    167                 {
    168                 C->dadlink = getStick(C->dadlink);
     165                if (C->dadlink->type != CELL_STICK)
     166                {
     167                        C->dadlink = getStick(C->dadlink);
    169168                }
    170169
     
    179178
    180179        range = C->genoRange;
    181         if (C->type == T_STICK4)
     180        if (C->type == CELL_STICK)
    182181        {
    183182                int jj_p1_refno;  // save for later
     
    190189                                /*1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation
    191190                                //C->firstend.x, C->firstend.y, C->firstend.z
    192                                 );
     191                        );
    193192                        partidx = addFromString(PartType, tmpLine, &range);
    194193                        if (partidx < 0) return -1;
     
    207206                        //C->lastend.x, C->lastend.y, C->lastend.z
    208207                        /*"vol=" 1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation
    209                         );
     208                );
    210209                partidx = addFromString(PartType, tmpLine, &range);
    211210                if (partidx < 0) return -2;
     
    227226                        //C->P.ruch,   // rotstif
    228227                        C->P.stamina
    229                         );
     228                );
    230229                partidx = addFromString(JointType, tmpLine, &range);
    231230                if (partidx < 0) return -13;
     
    234233        }
    235234
    236         if (C->type == T_NEURON4) ///<this case was updated by MacKo
     235        if (C->type == CELL_NEURON)
    237236        {
    238237                const char* nclass = C->neuclass->name.c_str();
     
    259258                        partno = C->dadlink->p2_refno;
    260259                        if ((partno < 0) || (partno >= getPartCount())) return -21;
     260
    261261                        if (strcmp(nclass, "N") == 0)
    262                         {
    263262                                sprintf(tmpLine, "p=%d,d=\"N:in=%g,fo=%g,si=%g\"", partno, C->inertia, C->force, C->sigmo);
    264                         }
    265263                        else
    266                         {
    267264                                sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass);
    268                         }
     265
    269266                        partidx = addFromString(NeuronType, tmpLine, &range);
    270267                        if (partidx < 0) return -22;
     
    275272                {
    276273                        jointno = C->dadlink->joint_refno;
    277                         sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass);
     274
     275                        if (strcmp(nclass, "@") == 0)
     276                                sprintf(tmpLine, "j=%d,d=\"@:p=%g\"", jointno, C->P.muscle_power);
     277                        else if (strcmp(nclass, "|") == 0)
     278                                sprintf(tmpLine, "j=%d,d=\"|:p=%g,r=%g\"", jointno, C->P.muscle_power, C->mz);
     279                        else
     280                                sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass);
     281
    278282                        partidx = addFromString(NeuronType, tmpLine, &range);
    279283                        if (partidx < 0) return -32;
     
    283287                int n_refno = C->neuro_refno;
    284288
    285                 if ((strcmp(nclass,"N") == 0) && C->ctrl)
    286                 {
    287                         if (1 == C->ctrl)
    288                                 sprintf(tmpLine, "j=%d,d=\"@:p=%g\"", C->dadlink->joint_refno, C->P.muscle_power);
    289                         else
    290                                 sprintf(tmpLine, "j=%d,d=\"|:p=%g,r=%g\"", C->dadlink->joint_refno, C->P.muscle_power, C->mz);
    291                         partidx = addFromString(NeuronType, tmpLine, &range);
    292                         if (partidx < 0) return -32;
    293                         sprintf(tmpLine, "%d,%d", partidx, n_refno);
    294                         if (addFromString(NeuronConnectionType, tmpLine, &range) < 0) return -33;
    295                         this->checkpoint();
    296                 }
    297 
    298                 for (j = 0; j < C->nolink; j++)
    299                 {
    300                         if (NULL != C->links[j]->from)
    301                                 buildModelRec(C->links[j]->from);
     289                for (j = 0; j < C->conns_count; j++)
     290                {
     291                        if (C->conns[j]->from != NULL)
     292                                buildModelRec(C->conns[j]->from);
    302293
    303294                        tmpLine[0] = 0;
    304                         if (C->links[j]->from == NULL)
     295                        if (C->conns[j]->from == NULL)
    305296                        {
    306                                 const char* nclass = C->links[j]->t.c_str();
    307                                 char* temp = (char*)C->links[j]->t.c_str();
    308                                 NeuroClass *sensortest = GenoOperators::parseNeuroClass(temp);
    309                                 //backward compatibility for G*TS
    310                                 if (C->links[j]->t == "*" || C->links[j]->t == "S" || C->links[j]->t == "T")
    311                                 {
    312                                         partno = C->dadlink->p2_refno;
    313                                         sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass);
    314                                 }
    315                                 else if (C->links[j]->t == "G")
    316                                 {
    317                                         jointno = C->dadlink->joint_refno;
    318                                         sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass);
    319                                 }                               
    320                                 else if (sensortest->getPreferredLocation() == 0)
    321                                 {
    322                                         sprintf(tmpLine, "d=\"%s\"",nclass);
    323                                 }
    324                                 else if (sensortest->getPreferredLocation() == 1)
    325                                 {
    326                                         partno = C->dadlink->p2_refno;
    327                                         sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass);
    328                                 }
    329                                 else
    330                                 {
    331                                         jointno = C->dadlink->joint_refno;
    332                                         sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass);
    333                                 }
    334 
     297                                logMessage("f4_Model", "buildModelRec", LOG_ERROR, "Old code for sensors as inputs embedded in [connection]: C->conns[j]->from == NULL");
    335298                        }
    336299                        int from = -1;
    337                         if (tmpLine[0]) //input from receptor
    338                         {
    339                                 from = addFromString(NeuronType, tmpLine, &range);
    340                                 if (from < 0) return -34;
    341                                 this->checkpoint();
    342                         } /*could be 'else'...*/
    343 
    344                         if (NULL != C->links[j]->from) // input from another neuron
    345                                 from = C->links[j]->from->neuro_refno;
     300                        if (C->conns[j]->from != NULL) // input from another neuron
     301                                from = C->conns[j]->from->neuro_refno;
    346302                        if (from >= 0)
    347303                        {
    348                                 sprintf(tmpLine, "%d,%d,%g", n_refno, from, C->links[j]->w);
     304                                sprintf(tmpLine, "%d,%d,%g", n_refno, from, C->conns[j]->weight);
    349305                                if (addFromString(NeuronConnectionType, tmpLine, &range) < 0) return -35;
    350306                                this->checkpoint();
  • cpp/frams/genetics/f4/f4_general.cpp

    r1213 r1227  
    1717#endif
    1818
     19
     20#define BREAK_WHEN_REP_COUNTER_NULL //see comments where it is used
     21#define EXTRA_STEP_CELL_DEVELOPMENT //see comments where it is used
     22#define TREAT_BAD_CONNECTIONS_AS_INVALID_GENO //see comments where it is used
     23
     24
    1925void rolling_dec(double *v)
    2026{
     
    2733}
    2834
    29 int scanrec(const char* s, unsigned int slen, char stopchar)
    30 {
    31         unsigned int i = 0;
     35int scanrec(const char* s, size_t slen, char stopchar)
     36{
     37        size_t i = 0;
    3238        //DB( printf("    scan('%s', '%c')\n", s, stopchar); )
    3339        while (1)
     
    3642                        return 1;
    3743                if (stopchar == s[i])  // bumped into stopchar
    38                         return i;
     44                        return int(i);
    3945                if (i < slen - 1) // s[i] is not the last char
    4046                {
     
    5864                i++;
    5965        }
    60         return i;
    61 }
    62 
    63 
    64 f4_Cell::f4_Cell(int nname,
    65         f4_Cell *ndad, int nangle, GeneProps newP)
    66 {
    67         name = nname;
    68         type = T_UNDIFF4;
     66        return int(i);
     67}
     68
     69
     70f4_Cell::f4_Cell(int nnr, f4_Cell *ndad, int nangle, GeneProps newP)
     71{
     72        nr = nnr;
     73        type = CELL_UNDIFF;
    6974        dadlink = ndad;
    7075        org = NULL;
    7176        genot = NULL;
    7277        gcur = NULL;
    73         active = 1;
     78        active = true;
    7479        repeat.clear();
    7580        //genoRange.clear(); -- implicit
     
    8388        zrot = 0;
    8489        //OM = Orient_1;
    85         ctrl = 0;
    8690        inertia = 0.8;
    8791        force = 0.04;
    8892        sigmo = 2;
    89         nolink = 0;
     93        conns_count = 0;
    9094
    9195        // adjust firstend and OM if there is a stick dad
     
    9397        {
    9498                // make sure it is a stick (and not a stick f4_Cell!)
    95                 if (T_STICK4 == ndad->type)
     99                if (ndad->type == CELL_STICK)
    96100                {
    97101                        //firstend = ndad->lastend;
     
    99103                        ndad->childcount++;
    100104                }
    101                 if (T_NEURON4 == ndad->type)
     105                if (ndad->type == CELL_NEURON)
    102106                {
    103107                        inertia = ndad->inertia;
     
    112116
    113117
    114 f4_Cell::f4_Cell(f4_Cells *nO, int nname, f4_node *ngeno, f4_node *ngcur, f4_Cell *ndad, int nangle, GeneProps newP)
    115 {
    116         name = nname;
    117         type = T_UNDIFF4;
     118f4_Cell::f4_Cell(f4_Cells *nO, int nnr, f4_Node *ngeno, f4_Node *ngcur, f4_Cell *ndad, int nangle, GeneProps newP)
     119{
     120        nr = nnr;
     121        type = CELL_UNDIFF;
    118122        dadlink = ndad;
    119123        org = nO;
    120124        genot = ngeno;
    121125        gcur = ngcur;
    122         active = 1;
     126        active = true;
    123127        repeat.clear();
    124128        //genoRange.clear(); -- implicit
     
    135139        zrot = 0;
    136140        //OM = Orient_1;
    137         ctrl = 0;
    138141        inertia = 0.8;
    139142        force = 0.04;
    140143        sigmo = 2;
    141         nolink = 0;
     144        conns_count = 0;
    142145
    143146        // adjust firstend and OM if there is a stick dad
     
    145148        {
    146149                // make sure it is a stick (and not a stick f4_Cell!)
    147                 if (T_STICK4 == ndad->type)
     150                if (ndad->type == CELL_STICK)
    148151                {
    149152                        //firstend = ndad->lastend;
     
    151154                        ndad->childcount++;
    152155                }
    153                 if (T_NEURON4 == ndad->type)
     156                if (ndad->type == CELL_NEURON)
    154157                {
    155158                        inertia = ndad->inertia;
     
    166169f4_Cell::~f4_Cell()
    167170{
    168         // remove links
    169         if (nolink)
     171        // remove connections
     172        if (conns_count)
    170173        {
    171174                int i;
    172                 for (i = nolink - 1; i >= 0; i--)
    173                         delete links[i];
    174                 nolink = 0;
     175                for (i = conns_count - 1; i >= 0; i--)
     176                        delete conns[i];
     177                conns_count = 0;
    175178        }
    176179}
     
    178181
    179182/* return codes:
    180         >1 error at pos
    181         0  halt development for a cycle
    182         -1  development finished OK
     183        1 error at pos
     184        0  halt development (yield) for a cycle
    183185        */
    184 int f4_Cell::onestep()
    185 {
    186         if (gcur == NULL)
    187         {
    188                 active = 0;
    189                 return 0;  // stop
    190         }
    191         while (NULL != gcur)
     186int f4_Cell::oneStep()
     187{
     188        while (gcur != NULL)
    192189        {
    193190                //DB( printf("  %d (%d) executing '%c' %d\n", name, type, gcur->name, gcur->pos); )
     
    195192                // the current genotype code is processed
    196193                //genoRange.add(gcur->pos,gcur->pos+gcur->name.length()-1);
    197                 bool neuclasshandler = false; // if set to true, then there is a set of characters that can be assigned to a neuron class type
    198                 // old semantics, one-character
    199                 if (gcur->name.length() == 1)
     194                bool neuclasshandler = false; // if set to true, then a separate neuron handler below will identify the neuroclass and assign the cell to the neuron type
     195
     196                // To detect what genes are valid neuroclass names, but do NOT have is_neuroclass==true
     197                // (just as a curiosity to ensure we properly distinguish between, for example, the "G" neuron and "G" modifier):
     198                //char *TMP = (char*)gcur->name.c_str();
     199                //if (gcur->is_neuroclass==false && GenoOperators::parseNeuroClass(TMP,ModelEnum::SHAPETYPE_BALL_AND_STICK))
     200                //      printf("Could be a valid neuroclass, but is_neuroclass==false: %s\n", gcur->name.c_str());
     201
     202                if (gcur->name.length() == 1 && gcur->neuclass == NULL) //one-character genes and not neuroclass names
    200203                {
    201204                        genoRange.add(gcur->pos, gcur->pos);
     
    209212
    210213                                // error: sticks cannot divide
    211                                 if (T_STICK4 == type)
     214                                if (type == CELL_STICK)
    212215                                {
    213216                                        // cannot fix
     
    217220
    218221                                // undiff divides
    219                                 if (T_UNDIFF4 == type)
     222                                if (type == CELL_UNDIFF)
    220223                                {
    221224                                        // commacount is set only when daughter turns into X
     
    224227                                        GeneProps newP = P;
    225228                                        newP.propagateAlong(false);
    226                                         f4_Cell *tmp = new f4_Cell(org, org->nc, genot, gcur->child2, this, commacount, newP);
     229                                        f4_Cell *tmp = new f4_Cell(org, org->cell_count, genot, gcur->child2, this, commacount, newP);
    227230                                        tmp->repeat = repeat;
    228231                                        repeat.clear();
    229232                                        org->addCell(tmp);
    230233                                }
    231                                 // a neuron divides: create a new, duplicate links
    232                                 if (T_NEURON4 == type) {
     234                                // a neuron divides: create a new, duplicate connections
     235                                if (type == CELL_NEURON)
     236                                {
    233237                                        // daughter cell
    234                                         f4_Cell *tmp = new f4_Cell(org, org->nc, genot, gcur->child2,
     238                                        f4_Cell *tmp = new f4_Cell(org, org->cell_count, genot, gcur->child2,
    235239                                                // has the same dadlink
    236240                                                this->dadlink, commacount, P);
     
    238242                                        repeat.clear();
    239243                                        // it is a neuron from start
    240                                         tmp->type = T_NEURON4;
     244                                        tmp->type = CELL_NEURON;
    241245                                        // it has the same type as the parent neuron
    242246                                        tmp->neuclass = neuclass;
    243                                         // duplicate links
    244                                         f4_CellLink *ll;
    245                                         for (int i = 0; i < nolink; i++)
     247                                        // duplicate connections
     248                                        f4_CellConn *conn;
     249                                        for (int i = 0; i < conns_count; i++)
    246250                                        {
    247                                                 ll = links[i];
    248                                                 tmp->addlink(ll->from, ll->w, ll->t);
     251                                                conn = conns[i];
     252                                                tmp->addConnection(conn->from, conn->weight);
    249253                                        }
    250254                                        org->addCell(tmp);
     
    258262                        {
    259263                                // finish
    260                                 // see if there is a repet count
     264                                // see if there is a repeat count
    261265                                if (repeat.top > 0)
    262266                                { // there is a repeat counter
     
    280284                                        {
    281285                                                repeat.pop();
     286                                                // MacKo 2023-04: originally, there was no "break" nor "return" here (hence [[fallthrough]]; needed below for modern compilers) - not sure if this was intentional or overlooking.
     287                                                // This case can be tested with "#0" in the genotype. Anyway, there seems to be no difference in outcomes with and without "break".
     288                                                // However, falling through [[fallthrough]] below for count==0 causes performing repeat.push(repeat_ptr(gcur, 0)) while the very reason
     289                                                // we are here is that repeat count==0 (one of the conditions for isNull()), so I opted to add "break", but marked this tentative decision using #define.
     290                                                // The ultimate informed decision would require understanding all the logic and testing all the edge cases.
     291#ifdef BREAK_WHEN_REP_COUNTER_NULL
     292                                                break;
     293#endif
    282294                                        }
    283295                                }
     
    285297                                {
    286298                                        // error: still undiff
    287                                         if (T_UNDIFF4 == type)
     299                                        if (type == CELL_UNDIFF)
    288300                                        {
    289301                                                // fix it: insert an 'X'
    290                                                 f4_node *insertnode = new f4_node("X", NULL, gcur->pos);
     302                                                f4_Node *insertnode = new f4_Node("X", NULL, gcur->pos);
    291303                                                if (org->setRepairInsert(gcur->pos, gcur, insertnode)) // not in repair mode, release
    292304                                                        delete insertnode;
     
    294306                                        }
    295307                                        repeat.clear();
    296                                         active = 0;  // stop
     308                                        active = false;  // stop
    297309                                        // eat up rest
     310                                        int remaining_nodes = gcur->count() - 1;
     311                                        if (remaining_nodes > 0)
     312                                                logPrintf("f4_Cell", "oneStep", LOG_WARN, "Ignoring junk genetic code: %d node(s) at position %d", remaining_nodes, gcur->child->pos); //let's see an example of such a genotype...
    298313                                        gcur = NULL;
    299314                                        return 0;
    300315                                }
    301316                        }
     317#ifndef BREAK_WHEN_REP_COUNTER_NULL
    302318                        [[fallthrough]];
     319#endif
    303320                        case '#':
    304321                        {
     
    311328                                        return 1;  // stop
    312329                                }
    313                                 repeat.push(repeat_ptr(gcur, gcur->i1));
     330                                repeat.push(repeat_ptr(gcur, gcur->reps));
    314331                                gcur = gcur->child;
    315332                                break;
     
    324341                        {
    325342                                // error: if neuron
    326                                 if (T_NEURON4 == type)
     343                                if (type == CELL_NEURON)
    327344                                {
    328345                                        // fix: delete it
     
    354371                        {
    355372                                // error: if neuron
    356                                 if (T_NEURON4 == type)
    357                                 {
     373                                if (type == CELL_NEURON) //some neurons have the same single-letter names as modifiers (for example G,S,D), but they are supposed to have is_neuroclass==true so they should indeed not be handled here
     374                                {//however, what we see here is actually modifiers such as IdqEbWL (so not valid neuroclasses) that occurred within an already differentiated cell type==CELL_NEURON.
     375                                        //printf("Handled as a modifier, but type==CELL_NEURON: '%c'\n", name);
    358376                                        // fix: delete it
    359377                                        org->setRepairRemove(gcur->pos, gcur);
     
    368386                                // turn undiff. cell into a stick
    369387                                // error: already differentiated
    370                                 if (T_UNDIFF4 != type)
     388                                if (type != CELL_UNDIFF)
    371389                                {
    372390                                        // fix: delete this node
     
    374392                                        return 1;  // stop
    375393                                }
    376                                 type = T_STICK4;
     394                                type = CELL_STICK;
    377395                                // fix dad commacount and own anglepos
    378396                                if (NULL != dadlink)
     
    381399                                        anglepos = dadlink->commacount;
    382400                                }
    383                                 // change of type halts developments, see comment at 'N'
     401                                // change of type halts developments, see comment at 'neuclasshandler' below
    384402                                gcur = gcur->child;
    385403                                return 0;
    386404                        }
    387                         case 'N':
    388                         {
    389                                 // turn undiff. cell into a neuron
    390                                 // error: already differentiated
    391                                 if (T_UNDIFF4 != type)
    392                                 {
    393                                         // fix: delete this node
    394                                         org->setRepairRemove(gcur->pos, gcur);
    395                                         return 1;  // stop
    396                                 }
    397                                 // error: if no previous
    398                                 if (NULL == dadlink)
     405                        case '[':
     406                        {
     407                                // connection to neuron
     408                                // error: not a neuron
     409                                if (type != CELL_NEURON)
    399410                                {
    400411                                        // fix: delete it
     
    402413                                        return 1;  // stop
    403414                                }
    404                                 string temp1 = "N";
    405                                 char *temp = (char*)temp1.c_str();
    406                                 neuclass = GenoOperators::parseNeuroClass(temp);
    407                                 type = T_NEURON4;
    408                                 // change of type also halts development, to give other
    409                                 // cells a chance for adjustment.  Namely, it is important
    410                                 // to wait for other cells to turn N before adding links
     415                                // input [%d:%g]
     416                                int relfrom = gcur->conn_from;
     417                                double weight = gcur->conn_weight;
     418                                f4_Cell *neu_from = NULL;
     419
     420                                // input from other neuron
     421                                // find neuron at relative i
     422                                // find own index
     423                                int this_index = 0, neu_counter = 0;
     424                                for (int i = 0; i < org->cell_count; i++)
     425                                {
     426                                        if (org->C[i]->type == CELL_NEURON) neu_counter++;
     427                                        if (org->C[i] == this) { this_index = neu_counter - 1; break; }
     428                                }
     429                                // find index of incoming
     430                                int from_index = this_index + relfrom;
     431
     432                                if (from_index < 0) goto wait_conn;
     433                                if (from_index >= org->cell_count) goto wait_conn;
     434
     435                                // find that neuron
     436                                neu_counter = 0;
     437                                int from;
     438                                for (from = 0; from < org->cell_count; from++)
     439                                {
     440                                        if (org->C[from]->type == CELL_NEURON) neu_counter++;
     441                                        if (from_index == (neu_counter - 1)) break;
     442                                }
     443                                if (from >= org->cell_count) goto wait_conn;
     444                                neu_from = org->C[from];
     445
     446                                // add connection
     447                                // error: could not add connection (too many?)
     448                                if (addConnection(neu_from, weight))
     449                                {
     450                                        // cannot fix
     451                                        org->setError(gcur->pos);
     452                                        return 1;  // stop
     453                                }
    411454                                gcur = gcur->child;
    412                                 return 0;
    413                         }
    414                         case '@':
    415                         case '|':
    416                         {
    417                                 // neuron rotating / bending
    418                                 int j = 1;
    419                                 if ('@' == name) j = 1; // rot
     455                                break;
     456                        }
     457                wait_conn:
     458                        {
     459                                // wait for other neurons to develop
     460                                // if there are others still active
     461
     462                                int active_count = 0;
     463                                for (int i = 0; i < org->cell_count; i++)
     464                                        if (org->C[i]->active) active_count++;
     465                                active = false; // next time when we reach wait_conn, we will not count ourselves as active (if we were the last active cell, we got a chance to continue development for one development step only)
     466                                if (active_count > 0)
     467                                        return 0;  // there is at least one active (including ourselves), halt, try again
     468
     469#ifdef TREAT_BAD_CONNECTIONS_AS_INVALID_GENO // MacKo 2023-04: there were so many invalid connections accumulating in the genotype (and stopping processing of the chain of gcur->child) that it looks like treating them as errors is better... in 2000's, Framsticks neurons were flexible when it comes to inputs and outputs (for example, when asked, muscles would provide an output too, and neurons that ignored inputs would still accept them when connected) so f4 could create connections pretty randomly, but after 2000's we attempt to respect neurons' getPreferredInputs() and getPreferredOutput() so the network of connections has more constraints.
     470                                if (gcur->parent->name == "#")
     471                                {
     472                                        // MacKo 2023-04: Unfortunately the logic of multiplicating connections is not ideal...
     473                                        //TREAT_BAD_CONNECTIONS_AS_INVALID_GENO without this "#" exception would break /*4*/<X>N:N#5<[1:1]>
     474                                        // because every neuron wants to get an input from the neuron that will be created next
     475                                        // and all is fine until the last created neuron, which wants to get an input from another one which will not be created
     476                                        // (3 gets from 4, 4 gets from 5, 5 wants to get from 6 (relative connection offset for each of them is 1),
     477                                        // but 6 will not get created and if we want to TREAT_BAD_CONNECTIONS_AS_INVALID_GENO, we produce an error...
     478                                        // We would like to have this multiplication working, but OTAH we don't want to accept bad connections because then they tend to multiply as junk genes and bloat the genotype also causing more and more neutral mutations...
     479                                        //so this condition and checking for "#" is a simple way to be kind to some, but not all, bad connections, and not raise errors. Perhaps too kind and we open the door for too many cases with invalid connections.
     480                                        //Maybe it would be better to perform this check before addConnection(), seeing that for example we process the last iteration of the repetition counter? But how would we know that the (needed here) input neuron will not be developed later by other dividing cells...
     481                                        active = true; //not sure if needed, but let this cell have the chance to continue for as long as many children in the tree are left
     482                                        gcur = gcur->child;
     483                                        return 0;
     484                                }
    420485                                else
    421                                         if ('|' == name) j = 2; // bend
    422 
    423                                 // if undiff, then this is a new muscle. Thanks to f4_processrec @ and | case we can skip repairing
    424                                 if (T_UNDIFF4 == type)
    425                                 {
    426                                         neuclasshandler = true;
    427                                         break;
    428                                 }
    429 
    430                                 // error: not a neuron (stick)
    431                                 if (T_NEURON4 != type)
     486                                {
     487                                        //org->setError(gcur->pos); //in case setRepairRemove() would not always produce reasonable results
     488                                        org->setRepairRemove(gcur->pos, gcur); //produces unexpected results? or NOT? TODO verify, some genotypes earlier produced strange outcomes of this repair (produced a valid genotype, but some neurons were multiplied/copied after repair - maybe because when a branch of '<' (or something else) is missing, the other branch is copied?)
     489                                        return 1;  // stop
     490                                }
     491#else
     492                                // no more actives, cannot add connection, ignore, but treat not as an error - before 2023-04
     493                                gcur = gcur->child;
     494#endif
     495                        }
     496                        break;
     497                        case ':':
     498                        {
     499                                // neuron parameter
     500                                // error: not a neuron
     501                                if (type != CELL_NEURON)
    432502                                {
    433503                                        // fix: delete it
     
    435505                                        return 1;  // stop
    436506                                }
    437                                 // error: already has control
    438                                 if (ctrl != 0)
    439                                 {
    440                                         // fix: delete it
    441                                         org->setRepairRemove(gcur->pos, gcur);
    442                                         return 1;  // stop
    443                                 }
    444                                 // make neuron ctrl = 1 or 2
    445                                 ctrl = j;
    446                                 gcur = gcur->child;
    447                                 break;
    448                         }
    449                         case '[':
    450                         {
    451                                 // link to neuron
    452                                 // error: not a neuron
    453                                 if (T_NEURON4 != type)
    454                                 {
    455                                         // fix: delete it
    456                                         org->setRepairRemove(gcur->pos, gcur);
    457                                         return 1;  // stop
    458                                 }
    459                                 // input (sensor or %d)
    460                                 int t = gcur->i1;
    461                                 int relfrom = gcur->l1;
    462                                 float w = gcur->f1;
    463                                 f4_Cell *tneu = NULL;
    464                                 if (t < 0) // wrong sensor
    465                                 {
    466                                         string buf = "wrong sensor in link '";
    467                                         buf.append(gcur->s1);
    468                                         buf.append("'");
    469                                         logMessage("f4_Cell", "onestep", LOG_ERROR, buf.c_str());
    470                                         org->setRepairRemove(gcur->pos, gcur);
    471                                         return 1;
    472                                 }
    473                                 else if (t > 0) // sensors
    474                                 {
    475                                         char *temp = (char*)gcur->s1.c_str();
    476                                         NeuroClass *sensortest = GenoOperators::parseNeuroClass(temp);
    477                                         if (sensortest == NULL || sensortest->getPreferredInputs() != 0)
    478                                         {
    479                                                 // error: unknown code
    480                                                 string buf = "wrong sensor in link '";
    481                                                 buf.append(gcur->s1);
    482                                                 buf.append("'");
    483                                                 logMessage("f4_Cell", "onestep", LOG_ERROR, buf.c_str());
    484                                                 org->setRepairRemove(gcur->pos, gcur);
    485                                                 return 1;
    486                                         }
    487                                 }
    488                                 else {
    489                                         // input from other neuron
    490                                         // find neuron at relative i
    491                                         // find own index
    492                                         int j = 0, k = 0;
    493                                         for (int i = 0; i < org->nc; i++)
    494                                         {
    495                                                 if (org->C[i]->type == T_NEURON4) k++;
    496                                                 if (org->C[i] == this) { j = k - 1; break; }
    497                                         }
    498                                         // find index of incoming
    499                                         j = j + relfrom;
    500                                         if (j < 0) goto wait_link;
    501                                         if (j >= org->nc) goto wait_link;
    502                                         // find that neuron
    503                                         k = 0;
    504                                         int i;
    505                                         for (i = 0; i < org->nc; i++)
    506                                         {
    507                                                 if (org->C[i]->type == T_NEURON4) k++;
    508                                                 if (j == (k - 1)) break;
    509                                         }
    510                                         if (i >= org->nc) goto wait_link;
    511                                         tneu = org->C[i];
    512                                 }
    513                                 // add link
    514                                 // error: could not add link (too many?)
    515                                 if (addlink(tneu, w, gcur->s1))
    516                                 {
    517                                         // cannot fix
    518                                         org->setError(gcur->pos);
    519                                         return 1;  // stop
    520                                 }
    521                                 gcur = gcur->child;
    522                                 break;
    523                         }
    524                 wait_link:
    525                         {
    526                                 // wait for other neurons to develop
    527                                 // if there are others still active
    528                                 active = 0;
    529                                 int j = 0;
    530                                 for (int i = 0; i < org->nc; i++)
    531                                 {
    532                                         if (org->C[i]->active) j++;
    533                                 }
    534                                 if (j > 0)
    535                                         return 0;  // there is other active, halt, try again
    536                                 // no more actives, cannot add link, ignore, but treat not as an error
    537                                 gcur = gcur->child;
    538                         }
    539                         break;
    540                         case ':':
    541                         {
    542                                 // neuron parameter
    543                                 // error: not a neuron
    544                                 if (T_NEURON4 != type)
    545                                 {
    546                                         // fix: delete it
    547                                         org->setRepairRemove(gcur->pos, gcur);
    548                                         return 1;  // stop
    549                                 }
    550                                 int j = gcur->l1;
    551                                 switch ((char)gcur->i1)
     507                                switch (gcur->prop_symbol)
    552508                                {
    553509                                case '!':
    554                                         if (j)
     510                                        if (gcur->prop_increase)
    555511                                                force += (1.0 - force) * 0.2;
    556512                                        else
     
    558514                                        break;
    559515                                case '=':
    560                                         if (j)
     516                                        if (gcur->prop_increase)
    561517                                                inertia += (1.0 - inertia) * 0.2;
    562518                                        else
     
    564520                                        break;
    565521                                case '/':
    566                                         if (j)
     522                                        if (gcur->prop_increase)
    567523                                                sigmo *= 1.4;
    568524                                        else
     
    586542                        default:
    587543                        {
    588                                 // because there are one-character neuron classes, default move control to neuclasshandler
     544                                // because there are one-character neuron classes, default is move control to neuclasshandler
    589545                                neuclasshandler = true;
    590546                        }
     
    593549                else
    594550                {
    595                         // if many characters, then it needs to be parsed below
     551                        // if many characters or single character but is_neuroclass, then it will be handled below
    596552                        neuclasshandler = true;
    597553                }
     
    599555                if (neuclasshandler)
    600556                {
    601                         genoRange.add(gcur->pos, gcur->pos + gcur->name.length() + 2 - 1); // +2 for N:
    602                         if (T_UNDIFF4 != type)
     557                        genoRange.add(gcur->pos, gcur->pos + int(gcur->name.length()) + 2 - 1); // +2 for N:
     558                        if (type != CELL_UNDIFF)
    603559                        {
    604560                                // fix: delete this node
     
    607563                        }
    608564                        // error: if no previous
    609                         if (NULL == dadlink)
     565                        if (dadlink == NULL)
    610566                        {
    611567                                // fix: delete it
     
    613569                                return 1;  // stop
    614570                        }
    615                         // multiple characters are neuron types. Need to check if exists
     571                        // multiple characters are neuron types. Let's check if exists in the current configuration of Framsticks
    616572                        char *temp = (char*)gcur->name.c_str();
    617                         neuclass = GenoOperators::parseNeuroClass(temp);
     573                        neuclass = GenoOperators::parseNeuroClass(temp, ModelEnum::SHAPETYPE_BALL_AND_STICK);
    618574                        if (neuclass == NULL)
    619575                        {
    620576                                // error: unknown code
    621                                 string buf = "unknown code '";
    622                                 buf.append(gcur->name);
    623                                 buf.append("'");
    624                                 logMessage("f4_Cell", "onestep", 2, buf.c_str());
     577                                string buf = "Unknown code '" + gcur->name + "'";
     578                                logMessage("f4_Cell", "oneStep", LOG_ERROR, buf.c_str());
    625579                                org->setRepairRemove(gcur->pos, gcur);
    626580                                return 1;
    627581                        }
    628                         type = T_NEURON4; //they belong to neurons
     582                        type = CELL_NEURON;
     583                        // change of type also halts development, to give other
     584                        // cells a chance for adjustment.  Namely, it is important
     585                        // to wait for other cells to turn to neurons before adding connections
    629586                        gcur = gcur->child;
    630587                        return 0; //stop
    631588                }
    632589        }
    633         active = 0;  // done
     590        active = false;  // done
    634591        return 0;
    635592}
    636593
    637594
    638 int f4_Cell::addlink(f4_Cell *nfrom, double nw, string nt)
    639 {
    640         // if incoming neuron does not produce output, return error
    641         if (nfrom != NULL && nfrom->neuclass->getPreferredOutput() == 0) return -1;
    642         if (neuclass->getPreferredInputs() != -1 && nolink >= neuclass->getPreferredInputs()) return -1;
    643         if (nolink >= MAXINPUTS - 1) return -1; // full!
    644         links[nolink] = new f4_CellLink(nfrom, nw, nt);
    645         nolink++;
     595int f4_Cell::addConnection(f4_Cell *nfrom, double nweight)
     596{
     597        if (nfrom->neuclass->getPreferredOutput() == 0) return -1; // if incoming neuron does not produce output, return error
     598        if (neuclass->getPreferredInputs() != -1 && conns_count >= neuclass->getPreferredInputs()) return -1; //cannot add more inputs to this neuron
     599        if (conns_count >= F4_MAX_CELL_INPUTS - 1) return -1; // over hardcoded limit
     600        conns[conns_count] = new f4_CellConn(nfrom, nweight);
     601        conns_count++;
    646602        return 0;
    647603}
     
    661617
    662618        // make sure its parent is processed first
    663         if (NULL != dadlink)
     619        if (dadlink != NULL)
    664620                dadlink->adjustRec();
    665621
    666622        // count children
    667623        childcount = 0;
    668         for (i = 0; i < org->nc; i++)
     624        for (i = 0; i < org->cell_count; i++)
    669625        {
    670626                if (org->C[i]->dadlink == this)
    671                         if (org->C[i]->type == T_STICK4)
     627                        if (org->C[i]->type == CELL_STICK)
    672628                                childcount++;
    673629        }
    674630
    675         if (type == T_STICK4)
    676         {
    677                 if (NULL == dadlink)
     631        if (type == CELL_STICK)
     632        {
     633                if (dadlink == NULL)
    678634                {
    679635                        //firstend = Pt3D_0;
     
    721677
    722678
    723 f4_CellLink::f4_CellLink(f4_Cell *nfrom, double nw, string nt)
     679f4_CellConn::f4_CellConn(f4_Cell *nfrom, double nweight)
    724680{
    725681        from = nfrom;
    726         w = nw;
    727         t = nt;
    728 }
    729 
    730 
    731 
    732 f4_Cells::f4_Cells(f4_node *genome, int nrepair)
     682        weight = nweight;
     683}
     684
     685
     686
     687f4_Cells::f4_Cells(f4_Node *genome, int nrepair)
    733688{
    734689        // create ancestor cell
    735690        repair = nrepair;
    736         error = 0;
     691        errorcode = GENOPER_OK;
    737692        errorpos = -1;
    738693        repair_remove = NULL;
     
    742697        f4rootnode = NULL;
    743698        C[0] = new f4_Cell(this, 0, genome, genome, NULL, 0, GeneProps::standard_values);
    744         nc = 1;
     699        cell_count = 1;
    745700}
    746701
     
    750705        int res;
    751706        repair = nrepair;
    752         error = 0;
     707        errorcode = GENOPER_OK;
    753708        errorpos = -1;
    754709        repair_remove = NULL;
     
    759714
    760715        // transform geno from string to nodes
    761         f4rootnode = new f4_node();
    762         res = f4_processrec(genome.c_str(), (unsigned)0, f4rootnode);
     716        f4rootnode = new f4_Node();
     717        res = f4_processrec(genome.c_str(), 0, f4rootnode);
    763718        if ((res < 0) || (1 != f4rootnode->childCount()))
    764719        {
    765                 error = GENOPER_OPFAIL;
     720                errorcode = GENOPER_OPFAIL;
    766721                errorpos = -1;
    767722        }
     
    769724        // create ancestor cell
    770725        C[0] = new f4_Cell(this, 0, f4rootnode->child, f4rootnode->child, NULL, 0, GeneProps::standard_values);
    771         nc = 1;
     726        cell_count = 1;
    772727}
    773728
     
    776731        // release cells
    777732        int i;
    778         if (nc)
    779         {
    780                 for (i = nc - 1; i >= 0; i--)
     733        if (cell_count)
     734        {
     735                for (i = cell_count - 1; i >= 0; i--)
    781736                        delete C[i];
    782                 nc = 0;
     737                cell_count = 0;
    783738        }
    784739        if (f4rootnode)
     
    787742
    788743
    789 int f4_Cells::onestep()
    790 {
    791         int i, ret, oldnc, ret2;
    792         oldnc = nc;
    793         ret = 0;
    794         for (i = 0; i < oldnc; i++)
    795         {
    796                 ret2 = C[i]->onestep();
    797                 if (ret2 > 0)
     744bool f4_Cells::oneStep()
     745{
     746        int old_cell_count = cell_count; //cell_count may change in the loop as new cells may be appended because cells may be dividing
     747        for (int i = 0; i < old_cell_count; i++)
     748        {
     749                int cellstep_ret = C[i]->oneStep(); //keeps calling independently of C[i]->active
     750                if (cellstep_ret > 0)
    798751                {
    799752                        // error
    800                         C[i]->active = 0;  // stop
    801                         return 0;
    802                 }
    803                 // if still active
     753                        C[i]->active = false;  // stop
     754                        return false;
     755                }
     756        }
     757        for (int i = 0; i < cell_count; i++) //we check all cells, including newly created ones
    804758                if (C[i]->active)
    805                         ret = 1;
    806         }
    807         return ret;
     759                        return true; //at least one cell is still active. TODO maybe the development should stop NOT because of the "active" field (there is this strange "yielding" state too), but by observing the progress of all cells and continuing the development while (errorcode==0 AND (gcur of at least one cell changed OR cell_count changed)) ? Also get rid of return 1/return 0, just observe error.
     760        return false;
    808761}
    809762
     
    811764int f4_Cells::simulate()
    812765{
    813         int i;
    814         error = GENOPER_OK;
    815 
    816         for (i = 0; i < nc; i++)  C[i]->active = 1;
    817 
    818         // execute onestep() in a cycle
    819         while (onestep());
    820 
    821         if (GENOPER_OK != error) return error;
     766        constexpr bool print_debugging = false; //print the state of cells during development
     767        errorcode = GENOPER_OK;
     768
     769        for (int i = 0; i < cell_count; i++)  C[i]->active = true;
     770
     771        if (print_debugging) f4_Node::print_tree(C[0]->genot, 0);
     772        if (print_debugging) print_cells("Initialization");
     773
     774        // execute oneStep() in a cycle
     775        while (oneStep()) if (print_debugging) print_cells("Development step");
     776        if (print_debugging) print_cells("After last development step");
     777
     778#ifdef EXTRA_STEP_CELL_DEVELOPMENT
     779        if (errorcode == GENOPER_OK)
     780        {
     781                oneStep(); if (print_debugging) print_cells("After extra step"); //for these "halted" (yielding) cells (they have active==false) that wait for other cells to develop. Without this step, the last, recently halted one(s) may miss the "retrying" step if all active==true cells became active==false in the last step.
     782        }
     783#endif
     784
     785        if (errorcode != GENOPER_OK) return errorcode;
    822786
    823787        // fix neuron attachements
    824         for (i = 0; i < nc; i++)
    825         {
    826                 if (C[i]->type == T_NEURON4)
    827                 {
    828                         while (T_NEURON4 == C[i]->dadlink->type)
     788        for (int i = 0; i < cell_count; i++)
     789        {
     790                if (C[i]->type == CELL_NEURON)
     791                {
     792                        while (C[i]->dadlink->type == CELL_NEURON)
    829793                        {
    830794                                C[i]->dadlink = C[i]->dadlink->dadlink;
     
    835799        // there should be no undiff. cells
    836800        // make undifferentiated cells sticks
    837         for (i = 0; i < nc; i++)
    838         {
    839                 if (C[i]->type == T_UNDIFF4)
    840                 {
    841                         C[i]->type = T_STICK4;
    842                         //seterror();
     801        for (int i = 0; i < cell_count; i++)
     802        {
     803                if (C[i]->type == CELL_UNDIFF)
     804                {
     805                        C[i]->type = CELL_STICK;
     806                        //setError();
    843807                }
    844808        }
     
    846810        // recursive adjust
    847811        // reset recursive traverse flags
    848         for (i = 0; i < nc; i++)
     812        for (int i = 0; i < cell_count; i++)
    849813                C[i]->recProcessedFlag = 0;
    850814        // process every cell
    851         for (i = 0; i < nc; i++)
     815        for (int i = 0; i < cell_count; i++)
    852816                C[i]->adjustRec();
    853817
    854818        //DB( printf("Cell simulation done, %d cells. \n", nc); )
    855819
    856         return error;
     820        if (print_debugging) print_cells("Final");
     821
     822        return errorcode;
     823}
     824
     825
     826void f4_Cells::print_cells(const char* description)
     827{
     828        printf("------ %-55s ------ errorcode=%d, errorpos=%d\n", description, getErrorCode(), getErrorPos());
     829        for (int i = 0; i < cell_count; i++)
     830        {
     831                f4_Cell *c = C[i];
     832                string type;
     833                switch (c->type)
     834                {
     835                case CELL_UNDIFF: type = "UNDIFF"; break;
     836                case CELL_STICK:  type = "STICK"; break;
     837                case CELL_NEURON: type = string("NEURON:") + c->neuclass->name.c_str(); break;
     838                default: type = std::to_string(c->type);
     839                }
     840                const char *status = c->active ? "active" : (c->gcur != NULL ? "yielding" : ""); //yielding = not active but waiting for other cells
     841                printf("%2d(%-8s)  nr=%d \t type=%-15s \t genot=%s \t gcurrent=%s", i, status, c->nr, type.c_str(), c->genot->name.c_str(), c->gcur ? c->gcur->name.c_str() : "null");
     842                if (c->gcur && c->gcur->name == "[")
     843                        printf("\tfrom=%d  weight=%g", c->gcur->conn_from, c->gcur->conn_weight);
     844                printf("\n");
     845                for (int l = 0; l < c->conns_count; l++)
     846                        printf("\tconn:%d from=%d weight=%g\n", l, c->conns[l]->from->nr, c->conns[l]->weight);
     847        }
     848        printf("\n");
    857849}
    858850
     
    860852void f4_Cells::addCell(f4_Cell *newcell)
    861853{
    862         if (nc >= MAX4CELLS - 1)
     854        if (cell_count >= F4_MAX_CELLS - 1)
    863855        {
    864856                delete newcell;
    865857                return;
    866858        }
    867         C[nc] = newcell;
    868         nc++;
     859        C[cell_count] = newcell;
     860        cell_count++;
    869861}
    870862
     
    872864void f4_Cells::setError(int nerrpos)
    873865{
    874         error = GENOPER_OPFAIL;
     866        errorcode = GENOPER_OPFAIL;
    875867        errorpos = nerrpos;
    876868}
    877869
    878 void f4_Cells::setRepairRemove(int nerrpos, f4_node *rem)
     870void f4_Cells::setRepairRemove(int nerrpos, f4_Node *rem)
    879871{
    880872        if (!repair)
    881873        {
    882874                // not in repair mode, treat as repairable error
    883                 error = GENOPER_REPAIR;
     875                errorcode = GENOPER_REPAIR;
    884876                errorpos = nerrpos;
    885877        }
    886878        else
    887879        {
    888                 error = GENOPER_REPAIR;
     880                errorcode = GENOPER_REPAIR;
    889881                errorpos = nerrpos;
    890882                repair_remove = rem;
     
    892884}
    893885
    894 int f4_Cells::setRepairInsert(int nerrpos, f4_node *parent, f4_node *insert)
     886int f4_Cells::setRepairInsert(int nerrpos, f4_Node *parent, f4_Node *insert)
    895887{
    896888        if (!repair)
    897889        {
    898890                // not in repair mode, treat as repairable error
    899                 error = GENOPER_REPAIR;
     891                errorcode = GENOPER_REPAIR;
    900892                errorpos = nerrpos;
    901893                return -1;
     
    903895        else
    904896        {
    905                 error = GENOPER_REPAIR;
     897                errorcode = GENOPER_REPAIR;
    906898                errorpos = nerrpos;
    907899                repair_parent = parent;
     
    911903}
    912904
    913 void f4_Cells::repairGeno(f4_node *geno, int whichchild)
     905void f4_Cells::repairGeno(f4_Node *geno, int whichchild)
    914906{
    915907        // assemble repaired geno, if the case
    916908        if (!repair) return;
    917         if ((NULL == repair_remove) && (NULL == repair_insert)) return;
     909        if ((repair_remove == NULL) && (repair_insert == NULL)) return;
    918910        // traverse genotype tree, remove / insert node
    919         f4_node *g2;
    920         if (1 == whichchild) g2 = geno->child;
    921         else             g2 = geno->child2;
    922         if (NULL == g2)
     911        f4_Node *g2;
     912        if (whichchild == 1)
     913                g2 = geno->child;
     914        else
     915                g2 = geno->child2;
     916        if (g2 == NULL)
    923917                return;
    924918        if (g2 == repair_remove)
    925919        {
    926                 f4_node *oldgeno;
     920                f4_Node *oldgeno;
    927921                geno->removeChild(g2);
    928922                if (g2->child)
    929923                {
    930924                        // add g2->child as child to geno
    931                         if (1 == whichchild) geno->child = g2->child;
    932                         else             geno->child2 = g2->child;
     925                        if (whichchild == 1)
     926                                geno->child = g2->child;
     927                        else
     928                                geno->child2 = g2->child;
    933929                        g2->child->parent = geno;
    934930                }
     
    936932                oldgeno->child = NULL;
    937933                delete oldgeno;
    938                 if (NULL == geno->child) return;
     934                if (geno->child == NULL) return;
    939935                // check this new
    940936                repairGeno(geno, whichchild);
     
    973969        char buf[200];
    974970
    975         if (curc >= nc) return;
    976 
    977         if (T_STICK4 != C[curc]->type) return;
     971        if (curc >= cell_count) return;
     972
     973        if (C[curc]->type != CELL_STICK) return;
    978974
    979975        thisti = C[curc];
    980         if (NULL != thisti->dadlink)
     976        if (thisti->dadlink != NULL)
    981977                *tmpcel = *(thisti->dadlink);
    982978
     
    10181014
    10191015        // neurons attached to it
    1020         for (i = 0; i < nc; i++)
    1021         {
    1022                 if (C[i]->type == T_NEURON4)
     1016        for (i = 0; i < cell_count; i++)
     1017        {
     1018                if (C[i]->type == CELL_NEURON)
    10231019                {
    10241020                        if (C[i]->dadlink == thisti)
     
    10271023                                out += "[";
    10281024                                // ctrl
    1029                                 if (1 == thneu->ctrl) out += "@";
    1030                                 if (2 == thneu->ctrl) out += "|";
    1031                                 // links
    1032                                 for (j = 0; j < thneu->nolink; j++)
     1025                                //if (1 == thneu->ctrl) out += "@"; // old code; this can be easily generalized to any neuroclass if ever needed
     1026                                //if (2 == thneu->ctrl) out += "|";
     1027                                out += thneu->neuclass->name.c_str(); // not tested, but something like that
     1028                                // connections
     1029                                for (j = 0; j < thneu->conns_count; j++)
    10331030                                {
    10341031                                        if (j) out += ",";
    1035                                         if (NULL == thneu->links[j]->from)
    1036                                         {
    1037                                                 // sensors
    1038                                                 out += thneu->links[j]->t.c_str();
    1039                                         }
    1040                                         else
    1041                                         {
    1042                                                 sprintf(buf, "%d", thneu->links[j]->from->name - thneu->name);
    1043                                                 out += buf;
    1044                                         }
     1032                                        sprintf(buf, "%d", thneu->conns[j]->from->nr - thneu->nr);
     1033                                        out += buf;
    10451034                                        out += ":";
    10461035                                        // connection weight
    1047                                         sprintf(buf, "%g", thneu->links[j]->w);
     1036                                        sprintf(buf, "%g", thneu->conns[j]->weight);
    10481037                                        out += buf;
    10491038                                }
     
    10581047
    10591048        ccount = 1;
    1060         for (i = 0; i < nc; i++)
    1061         {
    1062                 if (C[i]->type == T_STICK4)
     1049        for (i = 0; i < cell_count; i++)
     1050        {
     1051                if (C[i]->type == CELL_STICK)
    10631052                {
    10641053                        if (C[i]->dadlink == thisti)
     
    10881077// to organize an f4 genotype in a tree structure
    10891078
    1090 f4_node::f4_node()
     1079f4_Node::f4_Node()
    10911080{
    10921081        name = "?";
     
    10951084        child2 = NULL;
    10961085        pos = -1;
    1097         l1 = 0;
    1098         i1 = 0;
    1099         f1 = 0.0f;
    1100 }
    1101 
    1102 f4_node::f4_node(string nname, f4_node *nparent, int npos)
     1086
     1087        reps = 0;
     1088        prop_symbol = '\0';
     1089        prop_increase = false;
     1090        conn_from = 0;
     1091        conn_weight = 0.0;
     1092        neuclass = NULL;
     1093}
     1094
     1095f4_Node::f4_Node(string nname, f4_Node *nparent, int npos)
    11031096{
    11041097        name = nname;
     
    11081101        pos = npos;
    11091102        if (parent) parent->addChild(this);
    1110         l1 = 0;
    1111         i1 = 0;
    1112         f1 = 0.0f;
    1113 }
    1114 
    1115 f4_node::f4_node(char nname, f4_node *nparent, int npos)
     1103
     1104        reps = 0;
     1105        prop_symbol = '\0';
     1106        prop_increase = false;
     1107        conn_from = 0;
     1108        conn_weight = 0.0;
     1109        neuclass = NULL;
     1110}
     1111
     1112f4_Node::f4_Node(char nname, f4_Node *nparent, int npos)
    11161113{
    11171114        name = nname;
     
    11211118        pos = npos;
    11221119        if (parent) parent->addChild(this);
    1123         l1 = 0;
    1124         i1 = 0;
    1125         f1 = 0.0f;
    1126 }
    1127 
    1128 f4_node::~f4_node()
    1129 {
    1130         // (destroy() copied here for efficiency)
    1131         // children are destroyed (recursively) through the destructor
    1132         if (NULL != child2)  delete child2;
    1133         if (NULL != child)   delete child;
    1134 }
    1135 
    1136 int f4_node::addChild(f4_node *nchi)
    1137 {
    1138         if (NULL == child)
     1120
     1121        reps = 0;
     1122        prop_symbol = '\0';
     1123        prop_increase = false;
     1124        conn_from = 0;
     1125        conn_weight = 0.0;
     1126        neuclass = NULL;
     1127}
     1128
     1129f4_Node::~f4_Node()
     1130{
     1131        destroy();
     1132}
     1133
     1134void f4_Node::print_tree(const f4_Node *root, int indent)
     1135{
     1136        for (int i = 0; i < indent; i++) printf(" ");
     1137        printf("%s (%d)", root->name.c_str(), root->count());
     1138        if (root->name == "[")
     1139                printf("     from=%-3d  weight=%g", root->conn_from, root->conn_weight);
     1140        printf("\n");
     1141        if (root->child) print_tree(root->child, indent + 1);
     1142        if (root->child2) print_tree(root->child2, indent + 1);
     1143}
     1144
     1145int f4_Node::addChild(f4_Node *nchi)
     1146{
     1147        if (child == NULL)
    11391148        {
    11401149                child = nchi;
    11411150                return 0;
    11421151        }
    1143         if (NULL == child2)
     1152        if (child2 == NULL)
    11441153        {
    11451154                child2 = nchi;
     
    11491158}
    11501159
    1151 int f4_node::removeChild(f4_node *nchi)
     1160int f4_Node::removeChild(f4_Node *nchi)
    11521161{
    11531162        if (nchi == child2)
     
    11641173}
    11651174
    1166 int f4_node::childCount()
    1167 {
    1168         if (NULL != child)
    1169         {
    1170                 if (NULL != child2) return 2;
     1175int f4_Node::childCount()
     1176{
     1177        if (child != NULL)
     1178        {
     1179                if (child2 != NULL) return 2;
    11711180                else return 1;
    11721181        }
    11731182        else
    11741183        {
    1175                 if (NULL != child2) return 1;
     1184                if (child2 != NULL) return 1;
    11761185                else return 0;
    11771186        }
    11781187}
    11791188
    1180 int f4_node::count()
     1189int f4_Node::count() const
    11811190{
    11821191        int c = 1;
    1183         if (NULL != child)  c += child->count();
    1184         if (NULL != child2) c += child2->count();
     1192        if (child != NULL)  c += child->count();
     1193        if (child2 != NULL) c += child2->count();
    11851194        return c;
    11861195}
    11871196
    1188 f4_node* f4_node::ordNode(int n)
     1197f4_Node* f4_Node::ordNode(int n)
    11891198{
    11901199        int n1;
    1191         if (0 == n) return this;
     1200        if (n == 0) return this;
    11921201        n--;
    1193         if (NULL != child)
     1202        if (child != NULL)
    11941203        {
    11951204                n1 = child->count();
     
    11971206                n -= n1;
    11981207        }
    1199         if (NULL != child2)
     1208        if (child2 != NULL)
    12001209        {
    12011210                n1 = child2->count();
     
    12061215}
    12071216
    1208 f4_node* f4_node::randomNode()
     1217f4_Node* f4_Node::randomNode()
    12091218{
    12101219        int n = count();
     
    12131222}
    12141223
    1215 f4_node* f4_node::randomNodeWithSize(int mn, int mx)
     1224f4_Node* f4_Node::randomNodeWithSize(int mn, int mx)
    12161225{
    12171226        // try random nodes, and accept if size in range
    12181227        // limit to maxlim tries
    12191228        int i, n, maxlim;
    1220         f4_node *nod = NULL;
     1229        f4_Node *nod = NULL;
    12211230        maxlim = count();
    12221231        for (i = 0; i < maxlim; i++)
     
    12301239}
    12311240
    1232 void f4_node::sprint(SString& out)
     1241void f4_Node::sprint(SString& out)
    12331242{
    12341243        char buf2[20];
     
    12371246        {
    12381247                out += "#";
    1239                 if (i1 != 1)
    1240                 {
    1241                         sprintf(buf2, "%d", i1);
     1248                sprintf(buf2, "%d", reps);
     1249                out += buf2;
     1250        }
     1251        else {
     1252                // special case: neuron connection
     1253                if (name == "[")
     1254                {
     1255                        out += "[";
     1256                        sprintf(buf2, "%d", conn_from);
    12421257                        out += buf2;
    1243                 }
    1244         }
    1245         else {
    1246                 // special case: neuron link
    1247                 if (name == "[")
    1248                 {
    1249                         out += "[";
    1250                         if (i1 > 0)
    1251                         {
    1252                                 // sensor input
    1253                                 out += s1.c_str();
    1254                         }
    1255                         else
    1256                         {
    1257                                 sprintf(buf2, "%d", l1);
    1258                                 out += buf2;
    1259                         }
    1260                         sprintf(buf2, ":%g]", f1);
     1258                        sprintf(buf2, ":%g]", conn_weight);
    12611259                        out += buf2;
    12621260                }
    12631261                else if (name == ":")
    12641262                {
    1265                         sprintf(buf2, ":%c%c:", l1 ? '+' : '-', (char)i1);
     1263                        sprintf(buf2, ":%c%c:", prop_increase ? '+' : '-', prop_symbol);
    12661264                        out += buf2;
    12671265                }
    1268                 else if (name == "@" || name == "|")
    1269                 {
    1270                         if (parent->name == "N")
    1271                         {
    1272                                 out += name.c_str();
    1273                         }
    1274                         else
    1275                         {
    1276                                 out += "N:";
    1277                                 out += name.c_str();
    1278                         }
     1266                else if (neuclass != NULL)
     1267                {
     1268                        out += "N:";
     1269                        out += neuclass->name.c_str();
    12791270                }
    12801271                else
    12811272                {
    1282                         char *temp = (char*)name.c_str();
    1283                         NeuroClass *nc = GenoOperators::parseNeuroClass(temp);
    1284                         if (nc != NULL)
    1285                         {
    1286                                 out += "N:";
    1287                         }
    12881273                        out += name.c_str();
    12891274                }
    12901275        }
    1291         if (NULL != child)     child->sprint(out);
     1276
     1277        if (child != NULL)
     1278                child->sprint(out);
    12921279        // if two children, make sure last char is a '>'
    1293         if (2 == childCount())
    1294                 if (0 == out[0]) out += ">"; else
    1295                         if ('>' != out[out.length() - 1]) out += ">";
    1296         if (NULL != child2)    child2->sprint(out);
     1280        if (childCount() == 2)
     1281                if (out[0] == 0) out += ">"; else
     1282                        if (out[out.length() - 1] != '>') out += ">";
     1283
     1284        if (child2 != NULL)
     1285                child2->sprint(out);
    12971286        // make sure last char is a '>'
    1298         if (0 == out[0]) out += ">"; else
    1299                 if ('>' != out[out.length() - 1]) out += ">";
    1300 }
    1301 
    1302 void f4_node::sprintAdj(char *& buf)
     1287        if (out[0] == 0) out += ">"; else
     1288                if (out[out.length() - 1] != '>') out += ">";
     1289}
     1290
     1291void f4_Node::sprintAdj(char *& buf)
    13031292{
    13041293        unsigned int len;
     
    13121301        len = out.length();
    13131302        if (len > 1)
    1314                 if ('>' == out[len - 1]) { (out.directWrite())[len - 1] = 0; out.endWrite(); };
     1303                if (out[len - 1] == '>') { (out.directWrite())[len - 1] = 0; out.endWrite(); };
    13151304        // copy back to string
    13161305        // if new is longer, reallocate buf
     
    13221311}
    13231312
    1324 f4_node* f4_node::duplicate()
    1325 {
    1326         f4_node *copy;
    1327         copy = new f4_node(*this);
     1313f4_Node* f4_Node::duplicate()
     1314{
     1315        f4_Node *copy;
     1316        copy = new f4_Node(*this);
    13281317        copy->parent = NULL;  // set later
    13291318        copy->child = NULL;
    13301319        copy->child2 = NULL;
    1331         if (NULL != child)
     1320        if (child != NULL)
    13321321        {
    13331322                copy->child = child->duplicate();
    13341323                copy->child->parent = copy;
    13351324        }
    1336         if (NULL != child2)
     1325        if (child2 != NULL)
    13371326        {
    13381327                copy->child2 = child2->duplicate();
     
    13431332
    13441333
    1345 void f4_node::destroy()
     1334void f4_Node::destroy()
    13461335{
    13471336        // children are destroyed (recursively) through the destructor
    1348         if (NULL != child2) delete child2;
    1349         if (NULL != child)  delete child;
     1337        if (child2 != NULL) delete child2;
     1338        if (child != NULL) delete child;
    13501339}
    13511340
    13521341// scan genotype string and build tree
    13531342// return >1 for error (errorpos)
    1354 int f4_processrec(const char* genot, unsigned pos0, f4_node *parent)
    1355 {
    1356         int i, j, res, t;
    1357         char tc1, tc2, tc3[2]; // tc3 is only to ensure that neuron parameter definition is completed
    1358         int relfrom;
    1359         double w;
     1343int f4_processrec(const char* genot, unsigned pos0, f4_Node *parent)
     1344{
     1345        int i, res;
    13601346        unsigned gpos, oldpos;
    1361         f4_node *node1, *par;
     1347        f4_Node *node1, *par;
    13621348        unsigned beginindex;
    1363         string neutype = "";
    13641349
    13651350        gpos = pos0;
     
    13681353        while (gpos < strlen(genot))
    13691354        {
    1370                 neutype = "";
    13711355                // first switch across cell dividers and old semantics
    13721356                switch (genot[gpos])
     
    13751359                {
    13761360                        // find out genotype start for child
    1377                         j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>');
    1378 
    1379                         node1 = new f4_node("<", par, gpos);
     1361                        int j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>');
     1362
     1363                        node1 = new f4_Node("<", par, gpos);
    13801364                        par = node1;
    13811365                        res = f4_processrec(genot, gpos + 1, par);
     
    13881372                        else // ran out
    13891373                        {
    1390                                 node1 = new f4_node(">", par, int(strlen(genot)) - 1);
     1374                                node1 = new f4_Node(">", par, int(strlen(genot)) - 1);
    13911375                                par = node1;
    13921376                        }
     
    13961380                case '>':
    13971381                {
    1398                         node1 = new f4_node(">", par, gpos);
     1382                        node1 = new f4_Node(">", par, gpos);
    13991383                        par = node1;
    1400                         gpos = strlen(genot);
     1384                        gpos = (unsigned int)strlen(genot);
    14011385                        return 0;  // OK
    14021386                }
     
    14091393                        else i = val.getInt();
    14101394                        // find out genotype start for continuation
    1411                         j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>');
     1395                        int j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>');
    14121396                        // skip number
    14131397                        oldpos = gpos;
    14141398                        gpos += end - (genot + gpos);
    14151399                        //gpos++;
    1416                         //while ((genot[gpos] >= '0') && (genot[gpos] <= '9')) gpos++;node1 = new f4_node("#", par, oldpos);
    1417                         node1 = new f4_node("#", par, oldpos);
    1418                         node1->i1 = i;
     1400                        //while ((genot[gpos] >= '0') && (genot[gpos] <= '9')) gpos++;node1 = new f4_Node("#", par, oldpos);
     1401                        node1 = new f4_Node("#", par, oldpos);
     1402                        node1->reps = i;
    14191403                        par = node1;
    14201404                        res = f4_processrec(genot, gpos, node1);
     
    14271411                        else // ran out
    14281412                        {
    1429                                 node1 = new f4_node(">", par, int(strlen(genot)) - 1);
     1413                                node1 = new f4_Node(">", par, int(strlen(genot)) - 1);
    14301414                        }
    14311415                        return 0;  // OK
     
    14401424                        break;
    14411425                }
    1442                 case 'l':  case 'L':
    1443                 case 'c':  case 'C':
    1444                 case 'q':  case 'Q':
    1445                 case 'r':  case 'R':
    1446                 case 'X':  case ',':
    1447                 case 'a':  case 'A':
    1448                 case 's':  case 'S':
    1449                 case 'm':  case 'M':
    1450                 case 'i':  case 'I':
    1451                 case 'f':  case 'F':
    1452                 case 'w':  case 'W':
    1453                 case 'e':  case 'E':
    1454                 {
    1455                         node1 = new f4_node(genot[gpos], par, gpos);
     1426                case 'N':
     1427                {
     1428                        int forgenorange = gpos;
     1429                        if (genot[gpos + 1] != ':')
     1430                                return gpos + 1; //error
     1431                        gpos += 2; //skipping "N:"
     1432                        beginindex = gpos;
     1433                        char* end = (char*)genot + beginindex;
     1434                        NeuroClass *neuclass = GenoOperators::parseNeuroClass(end, ModelEnum::SHAPETYPE_BALL_AND_STICK);
     1435                        if (neuclass == NULL)
     1436                                return gpos + 1; //error
     1437                        gpos += end - genot - beginindex;
     1438                        string neutype = string(genot + beginindex, genot + gpos);
     1439                        node1 = new f4_Node(neutype, par, forgenorange);
     1440                        node1->neuclass = neuclass;
     1441                        par = node1;
     1442                        // if it continues with a colon that determines a neuron parameter (e.g. N:N:+=: ), then let the switch case for colon handle this
     1443                        break;
     1444                }
     1445                case ':':
     1446                {
     1447                        // neuron parameter  +! -! += -= +/ or -/
     1448                        // in the future this could be generalized to all neuron properties, for example N:|:power:0.6:range:1.4, or can even use '=' or ',' instead of ':' if no ambiguity
     1449                        char prop_dir, prop_symbol, prop_end[2]; // prop_end is only to ensure that neuron parameter definition is completed
     1450                        if (sscanf(genot + gpos, ":%c%c%1[:]", &prop_dir, &prop_symbol, &prop_end) != 3)
     1451                                // error: incorrect format
     1452                                return gpos + 1 + 1;
     1453                        if (prop_dir != '-' && prop_dir != '+')
     1454                                return gpos + 1 + 1; //error
     1455                        switch (prop_symbol)
     1456                        {
     1457                        case '!':  case '=':  case '/':  break;
     1458                        default:
     1459                                return gpos + 1 + 1; //error
     1460                        }
     1461                        node1 = new f4_Node(":", par, gpos);
     1462                        node1->prop_symbol = prop_symbol;
     1463                        node1->prop_increase = prop_dir == '+' ? true : false; // + or -
     1464                        par = node1;
     1465                        int chars = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), ':');
     1466                        gpos += chars + 2;
     1467                        break;
     1468                }
     1469                case '[':
     1470                {
     1471                        double weight = 0;
     1472                        int relfrom;
     1473                        const char *end = parseConnection(genot + gpos, relfrom, weight);
     1474                        if (end == NULL)
     1475                                return gpos + 1; //error
     1476
     1477                        node1 = new f4_Node("[", par, gpos);
     1478                        node1->conn_from = relfrom;
     1479                        node1->conn_weight = weight;
     1480                        par = node1;
     1481                        int j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), ']');
     1482                        gpos += j + 2;
     1483                        break;
     1484                }
     1485                default: // 'X' and ',' and all modifiers and also invalid symbols - add a node, for invalid symbols build will give the error or repair
     1486                {
     1487                        //printf("any regular character '%c'\n", genot[gpos]);
     1488                        node1 = new f4_Node(genot[gpos], par, gpos);
    14561489                        par = node1;
    14571490                        gpos++;
    14581491                        break;
    14591492                }
    1460                 case '@':  case '|':
    1461                 {
    1462                         // in order to prevent the presence of "free muscles", we need to ensure that a muscle is written as N@/N| or N:@/N:|
    1463                         if (par != NULL && par->name == "N")
    1464                         {
    1465                                 node1 = new f4_node(genot[gpos], par, gpos);
    1466                                 par = node1;
    1467                                 gpos++;
    1468                         }
    1469                         else
    1470                         {
    1471                                 return gpos + 1;
    1472                         }
    1473                         break;
    1474                 }
    1475 
    1476                 case 'N':
    1477                 {
    1478                         // if there is no colon after N, then there is no class definition
    1479                         if (gpos + 1 >= strlen(genot) || genot[gpos + 1] != ':')
    1480                         {
    1481                                 node1 = new f4_node(genot[gpos], par, gpos);
    1482                                 par = node1;
    1483                                 gpos++;
    1484                                 break;
    1485                         }
    1486                         // if there is a colon determining neuron parameter, then let the switch case colon handle this
    1487                         else if (sscanf(genot + gpos + 1, ":%c%c%1[:]", &tc1, &tc2, &tc3) == 3)
    1488                         {
    1489                                 node1 = new f4_node(genot[gpos], par, gpos);
    1490                                 par = node1;
    1491                                 gpos++;
    1492                                 break;
    1493                         }
    1494                         int forgenorange = gpos;
    1495                         gpos += 2; //skipping "N:"
    1496                         beginindex = gpos;
    1497                         char* end = (char*)genot + beginindex;
    1498                         GenoOperators::parseNeuroClass(end);
    1499                         gpos += end - genot - beginindex;
    1500                         neutype = string(genot + beginindex, genot + gpos);
    1501                         node1 = new f4_node(neutype, par, forgenorange);
    1502                         par = node1;
    1503                         break;
    1504                 }
    1505                 case ':':
    1506                 {
    1507                         // neuron parameter  +! -! += -= +/ or -/
    1508                         if (sscanf(genot + gpos, ":%c%c%1[:]", &tc1, &tc2, &tc3) != 3)
    1509                                 // error: incorrect format
    1510                                 return gpos + 1 + 1;
    1511                         if ('+' == tc1) j = 1;
    1512                         else if ('-' == tc1) j = 0;
    1513                         else return gpos + 1 + 1;
    1514                         switch (tc2)
    1515                         {
    1516                         case '!':  case '=':  case '/':  break;
    1517                         default:
    1518                                 return gpos + 1 + 1;
    1519                         }
    1520                         node1 = new f4_node(":", par, gpos);
    1521                         node1->l1 = j;
    1522                         node1->i1 = (int)tc2;
    1523                         par = node1;
    1524                         j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), ':');
    1525                         gpos += j + 2;
    1526                         break;
    1527                 }
    1528                 case '[':
    1529                 {
    1530                         const char *end = parseConnection(genot + gpos, relfrom, w);
    1531                         if (end == NULL)
    1532                         {
    1533                                 end = parseConnectionWithNeuron(genot + gpos, neutype, w);
    1534                                 if (end == NULL) t = -1;
    1535                                 else t = 1;
    1536                         }
    1537                         else
    1538                         {
    1539                                 t = 0;
    1540                         }
    1541                         node1 = new f4_node("[", par, gpos);
    1542                         node1->s1 = neutype;
    1543                         node1->i1 = t;
    1544                         node1->l1 = relfrom;
    1545                         node1->f1 = w;
    1546                         par = node1;
    1547                         j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), ']');
    1548                         gpos += j + 2;
    1549                         break;
    1550                 }
    1551                 default:
    1552                 {
    1553                         //DB( printf("unknown character '%c' ! \n", genot[gpos]); )
    1554                         //add it, build will give the error or repair
    1555                         node1 = new f4_node(genot[gpos], par, gpos);
    1556                         par = node1;
    1557                         gpos++;
    1558                         break;
    1559                 }
    15601493                }
    15611494        }
     
    15661499                if (par->name != ">")
    15671500                {
    1568                         node1 = new f4_node('>', par, int(strlen(genot)) - 1);
     1501                        node1 = new f4_Node('>', par, int(strlen(genot)) - 1);
    15691502                        par = node1;
    15701503                }
     
    15931526}
    15941527
    1595 const char* parseConnectionWithNeuron(const char *fragm, string &neutype, double &weight)
    1596 {
    1597         const char *parser = fragm;
    1598         if (*parser != '[') return NULL;
    1599         parser++;
    1600         char* p = (char*)parser;
    1601         if (GenoOperators::parseNeuroClass(p) == NULL) return NULL;
    1602         neutype = string(parser, (const char *)p);
    1603         parser = p;
    1604         if (*parser != ':') return NULL;
    1605         parser++;
    1606         ExtValue val;
    1607         parser = val.parseNumber(parser, ExtPType::TDouble);
    1608         if (parser == NULL) return NULL;
    1609         weight = val.getDouble();
    1610         if (*parser != ']') return NULL;
    1611         parser++;
    1612         return parser;
    1613 }
    1614 
    1615 f4_node* f4_processtree(const char* geno)
    1616 {
    1617         f4_node *root;
    1618         int res;
    1619         root = new f4_node();
    1620         res = f4_processrec(geno, 0, root);
     1528f4_Node* f4_processtree(const char* geno)
     1529{
     1530        f4_Node *root = new f4_Node();
     1531        int res = f4_processrec(geno, 0, root);
    16211532        if (res) return NULL;
    16221533        //DB( printf("test f4  "); )
  • cpp/frams/genetics/f4/f4_general.h

    r829 r1227  
    11// This file is a part of Framsticks SDK.  http://www.framsticks.com/
    2 // Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
     2// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
    33// See LICENSE.txt for details.
    44
     
    2929void rolling_inc(double *v);
    3030
    31 class f4_node;   // later
     31class f4_Node;   // later
    3232class f4_Cell;   // later
    3333class f4_Cells;  // later
     
    3636/** @name Types of f4_Cell's */
    3737//@{
    38 #define T_UNDIFF4 40 ///<undifferentiated cell
    39 #define T_STICK4  41 ///<differentiated to stick, cannot divide
    40 #define T_NEURON4 42 ///<differentiated to neuron, can divide
     38#define CELL_UNDIFF 40 ///<undifferentiated cell
     39#define CELL_STICK  41 ///<differentiated to stick, cannot divide
     40#define CELL_NEURON 42 ///<differentiated to neuron, can divide
    4141//@}
    4242
     
    5454
    5555
    56 class f4_CellLink;
     56class f4_CellConn;
    5757
    5858/** @name Constraints of f4 genotype structures */
    5959//@{
    60 #define MAXINPUTS 100 ///<maximum number of neuron inputs
    61 #define MAX4CELLS 100 ///<maximum number of f4 organism cells
     60#define F4_MAX_CELL_INPUTS  10 ///<maximum number of neuron inputs in a developing organism
     61#define F4_MAX_CELLS 100 ///<maximum number of f4 organism cells
    6262//@}
    6363
    6464/**
    65  * Abstract cell type - the representation of single component in the developmental
     65 * Abstract cell type - the representation of a single component in the developmental
    6666 * encoding. In the beginning, each f4_Cell is undifferentiated. During the process
    6767 * of development it can divide or differentiate into a stick or a neuron. If it
     
    8686                /**
    8787                 * A constructor that takes the pointer to the repetition node and the count of repetitions.
    88                  * @param a pointer to f4_node for repetition character
     88                 * @param a pointer to f4_Node for repetition character
    8989                 * @param b the number of repetitions
    9090                 */
    91                 repeat_ptr(f4_node *a, int b) : node(a), count(b) { };
     91                repeat_ptr(f4_Node *a, int b) : node(a), count(b) { };
    9292
    9393                inline void makeNull() { node = NULL; count = -1; };
     
    9696
    9797                inline void dec() { count--; };
    98                 f4_node    *node; ///<pointer to the repetition code
     98                f4_Node    *node; ///<pointer to the repetition code
    9999                int       count; ///<repetition counter
    100100        };
     
    130130                static const int stackSize = 4;  ///<max 4 nested levels
    131131                repeat_ptr ptr[stackSize]; ///<array holding pointers to repeat_ptr
    132                 short int top;  ///<index of the top of the stack
     132                int top;  ///<index of the top of the stack
    133133        };
    134134
    135135        /**
    136136         * Creates a new f4_Cell object.
    137          * @param nname name of a cell, can be T_UNDIFF4, T_STICK4 or T_NEURON4
     137         * @param nnr number of the cell
    138138         * @param ndad pointer to the parent of the created cell
    139139         * @param nangle the amount of commas affecting branch angles
    140140         * @param newP genotype properties of a given cell
    141141         */
    142         f4_Cell(int nname, f4_Cell *ndad, int nangle, GeneProps newP);
     142        f4_Cell(int nnr, f4_Cell *ndad, int nangle, GeneProps newP);
    143143        /**
    144144         * Creates a new f4_Cell object.
    145145         * @param nO pointer to an organism containing the cell
    146          * @param nname name of the cell, can be T_UNDIFF4, T_STICK4 or T_NEURON4
     146         * @param nnr number of the cell
    147147         * @param ngeno pointer to the root of the genotype tree
    148          * @param ngcur pointer to the f4_node representing the current cell in the genotype tree
     148         * @param ngcur pointer to the f4_Node representing the current cell in the genotype tree
    149149         * @param ndad pointer to the parent of the created cell
    150150         * @param nangle the number of commas affecting branch angles
    151151         * @param newP genotype properties of a given cell
    152152         */
    153         f4_Cell(f4_Cells *nO, int nname, f4_node *ngeno, f4_node *ngcur, f4_Cell *ndad, int nangle, GeneProps newP);
     153        f4_Cell(f4_Cells *nO, int nnr, f4_Node *ngeno, f4_Node *ngcur, f4_Cell *ndad, int nangle, GeneProps newP);
    154154
    155155        ~f4_Cell();
     
    172172         *  - the stick modifiers, like rotation, will be applied on neuron cell,
    173173         *  - the differentiated cell will be differentiated again,
    174          *  - the neuron class inside cell connection (i.e. N[G:5]) is not a sensor,
    175174         *  - the connection between neurons cannot be established,
    176175         *  - the neuron class is not valid.
     
    178177         * @return 0 if development was successful, 1 if there was an error in genotype tree
    179178         */
    180         int onestep();
    181 
    182         /**
    183          * Adds a link between this neuron cell and a given neuron cell in nfrom. If the nfrom object
    184          * is not given, neuron type in nt should be a sensor type.
    185          * @param nfrom input neuron cell, or NULL if not given
    186          * @param nw weight of connection
    187          * @param nt empty string or name of sensor class
    188          * @return 0 if link is established, -1 otherwise
    189          */
    190         int   addlink(f4_Cell *nfrom, double nw, string nt);
     179        int oneStep();
     180
     181        /**
     182         * Adds a connection between this neuron cell and a given neuron cell in nfrom.
     183         * @param nfrom input neuron cell
     184         * @param nweight weight of connection
     185         * @return 0 if connection is established, -1 otherwise
     186         */
     187        int   addConnection(f4_Cell *nfrom, double nweight);
    191188
    192189        /**
     
    195192        void  adjustRec();
    196193
    197         int        name;               ///<name of cell (number)
     194        int        nr;                 ///<number of cell (seems to be used only in old f1 converter for neuron connections)
    198195        int        type;               ///<type
    199196        f4_Cell *dadlink;              ///<pointer to cell parent
    200197        f4_Cells  *org;                ///<uplink to organism
    201198
    202         f4_node *genot;                    ///<genotype tree
    203         f4_node *gcur;                 ///<current genotype execution pointer
    204         int active;                    ///<determines whether development is still active
     199        f4_Node *genot;                    ///<genotype tree
     200        f4_Node *gcur;                 ///<current genotype execution pointer
     201        bool active;                   ///<determines whether development is still active; even if false, the cell may "yield" - may be halted (but still having its onStep() called) due to neural connections waiting for other cells to potentially develop neurons
    205202        repeat_stack repeat;           ///<stack holding repetition nodes and counters
    206203        int recProcessedFlag;          ///<used during recursive traverse
     
    220217        int          neuro_refno;      ///<the number of the neuro object, used in f0
    221218
    222         int          ctrl;             ///<neuron type
    223219        double       inertia;          ///<inertia of neuron
    224220        double       force;            ///<force of neuron
    225221        double       sigmo;            ///<sigmoid of neuron
    226         f4_CellLink *links[MAXINPUTS]; ///<array of neuron links
    227         int          nolink;           ///<number of links
     222        f4_CellConn *conns[F4_MAX_CELL_INPUTS]; ///<array of neuron connections
     223        int          conns_count;      ///<number of connections
    228224        NeuroClass *neuclass;          ///<pointer to neuron class
    229225};
    230226
    231227/**
    232  * Class representing link between neuron cells.
    233  */
    234 class f4_CellLink
     228 * Class representing a connection between neuron cells.
     229 */
     230class f4_CellConn
    235231{
    236232public:
    237233        /**
    238234         * Constructor for f4_CellLink class. Parameter nfrom represents input
    239          * neuron cell or NULL if connection has defined sensor type inside, like "[G:5]".
    240          * The name of sensor class defined inside neuron connection is stored in the nt
    241          * parameter.
    242          * @param nfrom pointer to input neuron cell or NULL
    243          * @param nw weight of connection
    244          * @param nt name of neuron class or empty string
    245          */
    246         f4_CellLink(f4_Cell *nfrom, double nw, string nt);
    247 
    248         f4_Cell *from; ///<pointer to input neuron cell
    249         string t;      ///<empty if 'from' cell is given, NeuroClass name otherwise
    250         double w;      ///<weight of connection
     235         * neuron cell.
     236         * @param nfrom pointer to input neuron cell
     237         * @param nweight weight of connection
     238         */
     239        f4_CellConn(f4_Cell *nfrom, double nweight);
     240
     241        f4_Cell *from;  ///<pointer to input neuron cell
     242        double weight;  ///<weight of connection
    251243};
    252244
     
    264256         * @param nrepair 0 if nothing to repair
    265257         */
    266         f4_Cells(f4_node *genome, int nrepair);
     258        f4_Cells(f4_Node *genome, int nrepair);
    267259
    268260        /**
     
    291283
    292284        /**
    293          * Performs a single step of organism development. It runs each active cell
    294          * in the organism.
    295          * @return 0 if all cells are developed, or 1 otherwise
    296          */
    297         int  onestep();
     285         * Performs a single step of organism development. It runs each active cell in the organism.
     286         * @return false if all cells are developed or there is an error, true otherwise
     287         */
     288        bool oneStep();
    298289
    299290        /**
     
    302293         * @return 0 if organism developed successfully, error code if something went wrong
    303294         */
    304         int  simulate();
     295        int simulate();
     296
     297        /**
     298         * Prints the current state of the organism (for debugging purposes).
     299         * @param description printout header
     300         */
     301        void print_cells(const char* description);
    305302
    306303        /**
     
    308305         * @return error code
    309306         */
    310         int  geterror() { return error; };
     307        int getErrorCode() { return errorcode; };
    311308
    312309        /**
     
    314311         * @return position of an error
    315312         */
    316         int  geterrorpos() { return errorpos; };
     313        int getErrorPos() { return errorpos; };
    317314
    318315        /**
     
    325322         * Sets the element of genotype to be repaired by removal.
    326323         * @param nerrpos position of an error in genotype
    327          * @param rem the f4_node to be removed from the  genotype tree in order to repair
    328          */
    329         void setRepairRemove(int nerrpos, f4_node *rem);
     324         * @param rem the f4_Node to be removed from the  genotype tree in order to repair
     325         */
     326        void setRepairRemove(int nerrpos, f4_Node *rem);
    330327
    331328        /**
     
    336333         * @return 0 if repair can be performed, or -1 otherwise because the repair flag wasn't set in the constructor
    337334         */
    338         int  setRepairInsert(int nerrpos, f4_node *parent, f4_node *insert);
     335        int setRepairInsert(int nerrpos, f4_Node *parent, f4_Node *insert);
    339336
    340337        /**
     
    343340         * @param whichchild 1 if first child, 2 otherwise
    344341         */
    345         void repairGeno(f4_node *geno, int whichchild);
     342        void repairGeno(f4_Node *geno, int whichchild);
    346343
    347344        // the cells
    348         f4_Cell *C[MAX4CELLS];  ///<Array of all cells of an organism
    349         int       nc;           ///<Number of cells in an organism
     345        f4_Cell *C[F4_MAX_CELLS];  ///<Array of all cells of an organism
     346        int     cell_count;        ///<Number of cells in an organism
    350347
    351348private:
    352349        // for error reporting / genotype fixing
    353350        int repair;
    354         int error;
     351        int errorcode;
    355352        int errorpos;
    356         f4_node *repair_remove;
    357         f4_node *repair_parent;
    358         f4_node *repair_insert;
     353        f4_Node *repair_remove;
     354        f4_Node *repair_parent;
     355        f4_Node *repair_insert;
    359356        void toF1GenoRec(int curc, SString &out);
    360357        f4_Cell *tmpcel;                // needed by toF1Geno
    361         f4_node *f4rootnode;          // used by constructor
     358        f4_Node *f4rootnode;          // used by constructor
    362359};
    363360
     
    366363 * A class to organize a f4 genotype in a tree structure.
    367364 */
    368 class f4_node
     365class f4_Node
    369366{
    370367public:
    371         string name; ///<one-letter 'name', multiple characters for classes
    372         f4_node *parent; ///<parent link or NULL
    373         f4_node *child; ///<child or NULL
    374         f4_node *child2; ///<second child or NULL
     368        string name; ///<one-letter gene code or multiple characters for neuron classes (then neuclass != NULL)
     369        f4_Node *parent; ///<parent link or NULL
     370        f4_Node *child; ///<child or NULL
     371        f4_Node *child2; ///<second child or NULL
    375372        int pos; ///<original position in the string
    376         int i1; ///<internal int parameter1
    377         int l1; ///<internal long parameter1 (now also int, since long is not well specified and it is in our scenarios equivalent to int)
    378         double f1; ///<internal double parameter1
    379         string s1; ///<internal string parameter1
    380 
    381         f4_node();
     373
     374        int reps; ///<repetition counter for the '#' gene
     375        char prop_symbol; ///<old-style properties (force,intertia,sigmoid) of the N neuron: !=/
     376        bool prop_increase; ///<false=decrease neuron property (force,intertia,sigmoid), true=increase it
     377        int conn_from; ///<relative number of the neuron this neuron get an input from
     378        double conn_weight; ///<neuron connection weight
     379        NeuroClass *neuclass; ///< NULL or not if "name" is a neuroclass name with a proper genotype context ("N:neuroclassname"). New in 2023-04 - to fix fatal flaw with fundamental assumptions: it was impossible to distinguish between single-character neuron names such as S, D, G and single-character modifiers. They were all stored in the "name" field. Before 2018 this was never a problem because the only supported neuroclasses had distinctive symbols such as @|*GTS, and the set of supported modifiers was small and different from neuroclass letters (no G,D,S clash).
     380
     381        f4_Node();
    382382
    383383        /**
     
    387387         * @param npos position of node substring in the genotype string
    388388         */
    389         f4_node(string nname, f4_node *nparent, int npos);
     389        f4_Node(string nname, f4_Node *nparent, int npos);
    390390
    391391        /**
     
    395395         * @param npos position of node character in the genotype string
    396396         */
    397         f4_node(char nname, f4_node *nparent, int npos);
    398 
    399         ~f4_node();
     397        f4_Node(char nname, f4_Node *nparent, int npos);
     398
     399        ~f4_Node();
     400
     401        /**
     402         * Recursively print subtree (for debugging).
     403         * @param root starting node
     404         * @param indent initial indentation
     405         */
     406        static void print_tree(const f4_Node *root, int indent);
    400407
    401408        /**
     
    404411         * @return 0 if the child could be added, -1 otherwise
    405412         */
    406         int addChild(f4_node *nchi);
     413        int addChild(f4_Node *nchi);
    407414
    408415        /**
     
    411418         * @return 0 if child could be removed, -1 otherwise
    412419         */
    413         int removeChild(f4_node *nchi);
     420        int removeChild(f4_Node *nchi);
    414421
    415422        /**
     
    423430         * @return the number of nodes from this node
    424431         */
    425         int count();
     432        int count() const;
    426433
    427434        /**
     
    430437         * @return pointer to the nth subnode or NULL if not found
    431438         */
    432         f4_node* ordNode(int n);
     439        f4_Node* ordNode(int n);
    433440
    434441        /**
     
    436443         * @return random subnode
    437444         */
    438         f4_node* randomNode();
     445        f4_Node* randomNode();
    439446
    440447        /**
     
    444451         * @return a random subnode with a given size or NULL
    445452         */
    446         f4_node* randomNodeWithSize(int min, int max);
     453        f4_Node* randomNodeWithSize(int min, int max);
    447454
    448455        /**
     
    456463         * @return pointer to a tree copy
    457464         */
    458         f4_node* duplicate();
     465        f4_Node* duplicate();
    459466
    460467        /**
     
    468475/**
    469476 * The main function for converting a string of f4 encoding to a tree structure. Prepares
    470  * f4_node root of tree and runs f4_processrec function for it.
     477 * f4_Node root of tree and runs f4_processrec function for it.
    471478 * @param geno the string representing an f4 genotype
    472  * @return a pointer to the f4_node object representing the f4 tree root
    473  */
    474 f4_node* f4_processtree(const char *geno);
     479 * @return a pointer to the f4_Node object representing the f4 tree root
     480 */
     481f4_Node* f4_processtree(const char *geno);
    475482
    476483/**
    477484 * Scans a genotype string starting from a given position. This recursive method creates
    478  * a tree of f4_node objects. This method extracts each potentially functional element
    479  * of a genotype string to a separate f4_nodes. When the branching character '<' occurs,
    480  * f4_processrec is deployed for the latest f4_node element. This method does not
     485 * a tree of f4_Node objects. This method extracts each potentially functional element
     486 * of a genotype string to a separate f4_Nodes. When the branching character '<' occurs,
     487 * f4_processrec is deployed for the latest f4_Node element. This method does not
    481488 * analyse the genotype semantically, it only checks if the syntax is proper. The only
    482489 * semantic aspect is neuron class name extraction, where the GenoOperators
     
    487494 * @return 0 if processing was successful, otherwise returns the position of an error in the genotype
    488495 */
    489 int f4_processrec(const char *genot, unsigned pos0, f4_node *parent);
     496int f4_processrec(const char *genot, unsigned pos0, f4_Node *parent);
    490497
    491498/**
     
    502509const char *parseConnection(const char *fragm, int &relfrom, double &weight);
    503510
    504 /**
    505  * Parses the notation of the neuron connection with neuron definition - takes
    506  * the beginning of the connection definition, extracts the name of neuron class
    507  * that will be the input for the current neuron and the weight of the connection.
    508  * After successful parsing, returns a pointer to the first character after the connection
    509  * definition, or NULL if the connection definition was not valid due to the lack of [, :, ]
    510  * characters, an invalid value of the weight or an invalid neuron class name.
    511  * @param fragm the beginning of the connection definition, should be the '[' character
    512  * @param neutype the reference to a string representing the input neuron class name. The name of the class is validated with GenoOperators::parseNeuroClass()
    513  * @param weight the reference to a double variable in which the weight of the connection will be stored
    514  * @return the pointer to the first character in string after the connection definition
    515  */
    516 const char *parseConnectionWithNeuron(const char *fragm, string &neutype, double &weight);
    517 
    518511#endif
  • cpp/frams/genetics/f4/f4_oper.cpp

    r1108 r1227  
    11// This file is a part of Framsticks SDK.  http://www.framsticks.com/
    2 // Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
     2// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
    33// See LICENSE.txt for details.
    44
     
    66// Copyright (C) since 2001 Maciej Komosinski
    77// 2018, Grzegorz Latosinski, added support for new API for neuron types and their properties
     8
     9
     10// This representation has a tendency to bloat - adding a small penalty to fitness such as "this.velocity - 0.000000001*String.len(this.genotype);"
     11// may help, but it would be better to improve the source code to make genetic operators neutral in terms of genotype length. Adding such a penalty
     12// removes "work in progress" changes in genotypes thus promoting immediate, straightforward improvements while hindering slower, multifaceted progress.
     13// TODO getting rid of redundancy (having valid genotypes with a lot of "junk code") in this representation looks like a good idea.
     14//
     15// Note: symbols after the last > are ignored, for example /*4*/<X>N:N>N:N[2:-0.5]XXXwhatever but since they are not parsed into the f4_Node tree, they will be lost after any mutation.
     16//
     17// TODO the behavior of neuron input indexes during mutation seems badly implemented (see also TREAT_BAD_CONNECTIONS_AS_ERRORS). Are they kept properly maintained when nodes are added and removed? This could be done well because during mutation we operate on the tree structure with cross-references between nodes (so they should not be affected by local changes in the tree), and then convert the tree back to string. Yet, the f4_Node.conn_from is an integer and these fields in nodes do not seem to be maintained on tree node adding/removal... change these integer offsets to references to node objects? But actually, do the offsets that constitute relative connection references concern the f4_Node tree structure (and all these sophisticated calculations of offsets during mutation are useful) or rather they concern the f4_Cells development? verify all situations in f4_Cell::oneStep(), case '['.
     18// TODO add simplifying sequences of modifiers (so capital and small letter cancel out, like in f1) - but seems like each single modifier is a separate f4_Node? and perhaps we don't want to use the repair mechanism for this... maybe mutations, when they add/modify/remove a modifier node, should be "cleaning" the tree by removing nodes when they encounter contradictory modifiers on the same subpath?
     19// TODO add support for properties of (any class of) neurons - not just sigmoid/force/intertia (':' syntax) for N
     20// TODO add mapping genotype character ranges for neural [connections]
     21
    822
    923#include "f4_oper.h"
     
    2034
    2135// codes that can be changed (apart from being added/deleted)
    22 #define MUT_CHAN_CODES "<[#"
    23 #define REP_MAXCOUNT 19
     36#define F4_MUT_CHANGE_CODES "<[#"
    2437
    2538#define FIELDSTRUCT Geno_f4
     
    2740static ParamEntry geno_f4_paramtab[] =
    2841{
    29         { "Genetics: f4", 1, F4_COUNT + F4_ADD_COUNT + 1, },
    30         { "f4_mut_add", 0, 0, "Add node", "f 0 100 50", FIELD(prob[F4_ADD]), "mutation: probability of adding a node", },
    31         { "f4_mut_add_div", 0, 0, "- add division", "f 0 100 20", FIELD(probadd[F4_ADD_DIV]), "add node mutation: probability of adding a division", },
    32         { "f4_mut_add_conn", 0, 0, "- add connection", "f 0 100 15", FIELD(probadd[F4_ADD_CONN]), "add node mutation: probability of adding a neural connection", },
    33         { "f4_mut_add_neupar", 0, 0, "- add neuron property", "f 0 100 5", FIELD(probadd[F4_ADD_NEUPAR]), "add node mutation: probability of adding a neuron property/modifier", },
    34         { "f4_mut_add_rep", 0, 0, "- add repetition", "f 0 100 10", FIELD(probadd[F4_ADD_REP]), "add node mutation: probability of adding a repetition", },
    35         { "f4_mut_add_simp", 0, 0, "- add simple node", "f 0 100 50", FIELD(probadd[F4_ADD_SIMP]), "add node mutation: probability of adding a random, simple gene", },
    36         { "f4_mut_del", 0, 0, "Delete node", "f 0 100 20", FIELD(prob[F4_DEL]), "mutation: probability of deleting a node", },
    37         { "f4_mut_mod", 0, 0, "Modify node", "f 0 100 30", FIELD(prob[F4_MOD]), "mutation: probability of changing a node", },
     42        { "Genetics: f4", 1, F4_COUNT + F4_ADD_COUNT + F4_MODNEU_COUNT + 2, },
     43        { "f4_mut_add", 0, 0, "Add node", "f 0 100 50", FIELD(prob[F4_ADD]), "Mutation: probability of adding a node", },
     44        { "f4_mut_add_div", 0, 0, "- add division", "f 0 100 20", FIELD(probadd[F4_ADD_DIV]), "Add node mutation: probability of adding a division", },
     45        { "f4_mut_add_conn", 0, 0, "- add connection", "f 0 100 15", FIELD(probadd[F4_ADD_CONN]), "Add node mutation: probability of adding a neural connection", },
     46        { "f4_mut_add_neupar", 0, 0, "- add neuron property", "f 0 100 5", FIELD(probadd[F4_ADD_NEUPAR]), "Add node mutation: probability of adding a neuron property/modifier", },
     47        { "f4_mut_add_rep", 0, 0, "- add repetition '#'", "f 0 100 10", FIELD(probadd[F4_ADD_REP]), "Add node mutation: probability of adding the '#' repetition gene", },
     48        { "f4_mut_add_simp", 0, 0, "- add simple node", "f 0 100 50", FIELD(probadd[F4_ADD_SIMP]), "Add node mutation: probability of adding a random, simple gene", },
     49
     50        { "f4_mut_del", 0, 0, "Delete node", "f 0 100 20", FIELD(prob[F4_DEL]), "Mutation: probability of deleting a node", },
     51
     52        { "f4_mut_mod", 0, 0, "Modify node", "f 0 100 30", FIELD(prob[F4_MOD]), "Mutation: probability of changing a node", },
     53        { "f4_mut_modneu_conn", 0, 0, "- neuron input: modify source", "f 0 100 60", FIELD(probmodneu[F4_MODNEU_CONN]), "Neuron input mutation: probability of changing its source neuron", },
     54        { "f4_mut_modneu_weight", 0, 0, "- neuron input: modify weight", "f 0 100 40", FIELD(probmodneu[F4_MODNEU_WEIGHT]), "Neuron input mutation: probability of changing its weight", },
     55
     56        { "f4_mut_max_rep", 1, 0, "Maximum number for '#' repetitions", "d 2 20 6", FIELD(mut_max_rep), "Maximum allowed number of repetitions for the '#' repetition gene", },
    3857        { "f4_mut_exmod", 1, 0, "Excluded modifiers", "s 0 30", FIELD(excluded_modifiers), "Modifiers that will not be added nor deleted during mutation\n(all: " F14_MODIFIERS ")", },
    3958        { 0, },
     
    5978        mutation_method_names[index++] = "deleted a node";
    6079        mutation_method_names[index++] = "modified a node";
    61         if (index != F4_COUNT + F4_ADD_COUNT - 1) logMessage("Geno_f4", "Constructor", 3, "Mutation names init error");
     80        if (index != F4_COUNT + F4_ADD_COUNT - 1) logMessage("Geno_f4", "Constructor", LOG_CRITICAL, "Mutation names init error");
    6281}
    6382
     
    6786}
    6887
    69 int Geno_f4::ValidateRec(f4_node *geno, int retrycount) const
     88int Geno_f4::ValidateRec(f4_Node *geno, int retrycount) const
    7089{
    7190        // ! the genotype is geno->child (not geno) !
     
    7695
    7796        // errors not fixed:
    78         if (GENOPER_OPFAIL == cells.geterror())
    79         {
    80                 if (cells.geterrorpos() >= 0) return 1 + cells.geterrorpos();
     97        if (cells.getErrorCode() == GENOPER_OPFAIL)
     98        {
     99                if (cells.getErrorPos() >= 0) return 1 + cells.getErrorPos();
    81100                return GENOPER_OPFAIL;
    82101        }
    83102        // errors can be fixed
    84         if (GENOPER_REPAIR == cells.geterror())
     103        if (cells.getErrorCode() == GENOPER_REPAIR)
    85104        {
    86105                cells.repairGeno(geno, 1);
     
    102121{
    103122        // convert geno to tree, then try to validate 20 times
    104         f4_node root;
     123        f4_Node root;
    105124        if (f4_processrec(geno, 0, &root) || root.childCount() != 1) return GENOPER_OK; // cannot repair
    106125        if (ValidateRec(&root, 20) == GENOPER_REPAIR) // if repaired, make it back to string
     
    115134int Geno_f4::checkValidity(const char* geno, const char *genoname)
    116135{
    117         f4_node root;
     136        f4_Node root;
    118137        int res = f4_processrec(geno, 0, &root);
    119138        if (res) return res;  // errorpos, >0
     
    121140        f4_Cells cells(root.child, 0);
    122141        cells.simulate();
    123         if (cells.geterror() == GENOPER_OPFAIL || cells.geterror() == GENOPER_REPAIR)
    124         {
    125                 if (cells.geterrorpos() >= 0) return 1 + cells.geterrorpos();
     142        if (cells.getErrorCode() == GENOPER_OPFAIL || cells.getErrorCode() == GENOPER_REPAIR)
     143        {
     144                if (cells.getErrorPos() >= 0) return 1 + cells.getErrorPos();
    126145                else return 1; //earlier: GENOPER_OPFAIL;
    127146        }
     
    130149
    131150
    132 int Geno_f4::MutateOne(f4_node *& g, int &method) const
     151int Geno_f4::MutateOne(f4_Node *& g, int &method) const
    133152{
    134153        // ! the genotype is g->child (not g) !
     
    136155        // do the mutation
    137156        // pick a random node
    138         f4_node *n1 = g->child->randomNode();
    139         vector<NeuroClass*> neulist;
    140         //DB( printf("%c\n", n1->name); )
    141         int neuronid = -1;
     157        f4_Node *node_mutated = g->child->randomNode();
     158        //DB( printf("%c\n", node_mutated->name); )
    142159
    143160        switch (roulette(prob, F4_COUNT))
     
    151168                {
    152169                        // add division ('<')
    153                         f4_node *n3 = n1->parent;
    154                         n3->removeChild(n1);
    155                         f4_node *n2 = new f4_node('<', n3, n3->pos);
    156                         n2->addChild(n1);
     170                        f4_Node *node_mutated_parent = node_mutated->parent;
     171                        node_mutated_parent->removeChild(node_mutated);
     172                        f4_Node *node_new_div = new f4_Node('<', node_mutated_parent, node_mutated_parent->pos);
     173                        node_new_div->addChild(node_mutated);
    157174                        // new cell is stick or neuron
    158175                        // "X>" or "N>"
    159                         f4_node *n5 = NULL;
    160                         double pr = rndDouble(1);
    161                         pr -= 0.5;
    162                         if (pr < 0) n5 = new f4_node('X', n2, n2->pos);
     176                        constexpr double STICK_OR_NEURON = 0.5; // hardcoded probability... could be parametrized, but in a general case (unknown fitness goal) 0.5 makes sense?
     177                        f4_Node *node_new = NULL; //stick or neuron or neural connection
     178                        if (rndDouble(1) < STICK_OR_NEURON)
     179                                node_new = new f4_Node('X', node_new_div, node_new_div->pos);
    163180                        else
    164181                        {
    165182                                // make neuron
    166183                                NeuroClass *rndclass = GenoOperators::getRandomNeuroClass(Model::SHAPETYPE_BALL_AND_STICK);
    167                                 if (rndclass == NULL)
     184                                if (rndclass == NULL) //no active neurons?
    168185                                {
    169                                         n5 = new f4_node('X', n2, n2->pos);
     186                                        node_new = new f4_Node('X', node_new_div, node_new_div->pos);
    170187                                }
    171188                                else
    172189                                {
    173                                         f4_node *n4 = new f4_node(rndclass->getName().c_str(), n2, n2->pos);
    174                                         if (rndclass->getPreferredInputs() != 0)
     190                                        f4_Node *node_new_neuron = new f4_Node(rndclass->getName().c_str(), node_new_div, node_new_div->pos);
     191                                        node_new_neuron->neuclass = rndclass;
     192                                        node_new = node_new_neuron; //can be changed below if all goes well and we add a new connection too
     193                                        if (probadd[F4_ADD_CONN] > 0) //user wants to add connections
    175194                                        {
    176                                                 neuronid = -1;
    177                                                 for (int i = 0; i < g->count(); i++)
     195                                                if (rndclass->getPreferredInputs() != 0) //neuron also wants connections?
    178196                                                {
    179                                                         f4_node *gcur = g->ordNode(i);
    180                                                         char* temp = (char*)gcur->name.c_str();
    181                                                         NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
    182                                                         if (neuclass != NULL)
     197                                                        int node_new_neuron_index, other_neuron_index;
     198                                                        bool ok = findConnectionNeuronIndexes(g, node_new_neuron, true, node_new_neuron_index, other_neuron_index); //node_new_neuron_index==-1 should never happen, we just added node_new_neuron we are looking for
     199                                                        if (ok) //we can create a new connection
    183200                                                        {
    184                                                                 neulist.push_back(neuclass);
    185                                                         }
    186                                                         if (g->ordNode(i) == n3)
    187                                                         {
    188                                                                 neuronid = int(neulist.size()) - 1;
     201                                                                node_new = new f4_Node('[', node_new_neuron, node_new_div->pos);
     202                                                                connectionNodeChangeRandom(node_new, node_new_neuron_index, other_neuron_index);
    189203                                                        }
    190204                                                }
    191                                                 if (neuronid == -1)
     205                                                else if (rndclass->getPreferredOutput() > 0) //neuron also wants connections?
    192206                                                {
    193                                                         return GENOPER_OPFAIL;
     207                                                        // Not so easy: we would need to add a '[' node as a child not of node_new_neuron, but of other neuron that would get an input from node_new_neuron (and need to properly calculate relative connection reference).
     208                                                        // The "false" argument in findConnectionNeuronIndexes() below is not suffient, because we also need to access (find) the f4_Node of the other neuron.
     209                                                        // A similar logic is implemented in F4_ADD_CONN below, but let's not complicate this F4_ADD_DIV mutation anymore.
     210                                                        // A disadvantage is that the node_new_neuron added here which is a neuron that provides output (e.g., a receptor, N, etc.) will not get connected immediately here even when there are already existing neurons wanting inputs (e.g., muscles, N, etc.).
     211                                                        //bool ok = findConnectionNeuronIndexes(g, ... , false, ..., ...);
    194212                                                }
    195                                                 n5 = new f4_node('[', n4, n2->pos);
    196                                                 linkNodeMakeRandom(n5, neuronid, neulist);
    197                                         }
    198                                         else {
    199                                                 n5 = n4;
    200213                                        }
    201214                                }
    202215                        }
    203                         new f4_node('>', n5, n5->pos);
    204                         n1->parent = n2;
    205                         // now with 50% chance swap children
     216                        new f4_Node('>', node_new, node_new->pos); //adds to node_new
     217                        node_mutated->parent = node_new_div;
     218                        // now, swap children with 50% chance
    206219                        if (rndUint(2) == 0)
    207220                        {
    208                                 n3 = n2->child;
    209                                 n2->child = n2->child2;
    210                                 n2->child2 = n3;
    211                         }
    212                 }
    213                         break;
     221                                node_mutated_parent = node_new_div->child;
     222                                node_new_div->child = node_new_div->child2;
     223                                node_new_div->child2 = node_mutated_parent;
     224                        }
     225                }
     226                break;
    214227                case F4_ADD_CONN:
    215228                {
    216                         // add link
    217                         f4_node *par = n1->parent;
    218                         char* temp = (char*)par->name.c_str();
    219                         NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
    220                         if (neuclass != NULL)
    221                         {
    222                                 n1->parent->removeChild(n1);
    223                                 f4_node *n2 = new f4_node('[', n1->parent, n1->parent->pos);
    224                                 n2->addChild(n1);
    225                                 n1->parent = n2;
    226                                 neuronid = -1;
    227                                 for (int i = 0; i < g->count(); i++)
    228                                 {
    229                                         f4_node *gcur = g->ordNode(i);
    230                                         temp = (char*)gcur->name.c_str();
    231                                         NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
    232                                         if (neuclass != NULL)
    233                                         {
    234                                                 neulist.push_back(neuclass);
    235                                         }
    236                                         if (gcur == par)
    237                                         {
    238                                                 neuronid = int(neulist.size()) - 1;
    239                                         }
    240                                 }
    241                                 if (neuronid == -1)
    242                                 {
    243                                         return GENOPER_OPFAIL;
    244                                 }
    245                                 linkNodeMakeRandom(n2, neuronid, neulist);
    246                         }
    247                 }
    248                         break;
     229                        // add connection
     230
     231                        // the probability that a randomly selected node will be a neuron and additionally this neuron will accept inputs is low,
     232                        // so we disregard randomly picked node_mutated and build a list of all valid candidate nodes here, then select a random one from them.
     233
     234                        vector<f4_Node*> candidate_nodes; //neurons that accept input(s)
     235                        for (int i = 0; i < g->count(); i++)
     236                        {
     237                                f4_Node *node = g->ordNode(i);
     238                                f4_Node *node_parent = node->parent;
     239                                if (node_parent == NULL || node_parent->neuclass == NULL) continue;
     240                                int prefinputs = node_parent->neuclass->getPreferredInputs();
     241                                if (prefinputs == -1 ||
     242                                        prefinputs > 0) //would be nice if we could easily and quickly check if the parent already has its preferred inputs used, so that we do not produce an invalid mutation here... it is possible through the f4_Cell.n_conns field, but only during organism development
     243                                        candidate_nodes.push_back(node);
     244                        }
     245
     246                        if (candidate_nodes.size() == 0)
     247                                return GENOPER_OPFAIL;
     248
     249                        node_mutated = candidate_nodes[rndUint((unsigned int)candidate_nodes.size())];
     250                        f4_Node *node_mutated_parent = node_mutated->parent;
     251
     252                        int node_mutated_parent_index, other_neuron_index;
     253                        bool ok = findConnectionNeuronIndexes(g, node_mutated_parent, true, node_mutated_parent_index, other_neuron_index); //node_mutated_parent_index==-1 should never happen, we earlier selected the neuron we are now looking for
     254                        if (!ok)
     255                                return GENOPER_OPFAIL;
     256
     257                        node_mutated->parent->removeChild(node_mutated); //this subtree will be reconnected below, as a child to node_new_conn
     258                        f4_Node *node_new_conn = new f4_Node('[', node_mutated->parent, node_mutated->parent->pos);
     259                        node_new_conn->addChild(node_mutated);
     260                        node_mutated->parent = node_new_conn; // node_mutated_parent is the neuron, node_mutated->parent is '['
     261                        connectionNodeChangeRandom(node_new_conn, node_mutated_parent_index, other_neuron_index);
     262                }
     263                break;
    249264                case F4_ADD_NEUPAR:
    250265                {
    251266                        // add neuron modifier
    252                         n1->parent->removeChild(n1);
    253                         f4_node *n2 = new f4_node(':', n1->parent, n1->parent->pos);
     267                        node_mutated->parent->removeChild(node_mutated);
     268                        f4_Node *n2 = new f4_Node(':', node_mutated->parent, node_mutated->parent->pos);
    254269                        nparNodeMakeRandom(n2);
    255                         n2->addChild(n1);
    256                         n1->parent = n2;
    257                 }
    258                         break;
     270                        n2->addChild(node_mutated);
     271                        node_mutated->parent = n2;
     272                }
     273                break;
    259274                case F4_ADD_REP:
    260275                {
    261276                        // add repetition ('#')
    262                         // repeated code (left child) is the original, right child is empty, count is 2
    263                         f4_node *n3 = n1->parent;
    264                         n3->removeChild(n1);
    265                         f4_node *n2 = new f4_node('#', n3, n3->pos);
    266                         n2->i1 = 2;
    267                         n2->addChild(n1);
    268                         new f4_node('>', n2, n2->pos);
    269                         n1->parent = n2;
    270                 }
    271                         break;
     277                        // repeated code (left child) is the original, right child is empty, count is set to 2
     278                        f4_Node *n3 = node_mutated->parent;
     279                        n3->removeChild(node_mutated);
     280                        f4_Node *n2 = new f4_Node('#', n3, n3->pos);
     281                        n2->reps = 2;
     282                        n2->addChild(node_mutated);
     283                        new f4_Node('>', n2, n2->pos);
     284                        node_mutated->parent = n2;
     285                }
     286                break;
    272287                case F4_ADD_SIMP:
    273288                {
    274289                        // add simple node
    275290                        // choose a simple node from ADD_SIMPLE_CODES
    276                         n1->parent->removeChild(n1);
    277                         //f4_node *n2 = new f4_node(ADD_SIMPLE_CODES[rndUint(strlen(ADD_SIMPLE_CODES))], n1->parent, n1->parent->pos);
     291                        node_mutated->parent->removeChild(node_mutated);
     292                        //f4_Node *n2 = new f4_Node(ADD_SIMPLE_CODES[rndUint(strlen(ADD_SIMPLE_CODES))], n1->parent, n1->parent->pos);
    278293                        int modifierid = GenoOperators::getRandomChar(all_modifiers, excluded_modifiers.c_str());
    279                         f4_node *n2 = new f4_node(all_modifiers[modifierid], n1->parent, n1->parent->pos);
    280                         n2->addChild(n1);
    281                         n1->parent = n2;
    282                 }
    283                         break;
    284                 }
    285         }
     294                        f4_Node *n2 = new f4_Node(all_modifiers[modifierid], node_mutated->parent, node_mutated->parent->pos);
     295                        n2->addChild(node_mutated);
     296                        node_mutated->parent = n2;
     297                }
    286298                break;
     299                }
     300        }
     301        break;
    287302
    288303        case F4_DEL:
     
    294309                for (int i = 0; i < 10; i++)
    295310                {
    296                         if ((NULL != n1->parent) && (g != n1->parent))
    297                                 if (NULL != n1->child)
     311                        if ((node_mutated->parent != NULL) && (g != node_mutated->parent))
     312                                if (node_mutated->child != NULL)
    298313                                        break;
    299314                        // try a new one
    300                         n1 = g->child->randomNode();
    301                 }
    302                 if ((NULL != n1->parent) && (g != n1->parent))
    303                 {
    304                         switch (n1->childCount())
     315                        node_mutated = g->child->randomNode();
     316                }
     317                if ((node_mutated->parent != NULL) && (g != node_mutated->parent))
     318                {
     319                        switch (node_mutated->childCount())
    305320                        {
    306321                        case 0: break;
    307322                        case 1:  // one child
    308323                        {
    309                                 f4_node *n2 = n1->parent;
    310                                 n2->removeChild(n1);
    311                                 if (NULL != n1->child)
     324                                f4_Node *node_mutated_parent = node_mutated->parent;
     325                                node_mutated_parent->removeChild(node_mutated);
     326                                if (node_mutated->child != NULL)
    312327                                {
    313                                         n1->child->parent = n2;
    314                                         n2->addChild(n1->child);
    315                                         n1->child = NULL;
     328                                        node_mutated->child->parent = node_mutated_parent;
     329                                        node_mutated_parent->addChild(node_mutated->child);
     330                                        node_mutated->child = NULL;
    316331                                }
    317                                 if (NULL != n1->child2)
     332                                if (node_mutated->child2 != NULL)
    318333                                {
    319                                         n1->child2->parent = n2;
    320                                         n2->addChild(n1->child2);
    321                                         n1->child2 = NULL;
     334                                        node_mutated->child2->parent = node_mutated_parent;
     335                                        node_mutated_parent->addChild(node_mutated->child2);
     336                                        node_mutated->child2 = NULL;
    322337                                }
    323338                                // destroy n1
    324                                 n1->parent = NULL;
    325                                 delete n1;
    326                         }
    327                                 break;
     339                                node_mutated->parent = NULL;
     340                                delete node_mutated;
     341                        }
     342                        break;
    328343
    329344                        case 2:  // two children
    330345                        {
    331346                                // two children
    332                                 f4_node *n2 = n1->parent;
    333                                 n2->removeChild(n1);
     347                                f4_Node *n2 = node_mutated->parent;
     348                                n2->removeChild(node_mutated);
    334349                                // n1 has two children. pick one randomly 50-50, destroy other
    335350                                if (rndUint(2) == 0)
    336351                                {
    337                                         n1->child->parent = n2;
    338                                         n2->addChild(n1->child);
    339                                         n1->child = NULL;
    340                                         n1->child2->parent = NULL;
     352                                        node_mutated->child->parent = n2;
     353                                        n2->addChild(node_mutated->child);
     354                                        node_mutated->child = NULL;
     355                                        node_mutated->child2->parent = NULL;
    341356                                }
    342357                                else
    343358                                {
    344                                         n1->child2->parent = n2;
    345                                         n2->addChild(n1->child2);
    346                                         n1->child2 = NULL;
    347                                         n1->child->parent = NULL;
     359                                        node_mutated->child2->parent = n2;
     360                                        n2->addChild(node_mutated->child2);
     361                                        node_mutated->child2 = NULL;
     362                                        node_mutated->child->parent = NULL;
    348363                                }
    349364                                // destroy n1
    350                                 n1->parent = NULL;
    351                                 delete n1;
    352                         }
    353                                 break;
     365                                node_mutated->parent = NULL;
     366                                delete node_mutated;
     367                        }
     368                        break;
    354369                        }
    355370                }
    356371                else return GENOPER_OPFAIL;
    357372        }
    358                 break;
     373        break;
    359374        case F4_MOD:
    360375        {
    361376                method = F4_ADD_COUNT - 1 + F4_MOD;
    362377                // change a node
    363                 // the only nodes that are modifiable are MUT_CHAN_CODES
     378                // the only nodes that are modifiable are F4_MUT_CHANGE_CODES
    364379                // try to get a modifiable node
    365380                // already picked a node, but repeat may be needed
     
    367382                while (1)
    368383                {
    369                         if (strchr(MUT_CHAN_CODES, n1->name[0])) break;
     384                        if (strchr(F4_MUT_CHANGE_CODES, node_mutated->name[0])) break;
    370385                        // try a new one
    371                         n1 = g->child->randomNode();
     386                        node_mutated = g->child->randomNode();
    372387                        i++;
    373388                        if (i >= 20) return GENOPER_OPFAIL;
    374389                }
    375                 switch (n1->name[0])
     390                switch (node_mutated->name[0])
    376391                {
    377392                case '<':
    378393                {
    379394                        // swap children
    380                         f4_node *n2 = n1->child; n1->child = n1->child2; n1->child2 = n2;
    381                 }
    382                         break;
     395                        f4_Node *n2 = node_mutated->child;
     396                        node_mutated->child = node_mutated->child2;
     397                        node_mutated->child2 = n2;
     398                }
     399                break;
    383400                case '[':
    384401                {
    385                         neuronid = -1;
    386                         for (int i = 0; i < g->count(); i++)
    387                         {
    388                                 f4_node *gcur = g->ordNode(i);
    389                                 char *temp = (char*)gcur->name.c_str();
    390                                 NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
    391                                 if (neuclass != NULL)
    392                                 {
    393                                         neulist.push_back(neuclass);
    394                                 }
    395                                 if (gcur == n1)
    396                                 {
    397                                         neuronid = int(neulist.size()) - 1;
    398                                 }
    399                         }
    400                         if (neuronid == -1)
    401                         {
    402                                 return GENOPER_OPFAIL;
    403                         }
    404                         linkNodeChangeRandom(n1, neuronid, neulist);
    405                 }
    406                         break;
     402                        switch (roulette(probmodneu, F4_MODNEU_COUNT))
     403                        {
     404                        case F4_MODNEU_CONN:
     405                        {
     406                                f4_Node *neuron = node_mutated; //we start in '[' node and follow up parents until we find the neuron with these connections
     407                                while (neuron != NULL && neuron->neuclass == NULL) neuron = neuron->parent;
     408                                if (neuron == NULL)
     409                                        return GENOPER_OPFAIL; //did not find a neuron on the way up tree
     410
     411
     412                                int neuron_index, other_neuron_index;
     413                                bool ok = findConnectionNeuronIndexes(g, neuron, true, neuron_index, other_neuron_index); //neuron_index==-1 should never happen, we know the neuron is in the tree
     414                                if (!ok)
     415                                        return GENOPER_OPFAIL;
     416
     417                                connectionNodeChangeRandom(node_mutated, neuron_index, other_neuron_index);
     418                                break;
     419                        }
     420                        case F4_MODNEU_WEIGHT:
     421                                node_mutated->conn_weight = GenoOperators::getMutatedNeuronConnectionWeight(node_mutated->conn_weight);
     422                                break;
     423                        }
     424                }
     425                break;
    407426
    408427                case '#':
    409428                {
    410                         repeatNodeChangeRandom(n1);
    411                 }
    412                         break;
    413                 }
    414         }
     429                        repeatNodeChangeRandom(node_mutated);
     430                }
    415431                break;
     432                }
     433        }
     434        break;
    416435
    417436        default: //no mutations allowed?
    418437                return GENOPER_OPFAIL;
    419438        }
    420 
    421439        return GENOPER_OK;
    422440}
    423441
    424 // make a random [ node
    425 void Geno_f4::linkNodeMakeRandom(f4_node *nn, int neuid, vector<NeuroClass*> neulist) const
    426 {
    427         float prob1;
    428         NeuroClass *nc = NULL;
    429 
    430         // 35% chance one of *GTS
    431         prob1 = rndDouble(1);
    432         prob1 -= 0.35f;
    433         if (prob1 < 0)
    434         {
    435                 // '*', 'G', 'T', or 'S', 1/4 chance each
    436                 nc = GenoOperators::getRandomNeuroClassWithOutputAndNoInputs(Model::SHAPETYPE_BALL_AND_STICK);
    437         }
    438         if (nc != NULL)
    439         {
    440                 nn->i1 = 1;
    441                 nn->s1 = nc->getName().c_str();
    442                 nn->l1 = 0;
    443         }
    444         else
    445         {
    446                 // relative input link
    447                 int id = GenoOperators::getRandomNeuroClassWithOutput(neulist);
    448                 int relid = neuid - id;
    449                 nn->l1 = relid;
    450                 //nn->l1 = (int)(4.0f * (rndDouble(1) - 0.5f));
    451         }
    452         // weight
    453         nn->f1 = GenoOperators::getMutatedNeuronConnectionWeight(nn->f1);
    454         //nn->f1 = 10.0f * (rndDouble(1) - 0.5f);
     442// find all neurons and the needle
     443vector<NeuroClass*> Geno_f4::findAllNeuronsAndNode(f4_Node * const & g, f4_Node* const &needle_neuron, int &found_index)
     444{
     445        found_index = -1; // not found (for example, needle_neuron is not a neuroclass node or not added to the "g" tree)
     446        vector<NeuroClass*> neulist;
     447        for (int i = 0; i < g->count(); i++)
     448        {
     449                f4_Node *node = g->ordNode(i);
     450                if (node->neuclass != NULL)
     451                {
     452                        neulist.push_back(node->neuclass);
     453                        if (node == needle_neuron)
     454                                found_index = int(neulist.size()) - 1;
     455                }
     456        }
     457        return neulist;
     458}
     459
     460bool Geno_f4::findConnectionNeuronIndexes(f4_Node * const &g, f4_Node *neuron, bool other_has_output, int &neuron_index, int &other_neuron_index)
     461{
     462        vector<NeuroClass*> neulist = findAllNeuronsAndNode(g, neuron, neuron_index);
     463        if (neuron_index == -1)
     464                return false;
     465
     466        other_neuron_index = other_has_output ?
     467                GenoOperators::getRandomNeuroClassWithOutput(neulist) //find an existing neuron that provides an output
     468                :
     469                GenoOperators::getRandomNeuroClassWithInput(neulist); //find an existing neuron that accepts input(s)
     470        return other_neuron_index >= 0;
    455471}
    456472
    457473// change a [ node
    458 void Geno_f4::linkNodeChangeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const      //rewritten by M.K. - should work as before (not tested)
    459 {
    460         double probs[3] = { 0.1, 0.3, 0.6 };
    461         NeuroClass *cl;
    462         // 10% change type
    463         // 30% change link
    464         // 60% change weight
    465 
    466         switch (roulette(probs, 3))
    467         {
    468         case 0: // change type
    469                 // 80% for link, 20% for random sensor
    470                 if (rndDouble(1) < 0.2f)
    471                 {
    472                         cl = GenoOperators::getRandomNeuroClassWithOutputAndNoInputs(Model::SHAPETYPE_BALL_AND_STICK);
    473                         if (cl != NULL)
    474                         {
    475                                 nn->i1 = 1;
    476                                 nn->s1 = cl->name.c_str();
    477                                 nn->l1 = 0;
    478                         }
    479                 }
    480                 break;
    481         case 1: // change link
    482                 if (0 == nn->i1) // relative input link
    483                 {
    484                         int id = GenoOperators::getRandomNeuroClassWithOutput(neulist);
    485                         nn->l1 = neuid - id;
    486                 }
    487                 //nn->l1 += (int)(2.0f * (rndDouble(1) - 0.5f));
    488                 break;
    489         case 2: // change weight
    490                 nn->f1 = GenoOperators::getMutatedNeuronConnectionWeight(nn->f1);
    491                 //nn->f1 += 1.0f * (rndDouble(1) - 0.5f);
    492                 break;
    493         }
    494 }
     474void Geno_f4::connectionNodeChangeRandom(f4_Node *nn, int nn_index, int other_index) const
     475{
     476        // relative input connection to some existing neuron
     477        nn->conn_from = nn_index - other_index;
     478        //nn->conn_from = (int)(4.0f * (rndDouble(1) - 0.5f)); //in very old times - did not care about neuron input/output preferences
     479
     480        nn->conn_weight = GenoOperators::getMutatedNeuronConnectionWeight(nn->conn_weight);
     481}
     482
    495483
    496484// make a random : node
    497 void Geno_f4::nparNodeMakeRandom(f4_node *nn) const
    498 {
    499         int sign = (int)rndDouble(2);
    500         int param = (int)rndDouble(3);
    501         if (param > 2) param = 2;
    502         nn->l1 = sign;
    503         nn->i1 = "!=/"[param];
     485void Geno_f4::nparNodeMakeRandom(f4_Node *nn) const
     486{
     487        unsigned int prop = rndUint(3); //random neuron property
     488        nn->prop_symbol = "!=/"[prop];
     489        nn->prop_increase = rndUint(2) == 1;
    504490}
    505491
    506492// change a repeat # node
    507 void Geno_f4::repeatNodeChangeRandom(f4_node *nn) const
    508 {
    509         int count;
    510         float prob1;
    511 
    512         // change count
    513         count = nn->i1;
    514         prob1 = rndDouble(1);
    515         if (prob1 < 0.5f) count++;
    516         else count--;
    517         if (count < 1) count = 1;
    518         if (count > REP_MAXCOUNT) count = REP_MAXCOUNT;
    519         nn->i1 = count;
    520 }
    521 
    522 
    523 int Geno_f4::MutateOneValid(f4_node *& g, int &method) const
     493void Geno_f4::repeatNodeChangeRandom(f4_Node *nn) const
     494{
     495        if (rndDouble(1) < 0.5f) nn->reps++; else nn->reps--; // change count
     496        if (nn->reps < 1) nn->reps = 1;
     497        if (nn->reps > mut_max_rep) nn->reps = mut_max_rep;
     498}
     499
     500
     501int Geno_f4::MutateOneValid(f4_Node *& g, int &method) const
    524502// mutate one, until a valid genotype is obtained
    525503{
    526504        // ! the genotype is g->child (not g) !
    527505        int i, res;
    528         f4_node *gcopy = NULL;
    529         // try this max 20 times:
    530         //   copy, mutate, then validate
    531 
    532         for (i = 0; i < 20; i++)
     506        f4_Node *gcopy = NULL;
     507        const int TRY_MUTATE = 20;
     508        // try this at most TRY_MUTATE times: copy, mutate, then validate
     509        for (i = 0; i < TRY_MUTATE; i++)
    533510        {
    534511                gcopy = g->duplicate();
     
    569546int Geno_f4::mutate(char *& g, float & chg, int &method)
    570547{
    571         f4_node *root = new f4_node;
     548        f4_Node *root = new f4_Node;
    572549        if (f4_processrec(g, 0, root) || root->childCount() != 1)
    573550        {
     
    600577
    601578// convert to tree
    602 f4_node *root;
    603 root = new f4_node();
     579f4_Node *root;
     580root = new f4_Node();
    604581res = f4_processrec(g, 0, root);
    605582if (res) {
     
    654631
    655632
    656 int Geno_f4::CrossOverOne(f4_node *g1, f4_node *g2, float chg) const
     633int Geno_f4::CrossOverOne(f4_Node *g1, f4_Node *g2, float chg) const
    657634{
    658635        // ! the genotypes are g1->child and g2->child (not g1 g2) !
     
    660637        int smin, smax;
    661638        float size;
    662         f4_node *n1, *n2, *n1p, *n2p;
     639        f4_Node *n1, *n2, *n1p, *n2p;
    663640
    664641        // determine desired size
    665642        size = (1 - chg) * (float)g1->count();
    666         smin = (int)(size*0.9f - 1);
    667         smax = (int)(size*1.1f + 1);
     643        smin = (int)(size * 0.9f - 1);
     644        smax = (int)(size * 1.1f + 1);
    668645        // get a random node with desired size
    669646        n1 = g1->child->randomNodeWithSize(smin, smax);
     
    671648        // determine desired size
    672649        size = (1 - chg) * (float)g2->count();
    673         smin = (int)(size*0.9f - 1);
    674         smax = (int)(size*1.1f + 1);
     650        smin = (int)(size * 0.9f - 1);
     651        smax = (int)(size * 1.1f + 1);
    675652        // get a random node with desired size
    676653        n2 = g2->child->randomNodeWithSize(smin, smax);
     
    691668int Geno_f4::crossOver(char *&g1, char *&g2, float &chg1, float &chg2)
    692669{
    693         f4_node root1, root2, *copy1, *copy2;
     670        f4_Node root1, root2, *copy1, *copy2;
    694671
    695672        // convert genotype strings into tree structures
     
    720697        // style categories
    721698#define STYL4CAT_MODIFIC F14_MODIFIERS ","
    722 #define STYL4CAT_NEUMOD "[]:+-/!="
     699#define STYL4CAT_NEUMOD "/!="
    723700#define STYL4CAT_NEUSPECIAL "|@*"
    724 #define STYL4CAT_DIGIT "0123456789."
    725 #define STYL4CAT_REST "XN<># "
     701#define STYL4CAT_DIGIT "+-0123456789.[]" //'+' is only for adjusting old-style properties "/!="
     702#define STYL4CAT_REST ":XN<># "
    726703
    727704        if (!isalpha(ch) && !strchr(STYL4CAT_MODIFIC STYL4CAT_NEUMOD STYL4CAT_NEUSPECIAL STYL4CAT_DIGIT STYL4CAT_REST "\t", ch))
     
    730707        }
    731708        uint32_t style = GENSTYLE_CS(0, GENSTYLE_STRIKEOUT); //default, should be changed below
    732         if (strchr("X ", ch))                    style = GENSTYLE_CS(0, GENSTYLE_NONE);
    733         else if (strchr("N", ch))                style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_NONE);
     709        if (strchr("X", ch))                     style = GENSTYLE_CS(0, GENSTYLE_BOLD);
     710        else if (strchr(":", ch))                style = GENSTYLE_CS(0, GENSTYLE_NONE);
     711        else if (strchr("#", ch))                style = GENSTYLE_RGBS(220, 0, 0, GENSTYLE_BOLD);
     712        else if (strchr("/=!", ch))              style = GENSTYLE_RGBS(255, 140, 0, GENSTYLE_BOLD); //property... for now, f4 does not supoprt properties in general for any neuron class, like f1 does
     713        else if (strchr("N@|*", ch))             style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); //neuroclass
    734714        else if (strchr("<", ch))                style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD);
    735715        else if (strchr(">", ch))                style = GENSTYLE_RGBS(0, 0, 100, GENSTYLE_NONE);
    736         else if (strchr(STYL4CAT_DIGIT, ch))     style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
     716        else if (strchr(STYL4CAT_DIGIT, ch))     style = GENSTYLE_CS(GENCOLOR_NUMBER, GENSTYLE_NONE);
    737717        else if (strchr(STYL4CAT_MODIFIC, ch))   style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
    738718        else if (strchr(STYL4CAT_NEUMOD, ch))    style = GENSTYLE_RGBS(0, 150, 0, GENSTYLE_NONE);
    739         if (isalpha(ch) || strchr(STYL4CAT_NEUSPECIAL, ch))
    740         {
     719        if (isalpha(ch))
     720        {
     721                // allowed neuron formats:
     722                //   N:CLASSNAME
     723                //   N:@
     724                //   N:|
     725                // old syntax still supported in coloring, but no longer valid:
     726                //   [SENSOR, WEIGHT]
     727                //   N@
     728                //   N|
     729                // ...so must have N: or [ before neuroclass name (or just N, but this is handled above - for N@|* only)
     730
    741731                while (pos > 0)
    742732                {
    743733                        pos--;
    744                         if (!(isalpha(g[pos]) || strchr(STYL4CAT_NEUSPECIAL, ch)))
    745                         {
    746                                 if (isupper(g[pos + 1]) && (g[pos] == ':' || g[pos] == '[')) // name of neuron class
    747                                         style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_ITALIC);
    748                                 else // property
    749                                         style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
     734                        if (!isalpha(g[pos]))
     735                        {
     736                                if (isupper(g[pos + 1]) && (g[pos] == '[') || (g[pos] == ':' && pos > 0 && g[pos - 1] == 'N')) //we may have sequences like :-/:I (even though they are not valid) - in this example "I" should not be treated as neuron name, hence there must also be a "N" before ":"
     737                                        style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); // neuroclass
     738                                //(...) else (...)
     739                                //      style = GENSTYLE_RGBS(255, 140, 0, GENSTYLE_BOLD); // property - current f4 does not support neuron properties in a general case, only those old-style "/=!" as +! -! += -= +/ -/
     740                                break;
    750741                        }
    751742                }
  • cpp/frams/genetics/f4/f4_oper.h

    r779 r1227  
    11// This file is a part of Framsticks SDK.  http://www.framsticks.com/
    2 // Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
     2// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
    33// See LICENSE.txt for details.
    44
     
    3333//@}
    3434
     35/** @name Codes for specific F4_MOD neuron mutation subtypes (not included in mutation_method_names, all are considered F4_MOD there) */
     36//@{
     37#define F4_MODNEU_CONN     0
     38#define F4_MODNEU_WEIGHT   1
     39#define F4_MODNEU_COUNT    2
     40//@}
     41
    3542class Geno_f4 : public GenoOperators
    3643{
     
    4855        // mutation probabilities
    4956        double prob[F4_COUNT];            ///<relative probabilities of selecting mutation types in f4 genotype
    50         double probadd[F4_ADD_COUNT];     ///<relative probabilities of selecting mutation addition subtypes
     57        double probadd[F4_ADD_COUNT];     ///<relative probabilities of selecting addition mutation subtypes
     58        double probmodneu[F4_MODNEU_COUNT];     ///<relative probabilities of selecting neuron mutation subtypes
     59        paInt mut_max_rep;                ///maximum allowed number of repetitions for the '#' repetition gene
    5160
    5261        SString excluded_modifiers;       ///<Modifiers that are excluded in mutation process
     
    6372         * @return GENOOPER_OK if genotype is valid, GENOPER_REPAIR if genotype can be repaired, GENOPER_OPFAIL if genotype can't be repaired
    6473         */
    65         int  ValidateRec(f4_node *geno, int retrycount) const;
     74        int  ValidateRec(f4_Node *geno, int retrycount) const;
    6675
    6776        /**
     
    8695         * @return GENOPER_OK if mutation was performed successfully, GENOPER_FAIL otherwise
    8796         */
    88         int  MutateOne(f4_node *& g, int &method) const;
     97        int  MutateOne(f4_Node *& g, int &method) const;
    8998
    9099        /**
    91          * Creates a random connection to an existing neuron or creates an additional
     100         * Finds all neurons in g (in the order of ordNode()) and returns their neuroclasses in a vector.
     101         * Additionally, looks for the needle_neuron node and returns its index (in the list of the returned vector) as found_index,
     102         * or -1 if not found (for example, it was not a neuroclass node or not added to the "g" tree).
     103         * @param g root node
     104         * @param needle_neuron neuroclass node to look for in all nodes
     105         * @param found_index returned index of needle
     106         * @return all nodes that are neurons
     107         */
     108        static vector<NeuroClass*> findAllNeuronsAndNode(f4_Node * const & g, f4_Node* const &needle_neuron, int &found_index);
     109
     110        /**
     111         * Finds indexes of a given neuron and another random (output- or input-providing) neuron in the list of all neurons present in the "g" tree.
     112         * @param g root node
     113         * @param neuron neuroclass node to look for in all nodes in g
     114         * @param other_has_output if true, other neuron will provide output; otherwise, it will accept input(s)
     115         * @param neuron_index returned index of neuron
     116         * @param other_neuron_index returned index of a random neuron that provides an output or accepts inputs
     117         * @return true if succeeded, false otherwise
     118         */
     119        static bool findConnectionNeuronIndexes(f4_Node * const &g, f4_Node *neuron, bool other_has_output, int &neuron_index, int &other_neuron_index);
     120
     121        /**
     122         * Creates a random connection to an existing neuron and randomizes connection weight
    92123         * sensor for a neuron.
    93124         * @param nn neuron class node
    94          * @param neuid id of a neuron
    95          * @param neulist list of genotype neuron classes
     125         * @param nn_index index of the nn neuron
     126         * @param other_index index of the neuron providing output, to get input from
    96127         */
    97         void linkNodeMakeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const;
    98 
    99         /**
    100          * Changes connection to an existing neuron or creates an additional
    101          * sensor for neuron.
    102          * @param nn neuron connection node
    103          * @param neuid id of a neuron
    104          * @param neulist list of genotype neuron classes
    105          */
    106         void linkNodeChangeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const;
     128        void connectionNodeChangeRandom(f4_Node *nn, int nn_index, int other_index) const;
    107129
    108130        /**
     
    110132         * @param nn neuron node
    111133         */
    112         void nparNodeMakeRandom(f4_node *nn) const;
     134        void nparNodeMakeRandom(f4_Node *nn) const;
    113135
    114136        /**
     
    116138         * @param nn repetition node
    117139         */
    118         void repeatNodeChangeRandom(f4_node *nn) const;
     140        void repeatNodeChangeRandom(f4_Node *nn) const;
    119141
    120142        /**
     
    124146         * @return GENOPER_OK if performed successful mutation, GENOPER_FAIL otherwise
    125147         */
    126         int  MutateOneValid(f4_node *&g, int &method) const;
     148        int  MutateOneValid(f4_Node *&g, int &method) const;
    127149
    128150        /**
     
    134156         * @param chg percentage of the first parent in offspring (the second parent has the rest)
    135157         */
    136         int  CrossOverOne(f4_node *g1, f4_node *g2, float chg) const;
     158        int  CrossOverOne(f4_Node *g1, f4_Node *g2, float chg) const;
    137159};
    138160
Note: See TracChangeset for help on using the changeset viewer.