source: cpp/frams/genetics/f4/f4_general.cpp @ 1227

Last change on this file since 1227 was 1227, checked in by Maciej Komosinski, 12 months ago

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
  • Property svn:eol-style set to native
File size: 38.1 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
6// 2018, Grzegorz Latosinski, added support for new API for neuron types and their properties
7
8#include "f4_general.h"
9#include "../genooperators.h" //for GENOPER_ constants
10#include <common/nonstd_stl.h>
11#include <common/log.h>
12#include <frams/model/model.h> // for min and max attributes
13#include <common/nonstd_math.h>
14
15#ifdef DMALLOC
16#include <dmalloc.h>
17#endif
18
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
25void rolling_dec(double *v)
26{
27        *v -= 0.7853;  // 0.7853981  45 degrees
28}
29
30void rolling_inc(double *v)
31{
32        *v += 0.7853;  // 0.7853981  45 degrees
33}
34
35int scanrec(const char* s, size_t slen, char stopchar)
36{
37        size_t i = 0;
38        //DB( printf("    scan('%s', '%c')\n", s, stopchar); )
39        while (1)
40        {
41                if (i >= slen)  // ran out the string, should never happen with correct string
42                        return 1;
43                if (stopchar == s[i])  // bumped into stopchar
44                        return int(i);
45                if (i < slen - 1) // s[i] is not the last char
46                {
47                        if (s[i] == '(')
48                        {
49                                i += 2 + scanrec(s + i + 1, slen - i - 1, ')');
50                                continue;
51                        }
52                        if (s[i] == '<')
53                        {
54                                i += 2 + scanrec(s + i + 1, slen - i - 1, '>');
55                                continue;
56                        }
57                        if (s[i] == '#')
58                        {
59                                i += 2 + scanrec(s + i + 1, slen - i - 1, '>');
60                                continue;
61                        }
62                }
63                // s[i] a non-special character
64                i++;
65        }
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;
74        dadlink = ndad;
75        org = NULL;
76        genot = NULL;
77        gcur = NULL;
78        active = true;
79        repeat.clear();
80        //genoRange.clear(); -- implicit
81
82        anglepos = nangle;
83        commacount = 0;
84        childcount = 0;
85        P = newP;
86        rolling = 0;
87        xrot = 0;
88        zrot = 0;
89        //OM = Orient_1;
90        inertia = 0.8;
91        force = 0.04;
92        sigmo = 2;
93        conns_count = 0;
94
95        // adjust firstend and OM if there is a stick dad
96        if (ndad != NULL)
97        {
98                // make sure it is a stick (and not a stick f4_Cell!)
99                if (ndad->type == CELL_STICK)
100                {
101                        //firstend = ndad->lastend;
102                        //OM = ndad->OM;
103                        ndad->childcount++;
104                }
105                if (ndad->type == CELL_NEURON)
106                {
107                        inertia = ndad->inertia;
108                        force = ndad->force;
109                        sigmo = ndad->sigmo;
110                }
111        }
112        // adjust lastend
113        //lastend = firstend + ((Orient)OM * (Pt3D(1,0,0) * P.len));
114        mz = 1;
115}
116
117
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;
122        dadlink = ndad;
123        org = nO;
124        genot = ngeno;
125        gcur = ngcur;
126        active = true;
127        repeat.clear();
128        //genoRange.clear(); -- implicit
129        // preserve geno range of parent cell
130        if (NULL != ndad)
131                genoRange.add(ndad->genoRange);
132
133        anglepos = nangle;
134        commacount = 0;
135        childcount = 0;
136        P = newP;
137        rolling = 0;
138        xrot = 0;
139        zrot = 0;
140        //OM = Orient_1;
141        inertia = 0.8;
142        force = 0.04;
143        sigmo = 2;
144        conns_count = 0;
145
146        // adjust firstend and OM if there is a stick dad
147        if (ndad != NULL)
148        {
149                // make sure it is a stick (and not a stick f4_Cell!)
150                if (ndad->type == CELL_STICK)
151                {
152                        //firstend = ndad->lastend;
153                        //OM = ndad->OM;
154                        ndad->childcount++;
155                }
156                if (ndad->type == CELL_NEURON)
157                {
158                        inertia = ndad->inertia;
159                        force = ndad->force;
160                        sigmo = ndad->sigmo;
161                }
162        }
163        // adjust lastend
164        //lastend = firstend + ((Orient)OM * (Pt3D(1,0,0) * P.len));
165        mz = 1;
166}
167
168
169f4_Cell::~f4_Cell()
170{
171        // remove connections
172        if (conns_count)
173        {
174                int i;
175                for (i = conns_count - 1; i >= 0; i--)
176                        delete conns[i];
177                conns_count = 0;
178        }
179}
180
181
182/* return codes:
183        1 error at pos
184        0  halt development (yield) for a cycle
185        */
186int f4_Cell::oneStep()
187{
188        while (gcur != NULL)
189        {
190                //DB( printf("  %d (%d) executing '%c' %d\n", name, type, gcur->name, gcur->pos); )
191                // currently this is the last one processed
192                // the current genotype code is processed
193                //genoRange.add(gcur->pos,gcur->pos+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
203                {
204                        genoRange.add(gcur->pos, gcur->pos);
205                        char name = gcur->name[0];
206                        switch (name)
207                        {
208                        case '<':
209                        {
210                                // cell division!
211                                //DB( printf("  div! %d\n", name); )
212
213                                // error: sticks cannot divide
214                                if (type == CELL_STICK)
215                                {
216                                        // cannot fix
217                                        org->setError(gcur->pos);
218                                        return 1;  // stop
219                                }
220
221                                // undiff divides
222                                if (type == CELL_UNDIFF)
223                                {
224                                        // commacount is set only when daughter turns into X
225                                        // daughter cell
226                                        // adjust new len
227                                        GeneProps newP = P;
228                                        newP.propagateAlong(false);
229                                        f4_Cell *tmp = new f4_Cell(org, org->cell_count, genot, gcur->child2, this, commacount, newP);
230                                        tmp->repeat = repeat;
231                                        repeat.clear();
232                                        org->addCell(tmp);
233                                }
234                                // a neuron divides: create a new, duplicate connections
235                                if (type == CELL_NEURON)
236                                {
237                                        // daughter cell
238                                        f4_Cell *tmp = new f4_Cell(org, org->cell_count, genot, gcur->child2,
239                                                // has the same dadlink
240                                                this->dadlink, commacount, P);
241                                        tmp->repeat = repeat;
242                                        repeat.clear();
243                                        // it is a neuron from start
244                                        tmp->type = CELL_NEURON;
245                                        // it has the same type as the parent neuron
246                                        tmp->neuclass = neuclass;
247                                        // duplicate connections
248                                        f4_CellConn *conn;
249                                        for (int i = 0; i < conns_count; i++)
250                                        {
251                                                conn = conns[i];
252                                                tmp->addConnection(conn->from, conn->weight);
253                                        }
254                                        org->addCell(tmp);
255                                }
256                                // adjustments for this cell
257                                gcur = gcur->child;
258                                // halt development
259                                return 0;
260                        }
261                        case '>':
262                        {
263                                // finish
264                                // see if there is a repeat count
265                                if (repeat.top > 0)
266                                { // there is a repeat counter
267                                        if (!repeat.first()->isNull())
268                                        { // repeat counter is not null
269                                                repeat.first()->dec();
270                                                if (repeat.first()->count > 0)
271                                                {
272                                                        // return to repeat
273                                                        gcur = repeat.first()->node->child;
274                                                }
275                                                else
276                                                {
277                                                        // continue
278                                                        gcur = repeat.first()->node->child2;
279                                                        repeat.pop();
280                                                }
281                                                break;
282                                        }
283                                        else
284                                        {
285                                                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
294                                        }
295                                }
296                                else
297                                {
298                                        // error: still undiff
299                                        if (type == CELL_UNDIFF)
300                                        {
301                                                // fix it: insert an 'X'
302                                                f4_Node *insertnode = new f4_Node("X", NULL, gcur->pos);
303                                                if (org->setRepairInsert(gcur->pos, gcur, insertnode)) // not in repair mode, release
304                                                        delete insertnode;
305                                                return 1;
306                                        }
307                                        repeat.clear();
308                                        active = false;  // stop
309                                        // 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...
313                                        gcur = NULL;
314                                        return 0;
315                                }
316                        }
317#ifndef BREAK_WHEN_REP_COUNTER_NULL
318                        [[fallthrough]];
319#endif
320                        case '#':
321                        {
322                                // repetition marker
323                                if (repeat.top >= repeat_stack::stackSize)
324                                {
325                                        // repeat pointer stack is full, cannot remember this one.
326                                        // fix: delete it
327                                        org->setRepairRemove(gcur->pos, gcur);
328                                        return 1;  // stop
329                                }
330                                repeat.push(repeat_ptr(gcur, gcur->reps));
331                                gcur = gcur->child;
332                                break;
333                        }
334                        case ',':
335                        {
336                                commacount++;
337                                gcur = gcur->child;
338                                break;
339                        }
340                        case 'r':  case 'R':
341                        {
342                                // error: if neuron
343                                if (type == CELL_NEURON)
344                                {
345                                        // fix: delete it
346                                        org->setRepairRemove(gcur->pos, gcur);
347                                        return 1;  // stop
348                                }
349                                switch (name)
350                                {
351                                case 'r':   rolling_dec(&rolling); break;
352                                case 'R':   rolling_inc(&rolling); break;
353                                }
354                                gcur = gcur->child;
355                                break;
356                        }
357                        case 'l':  case 'L':
358                        case 'c':  case 'C':
359                        case 'q':  case 'Q':
360                        case 'a':  case 'A':
361                        case 'i':  case 'I':
362                        case 's':  case 'S':
363                        case 'm':  case 'M':
364                        case 'f':  case 'F':
365                        case 'w':  case 'W':
366                        case 'e':  case 'E':
367                        case 'd':  case 'D':
368                        case 'g':  case 'G':
369                        case 'b':  case 'B':
370                        case 'h':  case 'H':
371                        {
372                                // error: if neuron
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);
376                                        // fix: delete it
377                                        org->setRepairRemove(gcur->pos, gcur);
378                                        return 1;  // stop
379                                }
380                                P.executeModifier(name);
381                                gcur = gcur->child;
382                                break;
383                        }
384                        case 'X':
385                        {
386                                // turn undiff. cell into a stick
387                                // error: already differentiated
388                                if (type != CELL_UNDIFF)
389                                {
390                                        // fix: delete this node
391                                        org->setRepairRemove(gcur->pos, gcur);
392                                        return 1;  // stop
393                                }
394                                type = CELL_STICK;
395                                // fix dad commacount and own anglepos
396                                if (NULL != dadlink)
397                                {
398                                        dadlink->commacount++;
399                                        anglepos = dadlink->commacount;
400                                }
401                                // change of type halts developments, see comment at 'neuclasshandler' below
402                                gcur = gcur->child;
403                                return 0;
404                        }
405                        case '[':
406                        {
407                                // connection to neuron
408                                // error: not a neuron
409                                if (type != CELL_NEURON)
410                                {
411                                        // fix: delete it
412                                        org->setRepairRemove(gcur->pos, gcur);
413                                        return 1;  // stop
414                                }
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                                }
454                                gcur = gcur->child;
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                                }
485                                else
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)
502                                {
503                                        // fix: delete it
504                                        org->setRepairRemove(gcur->pos, gcur);
505                                        return 1;  // stop
506                                }
507                                switch (gcur->prop_symbol)
508                                {
509                                case '!':
510                                        if (gcur->prop_increase)
511                                                force += (1.0 - force) * 0.2;
512                                        else
513                                                force -= force * 0.2;
514                                        break;
515                                case '=':
516                                        if (gcur->prop_increase)
517                                                inertia += (1.0 - inertia) * 0.2;
518                                        else
519                                                inertia -= inertia * 0.2;
520                                        break;
521                                case '/':
522                                        if (gcur->prop_increase)
523                                                sigmo *= 1.4;
524                                        else
525                                                sigmo /= 1.4;
526                                        break;
527                                default:
528                                        org->setRepairRemove(gcur->pos, gcur);
529                                        return 1;  // stop
530                                }
531                                gcur = gcur->child;
532                                break;
533                        }
534                        case ' ':
535                        {
536                                // space has no effect, should not occur
537                                // fix: delete it
538                                org->setRepairRemove(gcur->pos, gcur);
539                                gcur = gcur->child;
540                                break;
541                        }
542                        default:
543                        {
544                                // because there are one-character neuron classes, default is move control to neuclasshandler
545                                neuclasshandler = true;
546                        }
547                        }
548                }
549                else
550                {
551                        // if many characters or single character but is_neuroclass, then it will be handled below
552                        neuclasshandler = true;
553                }
554
555                if (neuclasshandler)
556                {
557                        genoRange.add(gcur->pos, gcur->pos + int(gcur->name.length()) + 2 - 1); // +2 for N:
558                        if (type != CELL_UNDIFF)
559                        {
560                                // fix: delete this node
561                                org->setRepairRemove(gcur->pos, gcur);
562                                return 1;  // stop
563                        }
564                        // error: if no previous
565                        if (dadlink == NULL)
566                        {
567                                // fix: delete it
568                                org->setRepairRemove(gcur->pos, gcur);
569                                return 1;  // stop
570                        }
571                        // multiple characters are neuron types. Let's check if exists in the current configuration of Framsticks
572                        char *temp = (char*)gcur->name.c_str();
573                        neuclass = GenoOperators::parseNeuroClass(temp, ModelEnum::SHAPETYPE_BALL_AND_STICK);
574                        if (neuclass == NULL)
575                        {
576                                // error: unknown code
577                                string buf = "Unknown code '" + gcur->name + "'";
578                                logMessage("f4_Cell", "oneStep", LOG_ERROR, buf.c_str());
579                                org->setRepairRemove(gcur->pos, gcur);
580                                return 1;
581                        }
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
586                        gcur = gcur->child;
587                        return 0; //stop
588                }
589        }
590        active = false;  // done
591        return 0;
592}
593
594
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++;
602        return 0;
603}
604
605
606void f4_Cell::adjustRec()
607{
608        //f4_OrientMat rot;
609        int i;
610
611        if (recProcessedFlag)
612                // already processed
613                return;
614
615        // mark it processed
616        recProcessedFlag = 1;
617
618        // make sure its parent is processed first
619        if (dadlink != NULL)
620                dadlink->adjustRec();
621
622        // count children
623        childcount = 0;
624        for (i = 0; i < org->cell_count; i++)
625        {
626                if (org->C[i]->dadlink == this)
627                        if (org->C[i]->type == CELL_STICK)
628                                childcount++;
629        }
630
631        if (type == CELL_STICK)
632        {
633                if (dadlink == NULL)
634                {
635                        //firstend = Pt3D_0;
636                        // rotation due to rolling
637                        xrot = rolling;
638                        mz = 1;
639                }
640                else
641                {
642                        //firstend = dadlink->lastend;
643                        GeneProps Pdad = dadlink->P;
644                        GeneProps Padj = Pdad;
645                        Padj.propagateAlong(false);
646
647                        //rot = Orient_1;
648
649                        // rotation due to rolling
650                        xrot = rolling +
651                                // rotation due to twist
652                                Pdad.twist;
653                        if (dadlink->commacount <= 1)
654                        {
655                                // rotation due to curvedness
656                                zrot = Padj.curvedness;
657                        }
658                        else
659                        {
660                                zrot = Padj.curvedness + (anglepos * 1.0 / (dadlink->commacount + 1) - 0.5) * M_PI * 2.0;
661                        }
662
663                        //rot = rot * f4_OrientMat(yOz, xrot);
664                        //rot = rot * f4_OrientMat(xOy, zrot);
665                        // rotation relative to parent stick
666                        //OM = rot * OM;
667
668                        // rotation in world coordinates
669                        //OM =  ((f4_OrientMat)dadlink->OM) * OM;
670                        mz = dadlink->mz / dadlink->childcount;
671                }
672                //Pt3D lastoffset = (Orient)OM * (Pt3D(1,0,0)*P.len);
673                //lastend = firstend + lastoffset;
674        }
675}
676
677
678
679f4_CellConn::f4_CellConn(f4_Cell *nfrom, double nweight)
680{
681        from = nfrom;
682        weight = nweight;
683}
684
685
686
687f4_Cells::f4_Cells(f4_Node *genome, int nrepair)
688{
689        // create ancestor cell
690        repair = nrepair;
691        errorcode = GENOPER_OK;
692        errorpos = -1;
693        repair_remove = NULL;
694        repair_parent = NULL;
695        repair_insert = NULL;
696        tmpcel = NULL;
697        f4rootnode = NULL;
698        C[0] = new f4_Cell(this, 0, genome, genome, NULL, 0, GeneProps::standard_values);
699        cell_count = 1;
700}
701
702
703f4_Cells::f4_Cells(SString & genome, int nrepair)
704{
705        int res;
706        repair = nrepair;
707        errorcode = GENOPER_OK;
708        errorpos = -1;
709        repair_remove = NULL;
710        repair_parent = NULL;
711        repair_insert = NULL;
712        tmpcel = NULL;
713        f4rootnode = NULL;
714
715        // transform geno from string to nodes
716        f4rootnode = new f4_Node();
717        res = f4_processrec(genome.c_str(), 0, f4rootnode);
718        if ((res < 0) || (1 != f4rootnode->childCount()))
719        {
720                errorcode = GENOPER_OPFAIL;
721                errorpos = -1;
722        }
723
724        // create ancestor cell
725        C[0] = new f4_Cell(this, 0, f4rootnode->child, f4rootnode->child, NULL, 0, GeneProps::standard_values);
726        cell_count = 1;
727}
728
729f4_Cells::~f4_Cells()
730{
731        // release cells
732        int i;
733        if (cell_count)
734        {
735                for (i = cell_count - 1; i >= 0; i--)
736                        delete C[i];
737                cell_count = 0;
738        }
739        if (f4rootnode)
740                delete f4rootnode;
741}
742
743
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)
751                {
752                        // error
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
758                if (C[i]->active)
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;
761}
762
763
764int f4_Cells::simulate()
765{
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;
786
787        // fix neuron attachements
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)
793                        {
794                                C[i]->dadlink = C[i]->dadlink->dadlink;
795                        }
796                }
797        }
798
799        // there should be no undiff. cells
800        // make undifferentiated cells sticks
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();
807                }
808        }
809
810        // recursive adjust
811        // reset recursive traverse flags
812        for (int i = 0; i < cell_count; i++)
813                C[i]->recProcessedFlag = 0;
814        // process every cell
815        for (int i = 0; i < cell_count; i++)
816                C[i]->adjustRec();
817
818        //DB( printf("Cell simulation done, %d cells. \n", nc); )
819
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");
849}
850
851
852void f4_Cells::addCell(f4_Cell *newcell)
853{
854        if (cell_count >= F4_MAX_CELLS - 1)
855        {
856                delete newcell;
857                return;
858        }
859        C[cell_count] = newcell;
860        cell_count++;
861}
862
863
864void f4_Cells::setError(int nerrpos)
865{
866        errorcode = GENOPER_OPFAIL;
867        errorpos = nerrpos;
868}
869
870void f4_Cells::setRepairRemove(int nerrpos, f4_Node *rem)
871{
872        if (!repair)
873        {
874                // not in repair mode, treat as repairable error
875                errorcode = GENOPER_REPAIR;
876                errorpos = nerrpos;
877        }
878        else
879        {
880                errorcode = GENOPER_REPAIR;
881                errorpos = nerrpos;
882                repair_remove = rem;
883        }
884}
885
886int f4_Cells::setRepairInsert(int nerrpos, f4_Node *parent, f4_Node *insert)
887{
888        if (!repair)
889        {
890                // not in repair mode, treat as repairable error
891                errorcode = GENOPER_REPAIR;
892                errorpos = nerrpos;
893                return -1;
894        }
895        else
896        {
897                errorcode = GENOPER_REPAIR;
898                errorpos = nerrpos;
899                repair_parent = parent;
900                repair_insert = insert;
901                return 0;
902        }
903}
904
905void f4_Cells::repairGeno(f4_Node *geno, int whichchild)
906{
907        // assemble repaired geno, if the case
908        if (!repair) return;
909        if ((repair_remove == NULL) && (repair_insert == NULL)) return;
910        // traverse genotype tree, remove / insert node
911        f4_Node *g2;
912        if (whichchild == 1)
913                g2 = geno->child;
914        else
915                g2 = geno->child2;
916        if (g2 == NULL)
917                return;
918        if (g2 == repair_remove)
919        {
920                f4_Node *oldgeno;
921                geno->removeChild(g2);
922                if (g2->child)
923                {
924                        // add g2->child as child to geno
925                        if (whichchild == 1)
926                                geno->child = g2->child;
927                        else
928                                geno->child2 = g2->child;
929                        g2->child->parent = geno;
930                }
931                oldgeno = g2;
932                oldgeno->child = NULL;
933                delete oldgeno;
934                if (geno->child == NULL) return;
935                // check this new
936                repairGeno(geno, whichchild);
937                return;
938        }
939        if (g2 == repair_parent)
940        {
941                geno->removeChild(g2);
942                geno->addChild(repair_insert);
943                repair_insert->parent = geno;
944                repair_insert->child = g2;
945                repair_insert->child2 = NULL;
946                g2->parent = repair_insert;
947        }
948        // recurse
949        if (g2->child)  repairGeno(g2, 1);
950        if (g2->child2) repairGeno(g2, 2);
951}
952
953
954void f4_Cells::toF1Geno(SString &out)
955{
956        if (tmpcel) delete tmpcel;
957        tmpcel = new f4_Cell(-1, NULL, 0, GeneProps::standard_values);
958        out = "";
959        toF1GenoRec(0, out);
960        delete tmpcel;
961}
962
963
964void f4_Cells::toF1GenoRec(int curc, SString &out)
965{
966        int i, j, ccount;
967        f4_Cell *thisti;
968        f4_Cell *thneu;
969        char buf[200];
970
971        if (curc >= cell_count) return;
972
973        if (C[curc]->type != CELL_STICK) return;
974
975        thisti = C[curc];
976        if (thisti->dadlink != NULL)
977                *tmpcel = *(thisti->dadlink);
978
979        // adjust length, curvedness, etc.
980        tmpcel->P.propagateAlong(false);
981        while (tmpcel->P.length > thisti->P.length)
982        {
983                tmpcel->P.executeModifier('l');
984                out += "l";
985        }
986        while (tmpcel->P.length < thisti->P.length)
987        {
988                tmpcel->P.executeModifier('L');
989                out += "L";
990        }
991        while (tmpcel->P.curvedness > thisti->P.curvedness)
992        {
993                tmpcel->P.executeModifier('c');
994                out += "c";
995        }
996        while (tmpcel->P.curvedness < thisti->P.curvedness)
997        {
998                tmpcel->P.executeModifier('C');
999                out += "C";
1000        }
1001        while (thisti->rolling > 0.0f)
1002        {
1003                rolling_dec(&(thisti->rolling));
1004                out += "R";
1005        }
1006        while (thisti->rolling < 0.0f)
1007        {
1008                rolling_inc(&(thisti->rolling));
1009                out += "r";
1010        }
1011
1012        // output X for this stick
1013        out += "X";
1014
1015        // neurons attached to it
1016        for (i = 0; i < cell_count; i++)
1017        {
1018                if (C[i]->type == CELL_NEURON)
1019                {
1020                        if (C[i]->dadlink == thisti)
1021                        {
1022                                thneu = C[i];
1023                                out += "[";
1024                                // ctrl
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++)
1030                                {
1031                                        if (j) out += ",";
1032                                        sprintf(buf, "%d", thneu->conns[j]->from->nr - thneu->nr);
1033                                        out += buf;
1034                                        out += ":";
1035                                        // connection weight
1036                                        sprintf(buf, "%g", thneu->conns[j]->weight);
1037                                        out += buf;
1038                                }
1039                                out += "]";
1040                        }
1041                }
1042        }
1043
1044        // sticks connected to it
1045        if (thisti->commacount >= 2)
1046                out += "(";
1047
1048        ccount = 1;
1049        for (i = 0; i < cell_count; i++)
1050        {
1051                if (C[i]->type == CELL_STICK)
1052                {
1053                        if (C[i]->dadlink == thisti)
1054                        {
1055                                while (ccount < (C[i])->anglepos)
1056                                {
1057                                        ccount++;
1058                                        out += ",";
1059                                }
1060                                toF1GenoRec(i, out);
1061                        }
1062                }
1063        }
1064
1065        while (ccount < thisti->commacount)
1066        {
1067                ccount++;
1068                out += ",";
1069        }
1070
1071        if (thisti->commacount >= 2)
1072                out += ")";
1073}
1074
1075
1076
1077// to organize an f4 genotype in a tree structure
1078
1079f4_Node::f4_Node()
1080{
1081        name = "?";
1082        parent = NULL;
1083        child = NULL;
1084        child2 = NULL;
1085        pos = -1;
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)
1096{
1097        name = nname;
1098        parent = nparent;
1099        child = NULL;
1100        child2 = NULL;
1101        pos = npos;
1102        if (parent) parent->addChild(this);
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)
1113{
1114        name = nname;
1115        parent = nparent;
1116        child = NULL;
1117        child2 = NULL;
1118        pos = npos;
1119        if (parent) parent->addChild(this);
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)
1148        {
1149                child = nchi;
1150                return 0;
1151        }
1152        if (child2 == NULL)
1153        {
1154                child2 = nchi;
1155                return 0;
1156        }
1157        return -1;
1158}
1159
1160int f4_Node::removeChild(f4_Node *nchi)
1161{
1162        if (nchi == child2)
1163        {
1164                child2 = NULL;
1165                return 0;
1166        }
1167        if (nchi == child)
1168        {
1169                child = NULL;
1170                return 0;
1171        }
1172        return -1;
1173}
1174
1175int f4_Node::childCount()
1176{
1177        if (child != NULL)
1178        {
1179                if (child2 != NULL) return 2;
1180                else return 1;
1181        }
1182        else
1183        {
1184                if (child2 != NULL) return 1;
1185                else return 0;
1186        }
1187}
1188
1189int f4_Node::count() const
1190{
1191        int c = 1;
1192        if (child != NULL)  c += child->count();
1193        if (child2 != NULL) c += child2->count();
1194        return c;
1195}
1196
1197f4_Node* f4_Node::ordNode(int n)
1198{
1199        int n1;
1200        if (n == 0) return this;
1201        n--;
1202        if (child != NULL)
1203        {
1204                n1 = child->count();
1205                if (n < n1) return child->ordNode(n);
1206                n -= n1;
1207        }
1208        if (child2 != NULL)
1209        {
1210                n1 = child2->count();
1211                if (n < n1) return child2->ordNode(n);
1212                n -= n1;
1213        }
1214        return NULL;
1215}
1216
1217f4_Node* f4_Node::randomNode()
1218{
1219        int n = count();
1220        // pick a random node, between 0 and n-1
1221        return ordNode(rndUint(n));
1222}
1223
1224f4_Node* f4_Node::randomNodeWithSize(int mn, int mx)
1225{
1226        // try random nodes, and accept if size in range
1227        // limit to maxlim tries
1228        int i, n, maxlim;
1229        f4_Node *nod = NULL;
1230        maxlim = count();
1231        for (i = 0; i < maxlim; i++)
1232        {
1233                nod = randomNode();
1234                n = nod->count();
1235                if ((n >= mn) && (n <= mx)) return nod;
1236        }
1237        // failed, doesn't matter
1238        return nod;
1239}
1240
1241void f4_Node::sprint(SString& out)
1242{
1243        char buf2[20];
1244        // special case: repetition code
1245        if (name == "#")
1246        {
1247                out += "#";
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);
1257                        out += buf2;
1258                        sprintf(buf2, ":%g]", conn_weight);
1259                        out += buf2;
1260                }
1261                else if (name == ":")
1262                {
1263                        sprintf(buf2, ":%c%c:", prop_increase ? '+' : '-', prop_symbol);
1264                        out += buf2;
1265                }
1266                else if (neuclass != NULL)
1267                {
1268                        out += "N:";
1269                        out += neuclass->name.c_str();
1270                }
1271                else
1272                {
1273                        out += name.c_str();
1274                }
1275        }
1276
1277        if (child != NULL)
1278                child->sprint(out);
1279        // if two children, make sure last char is a '>'
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);
1286        // make sure last char is a '>'
1287        if (out[0] == 0) out += ">"; else
1288                if (out[out.length() - 1] != '>') out += ">";
1289}
1290
1291void f4_Node::sprintAdj(char *& buf)
1292{
1293        unsigned int len;
1294        // build in a SString, with initial size
1295        SString out;
1296        out.reserve(int(strlen(buf)) + 2000);
1297
1298        sprint(out);
1299
1300        // very last '>' can be omitted
1301        len = out.length();
1302        if (len > 1)
1303                if (out[len - 1] == '>') { (out.directWrite())[len - 1] = 0; out.endWrite(); };
1304        // copy back to string
1305        // if new is longer, reallocate buf
1306        if (len + 1 > strlen(buf))
1307        {
1308                buf = (char*)realloc(buf, len + 1);
1309        }
1310        strcpy(buf, out.c_str());
1311}
1312
1313f4_Node* f4_Node::duplicate()
1314{
1315        f4_Node *copy;
1316        copy = new f4_Node(*this);
1317        copy->parent = NULL;  // set later
1318        copy->child = NULL;
1319        copy->child2 = NULL;
1320        if (child != NULL)
1321        {
1322                copy->child = child->duplicate();
1323                copy->child->parent = copy;
1324        }
1325        if (child2 != NULL)
1326        {
1327                copy->child2 = child2->duplicate();
1328                copy->child2->parent = copy;
1329        }
1330        return copy;
1331}
1332
1333
1334void f4_Node::destroy()
1335{
1336        // children are destroyed (recursively) through the destructor
1337        if (child2 != NULL) delete child2;
1338        if (child != NULL) delete child;
1339}
1340
1341// scan genotype string and build tree
1342// return >1 for error (errorpos)
1343int f4_processrec(const char* genot, unsigned pos0, f4_Node *parent)
1344{
1345        int i, res;
1346        unsigned gpos, oldpos;
1347        f4_Node *node1, *par;
1348        unsigned beginindex;
1349
1350        gpos = pos0;
1351        par = parent;
1352        if (gpos >= strlen(genot)) return 1;
1353        while (gpos < strlen(genot))
1354        {
1355                // first switch across cell dividers and old semantics
1356                switch (genot[gpos])
1357                {
1358                case '<':
1359                {
1360                        // find out genotype start for child
1361                        int j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>');
1362
1363                        node1 = new f4_Node("<", par, gpos);
1364                        par = node1;
1365                        res = f4_processrec(genot, gpos + 1, par);
1366                        if (res) return res;
1367                        if (gpos + j + 2 < strlen(genot))
1368                        {
1369                                res = f4_processrec(genot, gpos + j + 2, par);
1370                                if (res) return res;
1371                        }
1372                        else // ran out
1373                        {
1374                                node1 = new f4_Node(">", par, int(strlen(genot)) - 1);
1375                                par = node1;
1376                        }
1377                        gpos++;
1378                        return 0;  // OK
1379                }
1380                case '>':
1381                {
1382                        node1 = new f4_Node(">", par, gpos);
1383                        par = node1;
1384                        gpos = (unsigned int)strlen(genot);
1385                        return 0;  // OK
1386                }
1387                case '#':
1388                {
1389                        // repetition marker, 1 by default
1390                        ExtValue val;
1391                        const char* end = val.parseNumber(genot + gpos + 1, ExtPType::TInt);
1392                        if (end == NULL) i = 1;
1393                        else i = val.getInt();
1394                        // find out genotype start for continuation
1395                        int j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>');
1396                        // skip number
1397                        oldpos = gpos;
1398                        gpos += end - (genot + gpos);
1399                        //gpos++;
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;
1403                        par = node1;
1404                        res = f4_processrec(genot, gpos, node1);
1405                        if (res) return res;
1406                        if (oldpos + j + 2 < strlen(genot))
1407                        {
1408                                res = f4_processrec(genot, oldpos + j + 2, node1);
1409                                if (res) return res;
1410                        }
1411                        else // ran out
1412                        {
1413                                node1 = new f4_Node(">", par, int(strlen(genot)) - 1);
1414                        }
1415                        return 0;  // OK
1416                }
1417                case ' ':
1418                case '\n':
1419                case '\r':
1420                case '\t':
1421                {
1422                        // whitespace: ignore
1423                        gpos++;
1424                        break;
1425                }
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);
1489                        par = node1;
1490                        gpos++;
1491                        break;
1492                }
1493                }
1494        }
1495
1496        // should end with a '>'
1497        if (par)
1498        {
1499                if (par->name != ">")
1500                {
1501                        node1 = new f4_Node('>', par, int(strlen(genot)) - 1);
1502                        par = node1;
1503                }
1504        }
1505
1506        return 0;
1507}
1508
1509const char* parseConnection(const char *fragm, int& relfrom, double &weight)
1510{
1511        const char *parser = fragm;
1512        if (*parser != '[') return NULL;
1513        parser++;
1514        ExtValue val;
1515        parser = val.parseNumber(parser, ExtPType::TInt);
1516        if (parser == NULL) return NULL;
1517        relfrom = val.getInt();
1518        if (*parser != ':') return NULL;
1519        parser++;
1520        parser = val.parseNumber(parser, ExtPType::TDouble);
1521        if (parser == NULL) return NULL;
1522        weight = val.getDouble();
1523        if (*parser != ']') return NULL;
1524        parser++;
1525        return parser;
1526}
1527
1528f4_Node* f4_processtree(const char* geno)
1529{
1530        f4_Node *root = new f4_Node();
1531        int res = f4_processrec(geno, 0, root);
1532        if (res) return NULL;
1533        //DB( printf("test f4  "); )
1534        DB(
1535                if (root->child)
1536                {
1537                        char* buf = (char*)malloc(300);
1538                        DB(printf("(%d) ", root->child->count());)
1539                                buf[0] = 0;
1540                        root->child->sprintAdj(buf);
1541                        DB(printf("%s\n", buf);)
1542                                free(buf);
1543                }
1544        )
1545                return root->child;
1546}
Note: See TracBrowser for help on using the repository browser.