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

Last change on this file since 1228 was 1228, checked in by Maciej Komosinski, 20 months ago

Fixed a bug where an f4_Node tree that resulted from an f4 genotype that was not properly/completely parsed due to some error would still be used to try growing an organism

  • Property svn:eol-style set to native
File size: 38.3 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 scanRecur(const char* s, int slen, char stopchar)
36{
37        int 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 a 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 + scanRecur(s + i + 1, slen - i - 1, ')');
50                                continue;
51                        }
52                        if (s[i] == '<')
53                        {
54                                i += 2 + scanRecur(s + i + 1, slen - i - 1, '>');
55                                continue;
56                        }
57                        if (s[i] == '#')
58                        {
59                                i += 2 + scanRecur(s + i + 1, slen - i - 1, '>');
60                                continue;
61                        }
62                }
63                // s[i] is a non-special character
64                i++;
65        }
66        return 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        repair = nrepair;
706        errorcode = GENOPER_OK;
707        errorpos = -1;
708        repair_remove = NULL;
709        repair_parent = NULL;
710        repair_insert = NULL;
711        tmpcel = NULL;
712        f4rootnode = NULL;
713
714        // transform geno from string to nodes
715        f4rootnode = new f4_Node();
716        int res = f4_processRecur(genome.c_str(), 0, f4rootnode);
717        if (res || (f4rootnode->childCount() != 1))
718        {
719                errorcode = GENOPER_OPFAIL;
720                errorpos = -1;
721        }
722
723        // create ancestor cell
724        C[0] = new f4_Cell(this, 0, f4rootnode->child, f4rootnode->child, NULL, 0, GeneProps::standard_values);
725        cell_count = 1;
726}
727
728f4_Cells::~f4_Cells()
729{
730        // release cells
731        int i;
732        if (cell_count)
733        {
734                for (i = cell_count - 1; i >= 0; i--)
735                        delete C[i];
736                cell_count = 0;
737        }
738        if (f4rootnode)
739                delete f4rootnode;
740}
741
742
743bool f4_Cells::oneStep()
744{
745        int old_cell_count = cell_count; //cell_count may change in the loop as new cells may be appended because cells may be dividing
746        for (int i = 0; i < old_cell_count; i++)
747        {
748                int cellstep_ret = C[i]->oneStep(); //keeps calling independently of C[i]->active
749                if (cellstep_ret > 0)
750                {
751                        // error
752                        C[i]->active = false;  // stop
753                        return false;
754                }
755        }
756        for (int i = 0; i < cell_count; i++) //we check all cells, including newly created ones
757                if (C[i]->active)
758                        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.
759        return false;
760}
761
762
763int f4_Cells::simulate()
764{
765        constexpr bool print_debugging = false; //print the state of cells during development
766        errorcode = GENOPER_OK;
767
768        for (int i = 0; i < cell_count; i++)  C[i]->active = true;
769
770        if (print_debugging) f4_Node::print_tree(C[0]->genot, 0);
771        if (print_debugging) print_cells("Initialization");
772
773        // execute oneStep() in a cycle
774        while (oneStep()) if (print_debugging) print_cells("Development step");
775        if (print_debugging) print_cells("After last development step");
776
777#ifdef EXTRA_STEP_CELL_DEVELOPMENT
778        if (errorcode == GENOPER_OK)
779        {
780                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.
781        }
782#endif
783
784        if (errorcode != GENOPER_OK) return errorcode;
785
786        // fix neuron attachements
787        for (int i = 0; i < cell_count; i++)
788        {
789                if (C[i]->type == CELL_NEURON)
790                {
791                        while (C[i]->dadlink->type == CELL_NEURON)
792                        {
793                                C[i]->dadlink = C[i]->dadlink->dadlink;
794                        }
795                }
796        }
797
798        // there should be no undiff. cells
799        // make undifferentiated cells sticks
800        for (int i = 0; i < cell_count; i++)
801        {
802                if (C[i]->type == CELL_UNDIFF)
803                {
804                        C[i]->type = CELL_STICK;
805                        //setError();
806                }
807        }
808
809        // recursive adjust
810        // reset recursive traverse flags
811        for (int i = 0; i < cell_count; i++)
812                C[i]->recProcessedFlag = 0;
813        // process every cell
814        for (int i = 0; i < cell_count; i++)
815                C[i]->adjustRec();
816
817        //DB( printf("Cell simulation done, %d cells. \n", nc); )
818
819        if (print_debugging) print_cells("Final");
820
821        return errorcode;
822}
823
824
825void f4_Cells::print_cells(const char* description)
826{
827        printf("------ %-55s ------ errorcode=%d, errorpos=%d\n", description, getErrorCode(), getErrorPos());
828        for (int i = 0; i < cell_count; i++)
829        {
830                f4_Cell *c = C[i];
831                string type;
832                switch (c->type)
833                {
834                case CELL_UNDIFF: type = "undiff"; break;
835                case CELL_STICK:  type = "STICK"; break;
836                case CELL_NEURON: type = string("NEURON:") + c->neuclass->name.c_str(); break;
837                default: type = std::to_string(c->type);
838                }
839                const char *status = c->active ? "active" : (c->gcur != NULL ? "yielding" : ""); //yielding = not active but waiting for other cells
840                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");
841                if (c->gcur && c->gcur->name == "[")
842                        printf("\tfrom=%d  weight=%g", c->gcur->conn_from, c->gcur->conn_weight);
843                printf("\n");
844                for (int l = 0; l < c->conns_count; l++)
845                        printf("\tconn:%d from=%d weight=%g\n", l, c->conns[l]->from->nr, c->conns[l]->weight);
846        }
847        printf("\n");
848}
849
850
851void f4_Cells::addCell(f4_Cell *newcell)
852{
853        if (cell_count >= F4_MAX_CELLS - 1)
854        {
855                delete newcell;
856                return;
857        }
858        C[cell_count] = newcell;
859        cell_count++;
860}
861
862
863void f4_Cells::setError(int nerrpos)
864{
865        errorcode = GENOPER_OPFAIL;
866        errorpos = nerrpos;
867}
868
869void f4_Cells::setRepairRemove(int nerrpos, f4_Node *rem)
870{
871        if (!repair)
872        {
873                // not in repair mode, treat as repairable error
874                errorcode = GENOPER_REPAIR;
875                errorpos = nerrpos;
876        }
877        else
878        {
879                errorcode = GENOPER_REPAIR;
880                errorpos = nerrpos;
881                repair_remove = rem;
882        }
883}
884
885int f4_Cells::setRepairInsert(int nerrpos, f4_Node *parent, f4_Node *insert)
886{
887        if (!repair)
888        {
889                // not in repair mode, treat as repairable error
890                errorcode = GENOPER_REPAIR;
891                errorpos = nerrpos;
892                return -1;
893        }
894        else
895        {
896                errorcode = GENOPER_REPAIR;
897                errorpos = nerrpos;
898                repair_parent = parent;
899                repair_insert = insert;
900                return 0;
901        }
902}
903
904void f4_Cells::repairGeno(f4_Node *geno, int whichchild)
905{
906        // assemble repaired geno, if the case
907        if (!repair) return;
908        if ((repair_remove == NULL) && (repair_insert == NULL)) return;
909        // traverse genotype tree, remove / insert node
910        f4_Node *g2;
911        if (whichchild == 1)
912                g2 = geno->child;
913        else
914                g2 = geno->child2;
915        if (g2 == NULL)
916                return;
917        if (g2 == repair_remove)
918        {
919                f4_Node *oldgeno;
920                geno->removeChild(g2);
921                if (g2->child)
922                {
923                        // add g2->child as child to geno
924                        if (whichchild == 1)
925                                geno->child = g2->child;
926                        else
927                                geno->child2 = g2->child;
928                        g2->child->parent = geno;
929                }
930                oldgeno = g2;
931                oldgeno->child = NULL;
932                delete oldgeno;
933                if (geno->child == NULL) return;
934                // check this new
935                repairGeno(geno, whichchild);
936                return;
937        }
938        if (g2 == repair_parent)
939        {
940                geno->removeChild(g2);
941                geno->addChild(repair_insert);
942                repair_insert->parent = geno;
943                repair_insert->child = g2;
944                repair_insert->child2 = NULL;
945                g2->parent = repair_insert;
946        }
947        // recurse
948        if (g2->child)  repairGeno(g2, 1);
949        if (g2->child2) repairGeno(g2, 2);
950}
951
952
953void f4_Cells::toF1Geno(SString &out)
954{
955        if (tmpcel) delete tmpcel;
956        tmpcel = new f4_Cell(-1, NULL, 0, GeneProps::standard_values);
957        out = "";
958        toF1GenoRec(0, out);
959        delete tmpcel;
960}
961
962
963void f4_Cells::toF1GenoRec(int curc, SString &out)
964{
965        int i, j, ccount;
966        f4_Cell *thisti;
967        f4_Cell *thneu;
968        char buf[200];
969
970        if (curc >= cell_count) return;
971
972        if (C[curc]->type != CELL_STICK) return;
973
974        thisti = C[curc];
975        if (thisti->dadlink != NULL)
976                *tmpcel = *(thisti->dadlink);
977
978        // adjust length, curvedness, etc.
979        tmpcel->P.propagateAlong(false);
980        while (tmpcel->P.length > thisti->P.length)
981        {
982                tmpcel->P.executeModifier('l');
983                out += "l";
984        }
985        while (tmpcel->P.length < thisti->P.length)
986        {
987                tmpcel->P.executeModifier('L');
988                out += "L";
989        }
990        while (tmpcel->P.curvedness > thisti->P.curvedness)
991        {
992                tmpcel->P.executeModifier('c');
993                out += "c";
994        }
995        while (tmpcel->P.curvedness < thisti->P.curvedness)
996        {
997                tmpcel->P.executeModifier('C');
998                out += "C";
999        }
1000        while (thisti->rolling > 0.0f)
1001        {
1002                rolling_dec(&(thisti->rolling));
1003                out += "R";
1004        }
1005        while (thisti->rolling < 0.0f)
1006        {
1007                rolling_inc(&(thisti->rolling));
1008                out += "r";
1009        }
1010
1011        // output X for this stick
1012        out += "X";
1013
1014        // neurons attached to it
1015        for (i = 0; i < cell_count; i++)
1016        {
1017                if (C[i]->type == CELL_NEURON)
1018                {
1019                        if (C[i]->dadlink == thisti)
1020                        {
1021                                thneu = C[i];
1022                                out += "[";
1023                                // ctrl
1024                                //if (1 == thneu->ctrl) out += "@"; // old code; this can be easily generalized to any neuroclass if ever needed
1025                                //if (2 == thneu->ctrl) out += "|";
1026                                out += thneu->neuclass->name.c_str(); // not tested, but something like that
1027                                // connections
1028                                for (j = 0; j < thneu->conns_count; j++)
1029                                {
1030                                        if (j) out += ",";
1031                                        sprintf(buf, "%d", thneu->conns[j]->from->nr - thneu->nr);
1032                                        out += buf;
1033                                        out += ":";
1034                                        // connection weight
1035                                        sprintf(buf, "%g", thneu->conns[j]->weight);
1036                                        out += buf;
1037                                }
1038                                out += "]";
1039                        }
1040                }
1041        }
1042
1043        // sticks connected to it
1044        if (thisti->commacount >= 2)
1045                out += "(";
1046
1047        ccount = 1;
1048        for (i = 0; i < cell_count; i++)
1049        {
1050                if (C[i]->type == CELL_STICK)
1051                {
1052                        if (C[i]->dadlink == thisti)
1053                        {
1054                                while (ccount < (C[i])->anglepos)
1055                                {
1056                                        ccount++;
1057                                        out += ",";
1058                                }
1059                                toF1GenoRec(i, out);
1060                        }
1061                }
1062        }
1063
1064        while (ccount < thisti->commacount)
1065        {
1066                ccount++;
1067                out += ",";
1068        }
1069
1070        if (thisti->commacount >= 2)
1071                out += ")";
1072}
1073
1074
1075
1076// to organize an f4 genotype in a tree structure
1077
1078f4_Node::f4_Node()
1079{
1080        name = "?";
1081        parent = NULL;
1082        child = NULL;
1083        child2 = NULL;
1084        pos = -1;
1085
1086        reps = 0;
1087        prop_symbol = '\0';
1088        prop_increase = false;
1089        conn_from = 0;
1090        conn_weight = 0.0;
1091        neuclass = NULL;
1092}
1093
1094f4_Node::f4_Node(string nname, f4_Node *nparent, int npos)
1095{
1096        name = nname;
1097        parent = nparent;
1098        child = NULL;
1099        child2 = NULL;
1100        pos = npos;
1101        if (parent) parent->addChild(this);
1102
1103        reps = 0;
1104        prop_symbol = '\0';
1105        prop_increase = false;
1106        conn_from = 0;
1107        conn_weight = 0.0;
1108        neuclass = NULL;
1109}
1110
1111f4_Node::f4_Node(char nname, f4_Node *nparent, int npos)
1112{
1113        name = nname;
1114        parent = nparent;
1115        child = NULL;
1116        child2 = NULL;
1117        pos = npos;
1118        if (parent) parent->addChild(this);
1119
1120        reps = 0;
1121        prop_symbol = '\0';
1122        prop_increase = false;
1123        conn_from = 0;
1124        conn_weight = 0.0;
1125        neuclass = NULL;
1126}
1127
1128f4_Node::~f4_Node()
1129{
1130        destroy();
1131}
1132
1133void f4_Node::print_tree(const f4_Node *root, int indent)
1134{
1135        for (int i = 0; i < indent; i++) printf(" ");
1136        printf("%s (%d)", root->name.c_str(), root->count());
1137        if (root->name == "[")
1138                printf("     from=%-3d  weight=%g", root->conn_from, root->conn_weight);
1139        printf("\n");
1140        if (root->child) print_tree(root->child, indent + 1);
1141        if (root->child2) print_tree(root->child2, indent + 1);
1142}
1143
1144int f4_Node::addChild(f4_Node *nchi)
1145{
1146        if (child == NULL)
1147        {
1148                child = nchi;
1149                return 0;
1150        }
1151        if (child2 == NULL)
1152        {
1153                child2 = nchi;
1154                return 0;
1155        }
1156        return -1;
1157}
1158
1159int f4_Node::removeChild(f4_Node *nchi)
1160{
1161        if (nchi == child2)
1162        {
1163                child2 = NULL;
1164                return 0;
1165        }
1166        if (nchi == child)
1167        {
1168                child = NULL;
1169                return 0;
1170        }
1171        return -1;
1172}
1173
1174int f4_Node::childCount()
1175{
1176        if (child != NULL)
1177        {
1178                if (child2 != NULL) return 2;
1179                else return 1;
1180        }
1181        else
1182        {
1183                if (child2 != NULL) return 1;
1184                else return 0;
1185        }
1186}
1187
1188int f4_Node::count() const
1189{
1190        int c = 1;
1191        if (child != NULL)  c += child->count();
1192        if (child2 != NULL) c += child2->count();
1193        return c;
1194}
1195
1196f4_Node* f4_Node::ordNode(int n)
1197{
1198        int n1;
1199        if (n == 0) return this;
1200        n--;
1201        if (child != NULL)
1202        {
1203                n1 = child->count();
1204                if (n < n1) return child->ordNode(n);
1205                n -= n1;
1206        }
1207        if (child2 != NULL)
1208        {
1209                n1 = child2->count();
1210                if (n < n1) return child2->ordNode(n);
1211                n -= n1;
1212        }
1213        return NULL;
1214}
1215
1216f4_Node* f4_Node::randomNode()
1217{
1218        int n = count();
1219        // pick a random node between 0 and n-1
1220        return ordNode(rndUint(n));
1221}
1222
1223f4_Node* f4_Node::randomNodeWithSize(int mn, int mx)
1224{
1225        // try random nodes, and accept if size in range
1226        // limit to maxlim tries
1227        int i, n, maxlim;
1228        f4_Node *nod = NULL;
1229        maxlim = count();
1230        for (i = 0; i < maxlim; i++)
1231        {
1232                nod = randomNode();
1233                n = nod->count();
1234                if ((n >= mn) && (n <= mx)) return nod;
1235        }
1236        // failed, doesn't matter
1237        return nod;
1238}
1239
1240void f4_Node::sprint(SString& out)
1241{
1242        char buf2[20];
1243        // special case: repetition code
1244        if (name == "#")
1245        {
1246                out += "#";
1247                sprintf(buf2, "%d", reps);
1248                out += buf2;
1249        }
1250        else {
1251                // special case: neuron connection
1252                if (name == "[")
1253                {
1254                        out += "[";
1255                        sprintf(buf2, "%d", conn_from);
1256                        out += buf2;
1257                        sprintf(buf2, ":%g]", conn_weight);
1258                        out += buf2;
1259                }
1260                else if (name == ":")
1261                {
1262                        sprintf(buf2, ":%c%c:", prop_increase ? '+' : '-', prop_symbol);
1263                        out += buf2;
1264                }
1265                else if (neuclass != NULL)
1266                {
1267                        out += "N:";
1268                        out += neuclass->name.c_str();
1269                }
1270                else
1271                {
1272                        out += name.c_str();
1273                }
1274        }
1275
1276        if (child != NULL)
1277                child->sprint(out);
1278        // if two children, make sure last char is a '>'
1279        if (childCount() == 2)
1280                if (out[0] == 0) out += ">"; else
1281                        if (out[out.length() - 1] != '>') out += ">";
1282
1283        if (child2 != NULL)
1284                child2->sprint(out);
1285        // make sure last char is a '>'
1286        if (out[0] == 0) out += ">"; else
1287                if (out[out.length() - 1] != '>') out += ">";
1288}
1289
1290void f4_Node::sprintAdj(char *& buf)
1291{
1292        unsigned int len;
1293        // build in a SString, with initial size
1294        SString out;
1295        out.reserve(int(strlen(buf)) + 2000);
1296
1297        sprint(out);
1298
1299        // very last '>' can be omitted
1300        len = out.length();
1301        if (len > 1)
1302                if (out[len - 1] == '>') { (out.directWrite())[len - 1] = 0; out.endWrite(); };
1303        // copy back to string
1304        // if new is longer, reallocate buf
1305        if (len + 1 > strlen(buf))
1306        {
1307                buf = (char*)realloc(buf, len + 1);
1308        }
1309        strcpy(buf, out.c_str());
1310}
1311
1312f4_Node* f4_Node::duplicate()
1313{
1314        f4_Node *copy;
1315        copy = new f4_Node(*this);
1316        copy->parent = NULL;  // set later
1317        copy->child = NULL;
1318        copy->child2 = NULL;
1319        if (child != NULL)
1320        {
1321                copy->child = child->duplicate();
1322                copy->child->parent = copy;
1323        }
1324        if (child2 != NULL)
1325        {
1326                copy->child2 = child2->duplicate();
1327                copy->child2->parent = copy;
1328        }
1329        return copy;
1330}
1331
1332
1333void f4_Node::destroy()
1334{
1335        // children are destroyed (recursively) through the destructor
1336        if (child2 != NULL) delete child2;
1337        if (child != NULL) delete child;
1338}
1339
1340// scan genotype string and build tree
1341// return >1 for error (errorpos)
1342int f4_processRecur(const char* genot, unsigned pos0, f4_Node *parent)
1343{
1344        unsigned int gpos;
1345        f4_Node *par;
1346
1347        gpos = pos0;
1348        par = parent;
1349        if (gpos >= strlen(genot)) return 1;
1350        while (gpos < strlen(genot))
1351        {
1352                // first switch across cell dividers and old semantics
1353                switch (genot[gpos])
1354                {
1355                case '<':
1356                {
1357                        // find out genotype start for child
1358                        int stopchar_offset = scanRecur(genot + gpos + 1, (int)strlen(genot + gpos + 1), '>');
1359
1360                        f4_Node *node = new f4_Node("<", par, gpos);
1361                        par = node;
1362                        int res = f4_processRecur(genot, gpos + 1, par);
1363                        if (res) return res;
1364                        if (gpos + stopchar_offset + 2 < strlen(genot))
1365                        {
1366                                res = f4_processRecur(genot, gpos + stopchar_offset + 2, par);
1367                                if (res) return res;
1368                        }
1369                        else // ran out
1370                        {
1371                                node = new f4_Node(">", par, int(strlen(genot)) - 1);
1372                                par = node;
1373                        }
1374                        gpos++;
1375                        return 0;  // OK
1376                }
1377                case '>':
1378                {
1379                        f4_Node *node = new f4_Node(">", par, gpos);
1380                        par = node;
1381                        gpos = (unsigned int)strlen(genot);
1382                        return 0;  // OK
1383                }
1384                case '#':
1385                {
1386                        // repetition marker, 1 by default
1387                        ExtValue val;
1388                        const char* end = val.parseNumber(genot + gpos + 1, ExtPType::TInt);
1389                        int reps = (end == NULL) ? 1 : val.getInt();
1390                        // find out genotype start for continuation
1391                        int stopchar_offset = scanRecur(genot + gpos + 1, (int)strlen(genot + gpos + 1), '>');
1392                        // skip number
1393                        unsigned int oldpos = gpos;
1394                        gpos += end - (genot + gpos);
1395                        //gpos++;
1396                        //while ((genot[gpos] >= '0') && (genot[gpos] <= '9')) gpos++;node1 = new f4_Node("#", par, oldpos);
1397                        f4_Node *node = new f4_Node("#", par, oldpos);
1398                        node->reps = reps;
1399                        par = node;
1400                        int res = f4_processRecur(genot, gpos, node);
1401                        if (res) return res;
1402                        if (oldpos + stopchar_offset + 2 < strlen(genot))
1403                        {
1404                                res = f4_processRecur(genot, oldpos + stopchar_offset + 2, node);
1405                                if (res) return res;
1406                        }
1407                        else // ran out
1408                        {
1409                                node = new f4_Node(">", par, int(strlen(genot)) - 1);
1410                        }
1411                        return 0;  // OK
1412                }
1413                case ' ':
1414                case '\n':
1415                case '\r':
1416                case '\t':
1417                {
1418                        // whitespace: ignore
1419                        gpos++;
1420                        break;
1421                }
1422                case 'N':
1423                {
1424                        int forgenorange = gpos;
1425                        if (genot[gpos + 1] != ':')
1426                                return gpos + 1; //error
1427                        gpos += 2; //skipping "N:"
1428                        unsigned int begin_index = gpos;
1429                        char* end = (char*)genot + begin_index;
1430                        NeuroClass *neuclass = GenoOperators::parseNeuroClass(end, ModelEnum::SHAPETYPE_BALL_AND_STICK);
1431                        if (neuclass == NULL)
1432                                return gpos + 1; //error
1433                        gpos += end - genot - begin_index;
1434                        string neutype = string(genot + begin_index, genot + gpos);
1435                        f4_Node *node = new f4_Node(neutype, par, forgenorange);
1436                        node->neuclass = neuclass;
1437                        par = node;
1438                        // if it continues with a colon that determines a neuron parameter (e.g. N:N:+=: ), then let the switch case for colon handle this
1439                        break;
1440                }
1441                case ':':
1442                {
1443                        // neuron parameter  +! -! += -= +/ or -/
1444                        // 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
1445                        char prop_dir, prop_symbol, prop_end[2]; // prop_end is only to ensure that neuron parameter definition is completed
1446                        if (sscanf(genot + gpos, ":%c%c%1[:]", &prop_dir, &prop_symbol, &prop_end) != 3)
1447                                // error: incorrect format
1448                                return gpos + 1 + 1;
1449                        if (prop_dir != '-' && prop_dir != '+')
1450                                return gpos + 1 + 1; //error
1451                        switch (prop_symbol)
1452                        {
1453                        case '!':  case '=':  case '/':  break;
1454                        default:
1455                                return gpos + 1 + 1; //error
1456                        }
1457                        f4_Node *node = new f4_Node(":", par, gpos);
1458                        node->prop_symbol = prop_symbol;
1459                        node->prop_increase = prop_dir == '+' ? true : false; // + or -
1460                        par = node;
1461                        int stopchar_offset = scanRecur(genot + gpos + 1, (int)strlen(genot + gpos + 1), ':');
1462                        gpos += stopchar_offset + 2;
1463                        break;
1464                }
1465                case '[':
1466                {
1467                        double weight = 0;
1468                        int relfrom;
1469                        const char *end = parseConnection(genot + gpos, relfrom, weight);
1470                        if (end == NULL)
1471                                return gpos + 1; //error
1472
1473                        f4_Node *node = new f4_Node("[", par, gpos);
1474                        node->conn_from = relfrom;
1475                        node->conn_weight = weight;
1476                        par = node;
1477                        int stopchar_offset = scanRecur(genot + gpos + 1, (int)strlen(genot + gpos + 1), ']');
1478                        gpos += stopchar_offset + 2;
1479                        break;
1480                }
1481                default: // 'X' and ',' and all modifiers and also invalid symbols - add a node, for invalid symbols build will give the error or repair
1482                {
1483                        //printf("any regular character '%c'\n", genot[gpos]);
1484                        f4_Node *node = new f4_Node(genot[gpos], par, gpos);
1485                        par = node;
1486                        gpos++;
1487                        break;
1488                }
1489                }
1490        }
1491
1492        // should end with a '>'
1493        if (par)
1494        {
1495                if (par->name != ">")
1496                {
1497                        f4_Node *node = new f4_Node('>', par, int(strlen(genot)) - 1);
1498                        par = node;
1499                }
1500        }
1501
1502        return 0;
1503}
1504
1505const char* parseConnection(const char *fragm, int& relfrom, double &weight)
1506{
1507        const char *parser = fragm;
1508        if (*parser != '[') return NULL;
1509        parser++;
1510        ExtValue val;
1511        parser = val.parseNumber(parser, ExtPType::TInt);
1512        if (parser == NULL) return NULL;
1513        relfrom = val.getInt();
1514        if (*parser != ':') return NULL;
1515        parser++;
1516        parser = val.parseNumber(parser, ExtPType::TDouble);
1517        if (parser == NULL) return NULL;
1518        weight = val.getDouble();
1519        if (*parser != ']') return NULL;
1520        parser++;
1521        return parser;
1522}
1523
1524/*
1525f4_Node* f4_processTree(const char* geno)
1526{
1527        f4_Node *root = new f4_Node();
1528        int res = f4_processRecur(geno, 0, root);
1529        if (res) return NULL;
1530        //DB( printf("test f4  "); )
1531        DB(
1532                if (root->child)
1533                {
1534                        char* buf = (char*)malloc(300);
1535                        DB(printf("(%d) ", root->child->count());)
1536                                buf[0] = 0;
1537                        root->child->sprintAdj(buf);
1538                        DB(printf("%s\n", buf);)
1539                                free(buf);
1540                }
1541        )
1542                return root->child;
1543}
1544*/
Note: See TracBrowser for help on using the repository browser.