Ignore:
Timestamp:
04/27/23 04:04:06 (17 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
File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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  "); )
Note: See TracChangeset for help on using the changeset viewer.