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

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

The validate() function may now attempt to repair genotypes without syntax errors (i.e., fully parsed into a correct f4_Node tree), but with semantic errors

  • 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 = pi/4 like in f1
28}
29
30void rolling_inc(double *v)
31{
32        *v += 0.7853;  // 0.7853981  45 degrees
33}
34
35
36f4_Cell::f4_Cell(int nnr, f4_Cell *ndad, int nangle, GeneProps newP)
37{
38        nr = nnr;
39        type = CELL_UNDIFF;
40        dadlink = ndad;
41        org = NULL;
42        genot = NULL;
43        gcur = NULL;
44        active = true;
45        repeat.clear();
46        //genoRange.clear(); -- implicit
47
48        anglepos = nangle;
49        commacount = 0;
50        childcount = 0;
51        P = newP;
52        rolling = 0;
53        xrot = 0;
54        zrot = 0;
55        //OM = Orient_1;
56        inertia = 0.8;
57        force = 0.04;
58        sigmo = 2;
59        conns_count = 0;
60
61        // adjust firstend and OM if there is a stick dad
62        if (ndad != NULL)
63        {
64                // make sure it is a stick (and not a stick f4_Cell!)
65                if (ndad->type == CELL_STICK)
66                {
67                        //firstend = ndad->lastend;
68                        //OM = ndad->OM;
69                        ndad->childcount++;
70                }
71                if (ndad->type == CELL_NEURON)
72                {
73                        inertia = ndad->inertia;
74                        force = ndad->force;
75                        sigmo = ndad->sigmo;
76                }
77        }
78        // adjust lastend
79        //lastend = firstend + ((Orient)OM * (Pt3D(1,0,0) * P.len));
80        mz = 1;
81}
82
83
84f4_Cell::f4_Cell(f4_Cells *nO, int nnr, f4_Node *ngeno, f4_Node *ngcur, f4_Cell *ndad, int nangle, GeneProps newP)
85{
86        nr = nnr;
87        type = CELL_UNDIFF;
88        dadlink = ndad;
89        org = nO;
90        genot = ngeno;
91        gcur = ngcur;
92        active = true;
93        repeat.clear();
94        //genoRange.clear(); -- implicit
95        // preserve geno range of parent cell
96        if (NULL != ndad)
97                genoRange.add(ndad->genoRange);
98
99        anglepos = nangle;
100        commacount = 0;
101        childcount = 0;
102        P = newP;
103        rolling = 0;
104        xrot = 0;
105        zrot = 0;
106        //OM = Orient_1;
107        inertia = 0.8;
108        force = 0.04;
109        sigmo = 2;
110        conns_count = 0;
111
112        // adjust firstend and OM if there is a stick dad
113        if (ndad != NULL)
114        {
115                // make sure it is a stick (and not a stick f4_Cell!)
116                if (ndad->type == CELL_STICK)
117                {
118                        //firstend = ndad->lastend;
119                        //OM = ndad->OM;
120                        ndad->childcount++;
121                }
122                if (ndad->type == CELL_NEURON)
123                {
124                        inertia = ndad->inertia;
125                        force = ndad->force;
126                        sigmo = ndad->sigmo;
127                }
128        }
129        // adjust lastend
130        //lastend = firstend + ((Orient)OM * (Pt3D(1,0,0) * P.len));
131        mz = 1;
132}
133
134
135f4_Cell::~f4_Cell()
136{
137        // remove connections
138        if (conns_count)
139        {
140                int i;
141                for (i = conns_count - 1; i >= 0; i--)
142                        delete conns[i];
143                conns_count = 0;
144        }
145}
146
147
148/* return codes:
149        1 error at pos
150        0  halt development (yield) for a cycle
151        */
152int f4_Cell::oneStep()
153{
154        while (gcur != NULL)
155        {
156                //DB( printf("  %d (%d) executing '%c' %d\n", name, type, gcur->name, gcur->pos); )
157                // currently this is the last one processed
158                // the current genotype code is processed
159                //genoRange.add(gcur->pos,gcur->pos+gcur->name.length()-1);
160
161                // To detect what genes are valid neuroclass names, but do NOT have is_neuroclass==true
162                // (just as a curiosity to ensure we properly distinguish between, for example, the "G" neuron and the "G" modifier):
163                //char *TMP = (char*)gcur->name.c_str();
164                //if (gcur->is_neuroclass==false && GenoOperators::parseNeuroClass(TMP, ModelEnum::SHAPETYPE_BALL_AND_STICK))
165                //      printf("Could be a valid neuroclass, but is_neuroclass==false: %s\n", gcur->name.c_str());
166
167                if (gcur->neuclass == NULL) //not a neuron
168                {
169                        if (gcur->name.length() > 1)
170                                logPrintf("f4_Cell", "oneStep", LOG_WARN, "Multiple-character code that is not a neuron class name: '%s'", gcur->name.c_str()); //let's see an example of such a code...
171
172                        genoRange.add(gcur->pos, gcur->pos);
173                        char name = gcur->name[0];
174                        switch (name)
175                        {
176                        case '<':
177                        {
178                                // cell division!
179                                //DB( printf("  div! %d\n", name); )
180
181                                // error: sticks cannot divide
182                                if (type == CELL_STICK)
183                                {
184                                        // cannot fix
185                                        org->setError(gcur->pos);
186                                        return 1;  // stop
187                                }
188
189                                // undiff divides
190                                if (type == CELL_UNDIFF)
191                                {
192                                        // commacount is set only when daughter turns into X
193                                        // daughter cell
194                                        // adjust new len
195                                        GeneProps newP = P;
196                                        newP.propagateAlong(false);
197                                        f4_Cell *tmp = new f4_Cell(org, org->cell_count, genot, gcur->child2, this, commacount, newP);
198                                        tmp->repeat = repeat;
199                                        repeat.clear();
200                                        org->addCell(tmp);
201                                }
202                                // a neuron divides: create a new, duplicate connections
203                                if (type == CELL_NEURON)
204                                {
205                                        // daughter cell
206                                        f4_Cell *tmp = new f4_Cell(org, org->cell_count, genot, gcur->child2,
207                                                // has the same dadlink
208                                                this->dadlink, commacount, P);
209                                        tmp->repeat = repeat;
210                                        repeat.clear();
211                                        // it is a neuron from start
212                                        tmp->type = CELL_NEURON;
213                                        // it has the same type as the parent neuron
214                                        tmp->neuclass = neuclass;
215                                        // duplicate connections
216                                        f4_CellConn *conn;
217                                        for (int i = 0; i < conns_count; i++)
218                                        {
219                                                conn = conns[i];
220                                                tmp->addConnection(conn->from, conn->weight);
221                                        }
222                                        org->addCell(tmp);
223                                }
224                                // adjustments for this cell
225                                gcur = gcur->child;
226                                // halt development
227                                return 0;
228                        }
229                        case '>':
230                        {
231                                // finish
232                                // see if there is a repeat count
233                                if (repeat.top > 0)
234                                { // there is a repeat counter
235                                        if (!repeat.first()->isNull())
236                                        { // repeat counter is not null
237                                                repeat.first()->dec();
238                                                if (repeat.first()->count > 0)
239                                                {
240                                                        // return to repeat
241                                                        gcur = repeat.first()->node->child;
242                                                }
243                                                else
244                                                {
245                                                        // continue
246                                                        gcur = repeat.first()->node->child2;
247                                                        repeat.pop();
248                                                }
249                                                break;
250                                        }
251                                        else
252                                        {
253                                                repeat.pop();
254                                                // 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.
255                                                // This case can be tested with "#0" in the genotype. Anyway, there seems to be no difference in outcomes with and without "break".
256                                                // However, falling through [[fallthrough]] below for count==0 causes performing repeat.push(repeat_ptr(gcur, 0)) while the very reason
257                                                // 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.
258                                                // The ultimate informed decision would require understanding all the logic and testing all the edge cases.
259#ifdef BREAK_WHEN_REP_COUNTER_NULL
260                                                break;
261#endif
262                                        }
263                                }
264                                else
265                                {
266                                        // error: still undiff
267                                        if (type == CELL_UNDIFF)
268                                        {
269                                                // fix it: insert an 'X'
270                                                f4_Node *insertnode = new f4_Node("X", NULL, gcur->pos);
271                                                if (org->setRepairInsert(gcur->pos, gcur, insertnode)) // not in repair mode, release
272                                                        delete insertnode;
273                                                return 1;
274                                        }
275                                        repeat.clear();
276                                        active = false;  // stop
277                                        // eat up rest
278                                        int remaining_nodes = gcur->count() - 1;
279                                        if (remaining_nodes > 0)
280                                                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...
281                                        gcur = NULL;
282                                        return 0;
283                                }
284                        }
285#ifndef BREAK_WHEN_REP_COUNTER_NULL
286                        [[fallthrough]];
287#endif
288                        case '#':
289                        {
290                                // repetition marker
291                                if (repeat.top >= repeat_stack::stackSize)
292                                {
293                                        // repeat pointer stack is full, cannot remember this one.
294                                        // fix: delete it
295                                        org->setRepairRemove(gcur->pos, gcur);
296                                        return 1;  // stop
297                                }
298                                repeat.push(repeat_ptr(gcur, gcur->reps));
299                                gcur = gcur->child;
300                                break;
301                        }
302                        case ',':
303                        {
304                                commacount++;
305                                gcur = gcur->child;
306                                break;
307                        }
308                        case 'r':  case 'R':
309                        {
310                                // error: if neuron
311                                if (type == CELL_NEURON)
312                                {
313                                        // fix: delete it
314                                        org->setRepairRemove(gcur->pos, gcur);
315                                        return 1;  // stop
316                                }
317                                switch (name)
318                                {
319                                case 'r':   rolling_dec(&rolling); break;
320                                case 'R':   rolling_inc(&rolling); break;
321                                }
322                                gcur = gcur->child;
323                                break;
324                        }
325                        case 'l':  case 'L':
326                        case 'c':  case 'C':
327                        case 'q':  case 'Q':
328                        case 'a':  case 'A':
329                        case 'i':  case 'I':
330                        case 's':  case 'S':
331                        case 'm':  case 'M':
332                        case 'f':  case 'F':
333                        case 'w':  case 'W':
334                        case 'e':  case 'E':
335                        case 'd':  case 'D':
336                        case 'g':  case 'G':
337                        case 'b':  case 'B':
338                        case 'h':  case 'H':
339                        {
340                                // error: if neuron
341                                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
342                                {//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.
343                                        //printf("Handled as a modifier, but type==CELL_NEURON: '%c'\n", name);
344                                        // fix: delete it
345                                        org->setRepairRemove(gcur->pos, gcur);
346                                        return 1;  // stop
347                                }
348                                P.executeModifier(name);
349                                gcur = gcur->child;
350                                break;
351                        }
352                        case 'X':
353                        {
354                                // turn undiff. cell into a stick
355                                // error: already differentiated
356                                if (type != CELL_UNDIFF)
357                                {
358                                        // fix: delete this node
359                                        org->setRepairRemove(gcur->pos, gcur);
360                                        return 1;  // stop
361                                }
362                                type = CELL_STICK;
363                                // fix dad commacount and own anglepos
364                                if (NULL != dadlink)
365                                {
366                                        dadlink->commacount++;
367                                        anglepos = dadlink->commacount;
368                                }
369                                // change of type halts developments, see comment at 'neuclasshandler' below
370                                gcur = gcur->child;
371                                return 0;
372                        }
373                        case '[':
374                        {
375                                // connection to neuron
376                                // error: not a neuron
377                                if (type != CELL_NEURON)
378                                {
379                                        // fix: delete it
380                                        org->setRepairRemove(gcur->pos, gcur);
381                                        return 1;  // stop
382                                }
383                                // input [%d:%g]
384                                int relfrom = gcur->conn_from;
385                                double weight = gcur->conn_weight;
386                                f4_Cell *neu_from = NULL;
387
388                                // input from other neuron
389                                // find neuron at relative i
390                                // find own index
391                                int this_index = 0, neu_counter = 0;
392                                for (int i = 0; i < org->cell_count; i++)
393                                {
394                                        if (org->C[i]->type == CELL_NEURON) neu_counter++;
395                                        if (org->C[i] == this) { this_index = neu_counter - 1; break; }
396                                }
397                                // find index of incoming
398                                int from_index = this_index + relfrom;
399
400                                if (from_index < 0) goto wait_conn;
401                                if (from_index >= org->cell_count) goto wait_conn;
402
403                                // find that neuron
404                                neu_counter = 0;
405                                int from;
406                                for (from = 0; from < org->cell_count; from++)
407                                {
408                                        if (org->C[from]->type == CELL_NEURON) neu_counter++;
409                                        if (from_index == (neu_counter - 1)) break;
410                                }
411                                if (from >= org->cell_count) goto wait_conn;
412                                neu_from = org->C[from];
413
414                                // add connection
415                                // error: could not add connection (too many?)
416                                if (addConnection(neu_from, weight))
417                                {
418                                        // cannot fix
419                                        org->setError(gcur->pos);
420                                        return 1;  // stop
421                                }
422                                gcur = gcur->child;
423                                break;
424                        }
425                wait_conn:
426                        {
427                                // wait for other neurons to develop
428                                // if there are others still active
429
430                                int active_count = 0;
431                                for (int i = 0; i < org->cell_count; i++)
432                                        if (org->C[i]->active) active_count++;
433                                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)
434                                if (active_count > 0)
435                                        return 0;  // there is at least one active (including ourselves), halt, try again
436
437#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.
438                                if (gcur->parent->name == "#")
439                                {
440                                        // MacKo 2023-04: Unfortunately the logic of multiplicating connections is not ideal...
441                                        //TREAT_BAD_CONNECTIONS_AS_INVALID_GENO without this "#" exception would break /*4*/<X>N:N#5<[1:1]>
442                                        // because every neuron wants to get an input from the neuron that will be created next
443                                        // and all is fine until the last created neuron, which wants to get an input from another one which will not be created
444                                        // (3 gets from 4, 4 gets from 5, 5 wants to get from 6 (relative connection offset for each of them is 1),
445                                        // but 6 will not get created and if we want to TREAT_BAD_CONNECTIONS_AS_INVALID_GENO, we produce an error...
446                                        // 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...
447                                        //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.
448                                        //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...
449                                        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
450                                        gcur = gcur->child;
451                                        return 0;
452                                }
453                                else
454                                {
455                                        //org->setError(gcur->pos); //in case setRepairRemove() would not always produce reasonable results
456                                        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?)
457                                        return 1;  // stop
458                                }
459#else
460                                // no more actives, cannot add connection, ignore, but treat not as an error - before 2023-04
461                                gcur = gcur->child;
462#endif
463                        }
464                        break;
465                        case ':':
466                        {
467                                // neuron parameter
468                                // error: not a neuron
469                                if (type != CELL_NEURON)
470                                {
471                                        // fix: delete it
472                                        org->setRepairRemove(gcur->pos, gcur);
473                                        return 1;  // stop
474                                }
475                                switch (gcur->prop_symbol)
476                                {
477                                case '!':
478                                        if (gcur->prop_increase)
479                                                force += (1.0 - force) * 0.2;
480                                        else
481                                                force -= force * 0.2;
482                                        break;
483                                case '=':
484                                        if (gcur->prop_increase)
485                                                inertia += (1.0 - inertia) * 0.2;
486                                        else
487                                                inertia -= inertia * 0.2;
488                                        break;
489                                case '/':
490                                        if (gcur->prop_increase)
491                                                sigmo *= 1.4;
492                                        else
493                                                sigmo /= 1.4;
494                                        break;
495                                default:
496                                        org->setRepairRemove(gcur->pos, gcur);
497                                        return 1;  // stop
498                                }
499                                gcur = gcur->child;
500                                break;
501                        }
502                        case ' ':
503                        {
504                                // space has no effect, should not occur
505                                // fix: delete it
506                                org->setRepairRemove(gcur->pos, gcur);
507                                gcur = gcur->child;
508                                break;
509                        }
510                        default:
511                        {
512                                // error: unknown code
513                                string buf = "Unknown code '" + gcur->name + "'";
514                                logMessage("f4_Cell", "oneStep", LOG_ERROR, buf.c_str());
515                                org->setRepairRemove(gcur->pos, gcur);
516                                return 1;
517                        }
518                        }
519                }
520                else
521                {
522                        genoRange.add(gcur->pos, gcur->pos + int(gcur->name.length()) + 2 - 1); // +2 for N:
523                        if (type != CELL_UNDIFF)
524                        {
525                                // fix: delete this node
526                                org->setRepairRemove(gcur->pos, gcur);
527                                return 1;  // stop
528                        }
529                        // error: if no previous
530                        if (dadlink == NULL)
531                        {
532                                // fix: delete it
533                                org->setRepairRemove(gcur->pos, gcur);
534                                return 1;  // stop
535                        }
536                        neuclass = gcur->neuclass;
537                        type = CELL_NEURON;
538                        // change of type also halts development, to give other
539                        // cells a chance for adjustment.  Namely, it is important
540                        // to wait for other cells to turn to neurons before adding connections
541                        gcur = gcur->child;
542                        return 0; //stop
543                }
544        }
545        active = false;  // done
546        return 0;
547}
548
549
550int f4_Cell::addConnection(f4_Cell *nfrom, double nweight)
551{
552        if (nfrom->neuclass->getPreferredOutput() == 0) return -1; // if incoming neuron does not produce output, return error
553        if (neuclass->getPreferredInputs() != -1 && conns_count >= neuclass->getPreferredInputs()) return -1; //cannot add more inputs to this neuron
554        if (conns_count >= F4_MAX_CELL_INPUTS - 1) return -1; // over hardcoded limit
555        conns[conns_count] = new f4_CellConn(nfrom, nweight);
556        conns_count++;
557        return 0;
558}
559
560
561void f4_Cell::adjustRec()
562{
563        //f4_OrientMat rot;
564        int i;
565
566        if (recProcessedFlag)
567                // already processed
568                return;
569
570        // mark it processed
571        recProcessedFlag = 1;
572
573        // make sure its parent is processed first
574        if (dadlink != NULL)
575                dadlink->adjustRec();
576
577        // count children
578        childcount = 0;
579        for (i = 0; i < org->cell_count; i++)
580        {
581                if (org->C[i]->dadlink == this)
582                        if (org->C[i]->type == CELL_STICK)
583                                childcount++;
584        }
585
586        if (type == CELL_STICK)
587        {
588                if (dadlink == NULL)
589                {
590                        //firstend = Pt3D_0;
591                        // rotation due to rolling
592                        xrot = rolling;
593                        mz = 1;
594                }
595                else
596                {
597                        //firstend = dadlink->lastend;
598                        GeneProps Pdad = dadlink->P;
599                        GeneProps Padj = Pdad;
600                        Padj.propagateAlong(false);
601
602                        //rot = Orient_1;
603
604                        // rotation due to rolling
605                        xrot = rolling +
606                                // rotation due to twist
607                                Pdad.twist;
608                        if (dadlink->commacount <= 1)
609                        {
610                                // rotation due to curvedness
611                                zrot = Padj.curvedness;
612                        }
613                        else
614                        {
615                                zrot = Padj.curvedness + (anglepos * 1.0 / (dadlink->commacount + 1) - 0.5) * M_PI * 2.0;
616                        }
617
618                        //rot = rot * f4_OrientMat(yOz, xrot);
619                        //rot = rot * f4_OrientMat(xOy, zrot);
620                        // rotation relative to parent stick
621                        //OM = rot * OM;
622
623                        // rotation in world coordinates
624                        //OM =  ((f4_OrientMat)dadlink->OM) * OM;
625                        mz = dadlink->mz / dadlink->childcount;
626                }
627                //Pt3D lastoffset = (Orient)OM * (Pt3D(1,0,0)*P.len);
628                //lastend = firstend + lastoffset;
629        }
630}
631
632
633
634f4_CellConn::f4_CellConn(f4_Cell *nfrom, double nweight)
635{
636        from = nfrom;
637        weight = nweight;
638}
639
640
641
642f4_Cells::f4_Cells(f4_Node *genome, bool nrepair)
643{
644        repair = nrepair;
645        errorcode = GENOPER_OK;
646        errorpos = -1;
647        repair_remove = NULL;
648        repair_parent = NULL;
649        repair_insert = NULL;
650        tmpcel = NULL;
651
652        // create ancestor cell
653        C[0] = new f4_Cell(this, 0, genome, genome, NULL, 0, GeneProps::standard_values);
654        cell_count = 1;
655}
656
657
658
659f4_Cells::~f4_Cells()
660{
661        // release cells
662        if (cell_count)
663        {
664                for (int i = cell_count - 1; i >= 0; i--)
665                        delete C[i];
666                cell_count = 0;
667        }
668}
669
670
671bool f4_Cells::oneStep()
672{
673        int old_cell_count = cell_count; //cell_count may change in the loop as new cells may be appended because cells may be dividing
674        for (int i = 0; i < old_cell_count; i++)
675        {
676                int cellstep_ret = C[i]->oneStep(); //keeps calling independently of C[i]->active
677                if (cellstep_ret > 0)
678                {
679                        // error
680                        C[i]->active = false;  // stop
681                        return false;
682                }
683        }
684        for (int i = 0; i < cell_count; i++) //we check all cells, including newly created ones
685                if (C[i]->active)
686                        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.
687        return false;
688}
689
690
691int f4_Cells::simulate()
692{
693        constexpr bool print_debugging = false; //print the state of cells during development
694        errorcode = GENOPER_OK;
695
696        for (int i = 0; i < cell_count; i++)  C[i]->active = true;
697
698        if (print_debugging) f4_Node::print_tree(C[0]->genot, 0);
699        if (print_debugging) print_cells("Initialization");
700
701        // execute oneStep() in a cycle
702        while (oneStep()) if (print_debugging) print_cells("Development step");
703        if (print_debugging) print_cells("After last development step");
704
705#ifdef EXTRA_STEP_CELL_DEVELOPMENT
706        if (errorcode == GENOPER_OK)
707        {
708                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.
709        }
710#endif
711
712        if (errorcode != GENOPER_OK) return errorcode;
713
714        // fix neuron attachements
715        for (int i = 0; i < cell_count; i++)
716        {
717                if (C[i]->type == CELL_NEURON)
718                {
719                        while (C[i]->dadlink->type == CELL_NEURON)
720                        {
721                                C[i]->dadlink = C[i]->dadlink->dadlink;
722                        }
723                }
724        }
725
726        // there should be no undiff. cells
727        // make undifferentiated cells sticks
728        for (int i = 0; i < cell_count; i++)
729        {
730                if (C[i]->type == CELL_UNDIFF)
731                {
732                        C[i]->type = CELL_STICK;
733                        //setError();
734                }
735        }
736
737        // recursive adjust
738        // reset recursive traverse flags
739        for (int i = 0; i < cell_count; i++)
740                C[i]->recProcessedFlag = 0;
741        // process every cell
742        for (int i = 0; i < cell_count; i++)
743                C[i]->adjustRec();
744
745        //DB( printf("Cell simulation done, %d cells. \n", nc); )
746
747        if (print_debugging) print_cells("Final");
748
749        return errorcode;
750}
751
752
753void f4_Cells::print_cells(const char* description)
754{
755        printf("------ %-55s ------ errorcode=%d, errorpos=%d\n", description, getErrorCode(), getErrorPos());
756        for (int i = 0; i < cell_count; i++)
757        {
758                f4_Cell *c = C[i];
759                string type;
760                switch (c->type)
761                {
762                case CELL_UNDIFF: type = "undiff"; break;
763                case CELL_STICK:  type = "STICK"; break;
764                case CELL_NEURON: type = string("NEURON:") + c->neuclass->name.c_str(); break;
765                default: type = std::to_string(c->type);
766                }
767                const char *status = c->active ? "active" : (c->gcur != NULL ? "yielding" : ""); //yielding = not active but waiting for other cells
768                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");
769                if (c->gcur && c->gcur->name == "[")
770                        printf("\tfrom=%d  weight=%g", c->gcur->conn_from, c->gcur->conn_weight);
771                printf("\n");
772                for (int l = 0; l < c->conns_count; l++)
773                        printf("\tconn:%d from=%d weight=%g\n", l, c->conns[l]->from->nr, c->conns[l]->weight);
774        }
775        printf("\n");
776}
777
778
779void f4_Cells::addCell(f4_Cell *newcell)
780{
781        if (cell_count >= F4_MAX_CELLS - 1)
782        {
783                delete newcell;
784                return;
785        }
786        C[cell_count] = newcell;
787        cell_count++;
788}
789
790
791void f4_Cells::setError(int nerrpos)
792{
793        errorcode = GENOPER_OPFAIL;
794        errorpos = nerrpos;
795}
796
797void f4_Cells::setRepairRemove(int nerrpos, f4_Node *to_remove)
798{
799        errorcode = GENOPER_REPAIR;
800        errorpos = nerrpos;
801        if (!repair)
802        {
803                // not in repair mode, treat as repairable error
804        }
805        else
806        {
807                repair_remove = to_remove;
808        }
809}
810
811int f4_Cells::setRepairInsert(int nerrpos, f4_Node *parent, f4_Node *to_insert)
812{
813        errorcode = GENOPER_REPAIR;
814        errorpos = nerrpos;
815        if (!repair)
816        {
817                // not in repair mode, treat as repairable error
818                return -1;
819        }
820        else
821        {
822                repair_parent = parent;
823                repair_insert = to_insert;
824                return 0;
825        }
826}
827
828void f4_Cells::repairGeno(f4_Node *geno, int whichchild)
829{
830        // assemble repaired geno, if the case
831        if (!repair) return;
832        if ((repair_remove == NULL) && (repair_insert == NULL)) return;
833        // traverse genotype tree, remove / insert node
834        f4_Node *g2;
835        if (whichchild == 1)
836                g2 = geno->child;
837        else
838                g2 = geno->child2;
839        if (g2 == NULL)
840                return;
841        if (g2 == repair_remove)
842        {
843                f4_Node *oldgeno;
844                geno->removeChild(g2);
845                if (g2->child)
846                {
847                        // add g2->child as child to geno
848                        if (whichchild == 1)
849                                geno->child = g2->child;
850                        else
851                                geno->child2 = g2->child;
852                        g2->child->parent = geno;
853                }
854                oldgeno = g2;
855                oldgeno->child = NULL;
856                delete oldgeno;
857                if (geno->child == NULL) return;
858                // check this new
859                repairGeno(geno, whichchild);
860                return;
861        }
862        if (g2 == repair_parent)
863        {
864                geno->removeChild(g2);
865                geno->addChild(repair_insert);
866                repair_insert->parent = geno;
867                repair_insert->child = g2;
868                repair_insert->child2 = NULL;
869                g2->parent = repair_insert;
870        }
871        // recurse
872        if (g2->child)  repairGeno(g2, 1);
873        if (g2->child2) repairGeno(g2, 2);
874}
875
876
877void f4_Cells::toF1Geno(SString &out)
878{
879        if (tmpcel) delete tmpcel;
880        tmpcel = new f4_Cell(-1, NULL, 0, GeneProps::standard_values);
881        out = "";
882        toF1GenoRec(0, out);
883        delete tmpcel;
884}
885
886
887void f4_Cells::toF1GenoRec(int curc, SString &out)
888{
889        int i, j, ccount;
890        f4_Cell *thisti;
891        f4_Cell *thneu;
892        char buf[200];
893
894        if (curc >= cell_count) return;
895
896        if (C[curc]->type != CELL_STICK) return;
897
898        thisti = C[curc];
899        if (thisti->dadlink != NULL)
900                *tmpcel = *(thisti->dadlink);
901
902        // adjust length, curvedness, etc.
903        tmpcel->P.propagateAlong(false);
904        while (tmpcel->P.length > thisti->P.length)
905        {
906                tmpcel->P.executeModifier('l');
907                out += "l";
908        }
909        while (tmpcel->P.length < thisti->P.length)
910        {
911                tmpcel->P.executeModifier('L');
912                out += "L";
913        }
914        while (tmpcel->P.curvedness > thisti->P.curvedness)
915        {
916                tmpcel->P.executeModifier('c');
917                out += "c";
918        }
919        while (tmpcel->P.curvedness < thisti->P.curvedness)
920        {
921                tmpcel->P.executeModifier('C');
922                out += "C";
923        }
924        while (thisti->rolling > 0.0f)
925        {
926                rolling_dec(&(thisti->rolling));
927                out += "R";
928        }
929        while (thisti->rolling < 0.0f)
930        {
931                rolling_inc(&(thisti->rolling));
932                out += "r";
933        }
934
935        // output X for this stick
936        out += "X";
937
938        // neurons attached to it
939        for (i = 0; i < cell_count; i++)
940        {
941                if (C[i]->type == CELL_NEURON)
942                {
943                        if (C[i]->dadlink == thisti)
944                        {
945                                thneu = C[i];
946                                out += "[";
947                                // ctrl
948                                //if (1 == thneu->ctrl) out += "@"; // old code; this can be easily generalized to any neuroclass if ever needed
949                                //if (2 == thneu->ctrl) out += "|";
950                                out += thneu->neuclass->name.c_str(); // not tested, but something like that
951                                // connections
952                                for (j = 0; j < thneu->conns_count; j++)
953                                {
954                                        if (j) out += ",";
955                                        sprintf(buf, "%d", thneu->conns[j]->from->nr - thneu->nr);
956                                        out += buf;
957                                        out += ":";
958                                        // connection weight
959                                        sprintf(buf, "%g", thneu->conns[j]->weight);
960                                        out += buf;
961                                }
962                                out += "]";
963                        }
964                }
965        }
966
967        // sticks connected to it
968        if (thisti->commacount >= 2)
969                out += "(";
970
971        ccount = 1;
972        for (i = 0; i < cell_count; i++)
973        {
974                if (C[i]->type == CELL_STICK)
975                {
976                        if (C[i]->dadlink == thisti)
977                        {
978                                while (ccount < (C[i])->anglepos)
979                                {
980                                        ccount++;
981                                        out += ",";
982                                }
983                                toF1GenoRec(i, out);
984                        }
985                }
986        }
987
988        while (ccount < thisti->commacount)
989        {
990                ccount++;
991                out += ",";
992        }
993
994        if (thisti->commacount >= 2)
995                out += ")";
996}
997
998
999
1000// to organize an f4 genotype in a tree structure
1001
1002f4_Node::f4_Node()
1003{
1004        name = "?";
1005        parent = NULL;
1006        child = NULL;
1007        child2 = NULL;
1008        pos = -1;
1009
1010        reps = 0;
1011        prop_symbol = '\0';
1012        prop_increase = false;
1013        conn_from = 0;
1014        conn_weight = 0.0;
1015        neuclass = NULL;
1016}
1017
1018f4_Node::f4_Node(string nname, f4_Node *nparent, int npos)
1019{
1020        name = nname;
1021        parent = nparent;
1022        child = NULL;
1023        child2 = NULL;
1024        pos = npos;
1025        if (parent) parent->addChild(this);
1026
1027        reps = 0;
1028        prop_symbol = '\0';
1029        prop_increase = false;
1030        conn_from = 0;
1031        conn_weight = 0.0;
1032        neuclass = NULL;
1033}
1034
1035f4_Node::f4_Node(char nname, f4_Node *nparent, int npos)
1036{
1037        name = nname;
1038        parent = nparent;
1039        child = NULL;
1040        child2 = NULL;
1041        pos = npos;
1042        if (parent) parent->addChild(this);
1043
1044        reps = 0;
1045        prop_symbol = '\0';
1046        prop_increase = false;
1047        conn_from = 0;
1048        conn_weight = 0.0;
1049        neuclass = NULL;
1050}
1051
1052f4_Node::~f4_Node()
1053{
1054        destroy();
1055}
1056
1057void f4_Node::print_tree(const f4_Node *root, int indent)
1058{
1059        for (int i = 0; i < indent; i++) printf(" ");
1060        printf("%s (%d)", root->name.c_str(), root->count());
1061        if (root->name == "[")
1062                printf("     from=%-3d  weight=%g", root->conn_from, root->conn_weight);
1063        printf("\n");
1064        if (root->child) print_tree(root->child, indent + 1);
1065        if (root->child2) print_tree(root->child2, indent + 1);
1066}
1067
1068int f4_Node::addChild(f4_Node *nchi)
1069{
1070        if (child == NULL)
1071        {
1072                child = nchi;
1073                return 0;
1074        }
1075        if (child2 == NULL)
1076        {
1077                child2 = nchi;
1078                return 0;
1079        }
1080        return -1;
1081}
1082
1083int f4_Node::removeChild(f4_Node *nchi)
1084{
1085        if (nchi == child2)
1086        {
1087                child2 = NULL;
1088                return 0;
1089        }
1090        if (nchi == child)
1091        {
1092                child = NULL;
1093                return 0;
1094        }
1095        return -1;
1096}
1097
1098int f4_Node::childCount()
1099{
1100        return int(child != NULL) + int(child2 != NULL); //0, 1 or 2
1101}
1102
1103int f4_Node::count() const
1104{
1105        int c = 1;
1106        if (child != NULL)  c += child->count();
1107        if (child2 != NULL) c += child2->count();
1108        return c;
1109}
1110
1111f4_Node* f4_Node::ordNode(int n)
1112{
1113        int n1;
1114        if (n == 0) return this;
1115        n--;
1116        if (child != NULL)
1117        {
1118                n1 = child->count();
1119                if (n < n1) return child->ordNode(n);
1120                n -= n1;
1121        }
1122        if (child2 != NULL)
1123        {
1124                n1 = child2->count();
1125                if (n < n1) return child2->ordNode(n);
1126                n -= n1;
1127        }
1128        return NULL;
1129}
1130
1131f4_Node* f4_Node::randomNode()
1132{
1133        int n = count();
1134        // pick a random node between 0 and n-1
1135        return ordNode(rndUint(n));
1136}
1137
1138f4_Node* f4_Node::randomNodeWithSize(int mn, int mx)
1139{
1140        // try random nodes, and accept if size in range
1141        // limit to maxlim tries
1142        int i, n, maxlim;
1143        f4_Node *nod = NULL;
1144        maxlim = count();
1145        for (i = 0; i < maxlim; i++)
1146        {
1147                nod = randomNode();
1148                n = nod->count();
1149                if ((n >= mn) && (n <= mx)) return nod;
1150        }
1151        // failed, doesn't matter
1152        return nod;
1153}
1154
1155void f4_Node::sprint(SString& out)
1156{
1157        char buf2[20];
1158        // special case: repetition code
1159        if (name == "#")
1160        {
1161                out += "#";
1162                sprintf(buf2, "%d", reps);
1163                out += buf2;
1164        }
1165        else {
1166                // special case: neuron connection
1167                if (name == "[")
1168                {
1169                        out += "[";
1170                        sprintf(buf2, "%d", conn_from);
1171                        out += buf2;
1172                        sprintf(buf2, ":%g]", conn_weight);
1173                        out += buf2;
1174                }
1175                else if (name == ":")
1176                {
1177                        sprintf(buf2, ":%c%c:", prop_increase ? '+' : '-', prop_symbol);
1178                        out += buf2;
1179                }
1180                else if (neuclass != NULL)
1181                {
1182                        out += "N:";
1183                        out += neuclass->name.c_str();
1184                }
1185                else
1186                {
1187                        out += name.c_str();
1188                }
1189        }
1190
1191        if (child != NULL)
1192                child->sprint(out);
1193        // if two children, make sure last char is a '>'
1194        if (childCount() == 2)
1195                if (out[0] == 0) out += ">"; else
1196                        if (out[out.length() - 1] != '>') out += ">";
1197
1198        if (child2 != NULL)
1199                child2->sprint(out);
1200        // make sure last char is a '>'
1201        if (out[0] == 0) out += ">"; else
1202                if (out[out.length() - 1] != '>') out += ">";
1203}
1204
1205void f4_Node::sprintAdj(char *& buf)
1206{
1207        unsigned int len;
1208        // build in a SString, with initial size
1209        SString out;
1210        out.reserve(int(strlen(buf)) + 2000);
1211
1212        sprint(out);
1213
1214        // very last '>' can be omitted
1215        len = out.length();
1216        if (len > 1)
1217                if (out[len - 1] == '>') { (out.directWrite())[len - 1] = 0; out.endWrite(); }; //Macko 2023-04 "can be omitted", but it is removed as a rule even in generated genotypes :)
1218        // copy back to string
1219        // if new is longer, reallocate buf
1220        if (len + 1 > strlen(buf))
1221        {
1222                buf = (char*)realloc(buf, len + 1);
1223        }
1224        strcpy(buf, out.c_str());
1225}
1226
1227f4_Node* f4_Node::duplicate()
1228{
1229        f4_Node *copy;
1230        copy = new f4_Node(*this);
1231        copy->parent = NULL;  // set later
1232        copy->child = NULL;
1233        copy->child2 = NULL;
1234        if (child != NULL)
1235        {
1236                copy->child = child->duplicate();
1237                copy->child->parent = copy;
1238        }
1239        if (child2 != NULL)
1240        {
1241                copy->child2 = child2->duplicate();
1242                copy->child2->parent = copy;
1243        }
1244        return copy;
1245}
1246
1247
1248void f4_Node::destroy()
1249{
1250        // children are destroyed (recursively) through the destructor
1251        if (child2 != NULL) delete child2;
1252        if (child != NULL) delete child;
1253}
1254
1255// scan genotype string and build tree
1256// return >1 for error (errorpos)
1257int f4_processRecur(const char* genot, int &pos_inout, f4_Node *parent)
1258{
1259        f4_Node *par = parent;
1260
1261        if (pos_inout >= (int)strlen(genot))
1262                return (int)strlen(genot) + 1;
1263
1264        while (pos_inout < (int)strlen(genot))
1265        {
1266//#define PRINT_PARSING_LOCATION
1267#ifdef PRINT_PARSING_LOCATION
1268                printf("%s\n", genot);
1269                for (int i = 0; i < pos_inout; i++) printf(" ");
1270                printf("^\n");
1271#endif
1272                switch (genot[pos_inout])
1273                {
1274                case '<':
1275                {
1276                        f4_Node *node = new f4_Node("<", par, pos_inout);
1277                        par = node;
1278                        pos_inout++; //move after '<'
1279                        int res = f4_processRecur(genot, pos_inout, par);
1280                        if (res) return res;
1281                        if (pos_inout < (int)strlen(genot))
1282                        {
1283                                res = f4_processRecur(genot, pos_inout, par);
1284                                if (res) return res;
1285                        }
1286                        else // ran out
1287                        {
1288                                //MacKo 2023-04, more strict behavior: instead of silent repair (no visible effect to the user, genotype stays invalid but is interpreted and reported as valid), we now point out where the error is. For example <X> or <X><X or <X><N:N>
1289                                return (int)strlen(genot) + 1;
1290                                //old silent repair:
1291                                //node = new f4_Node(">", par, int(strlen(genot)) - 1);
1292                        }
1293                        return 0;  // OK
1294                }
1295                case '>':
1296                {
1297                        new f4_Node(">", par, pos_inout);
1298                        pos_inout++; //move after '>'
1299                        return 0;  // OK
1300                }
1301                case '#':
1302                {
1303                        // repetition marker
1304                        ExtValue reps;
1305                        const char* end = reps.parseNumber(genot + pos_inout + 1, ExtPType::TInt);
1306                        if (end == NULL)
1307                                return pos_inout + 1; //error
1308                        f4_Node *node = new f4_Node("#", par, pos_inout);
1309                        node->reps = reps.getInt();
1310                        // skip number
1311                        pos_inout += end - (genot + pos_inout);
1312                        int res = f4_processRecur(genot, pos_inout, node);
1313                        if (res) return res;
1314                        if (pos_inout < (int)strlen(genot))
1315                        {
1316                                res = f4_processRecur(genot, pos_inout, node);
1317                                if (res) return res;
1318                        }
1319                        else // ran out
1320                        {
1321                                return (int)strlen(genot) + 1; //MacKo 2023-04: report an error, better to be more strict instead of a silent repair (genotype stays invalid but is interpreted and reported as valid) with non-obvious consequences?
1322                                //earlier apporach - silently treating this problem (we don't ever see where the error is because it gets corrected in some way here, while parsing the genotype, and error location in the genotype is never reported):
1323                                //node = new f4_Node(">", par, int(strlen(genot)) - 1); // check if needed and if this is really the best repair operation; seemed to happen too many times in succession for some genotypes even though they were only a result of f4 operators, not manually created... and the operators should not generate invalid genotypes, right? Or maybe crossover does? Seems like too many #N's for closing >'s; removing #N or adding > helped. Operators somehow don't do it properly sometimes? But F4_ADD_REP adds '>'... (TODO)
1324                        }
1325                        return 0;  // OK
1326                }
1327                case ' ':
1328                case '\n':
1329                case '\r':
1330                case '\t':
1331                {
1332                        // whitespace: ignore
1333                        pos_inout++;
1334                        break;
1335                }
1336                case 'N':
1337                {
1338                        int forgenorange = pos_inout;
1339                        if (genot[pos_inout + 1] != ':')
1340                                return pos_inout + 1; //error
1341                        pos_inout += 2; //skipping "N:"
1342                        unsigned int neuroclass_begin = pos_inout;
1343                        char* neuroclass_end = (char*)genot + neuroclass_begin;
1344                        NeuroClass *neuclass = GenoOperators::parseNeuroClass(neuroclass_end, ModelEnum::SHAPETYPE_BALL_AND_STICK); //advances neuroclass_end
1345                        if (neuclass == NULL)
1346                                return pos_inout + 1; //error
1347                        pos_inout += neuroclass_end - genot - neuroclass_begin;
1348                        string neutype = string(genot + neuroclass_begin, genot + pos_inout);
1349                        f4_Node *node = new f4_Node(neutype, par, forgenorange);
1350                        node->neuclass = neuclass;
1351                        par = node;
1352                        // if it continues with a colon that determines a neuron parameter (e.g. N:N:+=: ), then let the switch case for colon handle this
1353                        break;
1354                }
1355                case ':':
1356                {
1357                        // neuron parameter  +! -! += -= +/ or -/
1358                        // 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
1359                        char prop_dir, prop_symbol, prop_end[2]; // prop_end is only to ensure that neuron parameter definition is completed
1360                        if (sscanf(genot + pos_inout, ":%c%c%1[:]", &prop_dir, &prop_symbol, &prop_end) != 3)
1361                                // error: incorrect format
1362                                return pos_inout + 1 + 1;
1363                        if (prop_dir != '-' && prop_dir != '+')
1364                                return pos_inout + 1 + 1; //error
1365                        switch (prop_symbol)
1366                        {
1367                        case '!':  case '=':  case '/':  break;
1368                        default:
1369                                return pos_inout + 1 + 1; //error
1370                        }
1371                        f4_Node *node = new f4_Node(":", par, pos_inout);
1372                        node->prop_symbol = prop_symbol;
1373                        node->prop_increase = prop_dir == '+' ? true : false; // + or -
1374                        par = node;
1375                        pos_inout += 4; //skipping :ds:
1376                        break;
1377                }
1378                case '[':
1379                {
1380                        double weight = 0;
1381                        int relfrom;
1382                        const char *end = parseConnection(genot + pos_inout, relfrom, weight);
1383                        if (end == NULL)
1384                                return pos_inout + 1; //error
1385
1386                        f4_Node *node = new f4_Node("[", par, pos_inout);
1387                        node->conn_from = relfrom;
1388                        node->conn_weight = weight;
1389                        par = node;
1390                        pos_inout += end - (genot + pos_inout);
1391                        break;
1392                }
1393                default: // 'X' and ',' and all modifiers and also invalid symbols - add a node, for invalid symbols build will give the error or repair
1394                {
1395                        //printf("any regular character '%c'\n", genot[pos_inout]);
1396                        //TODO here: read a continuous sequence of modifiers, sort and optimize ("collapse") it like in f1, then add to tree
1397                        f4_Node *node = new f4_Node(genot[pos_inout], par, pos_inout);
1398                        par = node;
1399                        pos_inout++;
1400                        break;
1401                }
1402                }
1403        }
1404
1405        // should end with a '>'
1406        if (par && par->name != ">")
1407        {
1408                //happens when pos_inout == strlen(genot)
1409                //return pos_inout; //MacKo 2023-04: could report an error instead of silent repair, but repair operators only work in Cells (i.e., after the f4_Node tree has been parsed without errors and Cells can start developing) so we don't want to make a fatal error because of missing '>' here. Also after conversions from Cells to text, trailing '>' is deliberately removed... and also the simplest genotype is officially X, not X>.
1410                new f4_Node('>', par, int(strlen(genot)) - 1);
1411        }
1412
1413        return 0;  // OK
1414}
1415
1416int f4_process(const char *genot, f4_Node *root)
1417{
1418        int pos = 0;
1419        int res = f4_processRecur(genot, pos, root);
1420        if (res > 0)
1421                return res; //parsing error
1422        else if (genot[pos] != 0)
1423                return pos + 1; //parsing OK but junk, unparsed genes left, for example /*4*/<X>N:N>whatever or /*4*/<X>X>>>
1424        else
1425                return 0; //parsing OK and parsed until the end
1426}
1427
1428const char* parseConnection(const char *fragm, int& relfrom, double &weight)
1429{
1430        const char *parser = fragm;
1431        if (*parser != '[') return NULL;
1432        parser++;
1433        ExtValue val;
1434        parser = val.parseNumber(parser, ExtPType::TInt);
1435        if (parser == NULL) return NULL;
1436        relfrom = val.getInt();
1437        if (*parser != ':') return NULL;
1438        parser++;
1439        parser = val.parseNumber(parser, ExtPType::TDouble);
1440        if (parser == NULL) return NULL;
1441        weight = val.getDouble();
1442        if (*parser != ']') return NULL;
1443        parser++;
1444        return parser;
1445}
1446
1447/*
1448f4_Node* f4_processTree(const char* geno)
1449{
1450        f4_Node *root = new f4_Node();
1451        int res = f4_processRecur(geno, 0, root);
1452        if (res) return NULL;
1453        //DB( printf("test f4  "); )
1454        DB(
1455                if (root->child)
1456                {
1457                        char* buf = (char*)malloc(300);
1458                        DB(printf("(%d) ", root->child->count());)
1459                                buf[0] = 0;
1460                        root->child->sprintAdj(buf);
1461                        DB(printf("%s\n", buf);)
1462                                free(buf);
1463                }
1464        )
1465                return root->child;
1466}
1467*/
Note: See TracBrowser for help on using the repository browser.