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

Last change on this file since 1231 was 1231, checked in by Maciej Komosinski, 12 months ago
  • Thanks to r1230, it is possible to detect (and repair=remove) junk trailing genes that are left after successful parsing (after last '>')
  • The validate() function may attempt to repair a genotype where earlier it would give up
  • Stricter parsing of the '#' gene
  • Property svn:eol-style set to native
File size: 38.2 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 *rem)
798{
799        if (!repair)
800        {
801                // not in repair mode, treat as repairable error
802                errorcode = GENOPER_REPAIR;
803                errorpos = nerrpos;
804        }
805        else
806        {
807                errorcode = GENOPER_REPAIR;
808                errorpos = nerrpos;
809                repair_remove = rem;
810        }
811}
812
813int f4_Cells::setRepairInsert(int nerrpos, f4_Node *parent, f4_Node *insert)
814{
815        if (!repair)
816        {
817                // not in repair mode, treat as repairable error
818                errorcode = GENOPER_REPAIR;
819                errorpos = nerrpos;
820                return -1;
821        }
822        else
823        {
824                errorcode = GENOPER_REPAIR;
825                errorpos = nerrpos;
826                repair_parent = parent;
827                repair_insert = insert;
828                return 0;
829        }
830}
831
832void f4_Cells::repairGeno(f4_Node *geno, int whichchild)
833{
834        // assemble repaired geno, if the case
835        if (!repair) return;
836        if ((repair_remove == NULL) && (repair_insert == NULL)) return;
837        // traverse genotype tree, remove / insert node
838        f4_Node *g2;
839        if (whichchild == 1)
840                g2 = geno->child;
841        else
842                g2 = geno->child2;
843        if (g2 == NULL)
844                return;
845        if (g2 == repair_remove)
846        {
847                f4_Node *oldgeno;
848                geno->removeChild(g2);
849                if (g2->child)
850                {
851                        // add g2->child as child to geno
852                        if (whichchild == 1)
853                                geno->child = g2->child;
854                        else
855                                geno->child2 = g2->child;
856                        g2->child->parent = geno;
857                }
858                oldgeno = g2;
859                oldgeno->child = NULL;
860                delete oldgeno;
861                if (geno->child == NULL) return;
862                // check this new
863                repairGeno(geno, whichchild);
864                return;
865        }
866        if (g2 == repair_parent)
867        {
868                geno->removeChild(g2);
869                geno->addChild(repair_insert);
870                repair_insert->parent = geno;
871                repair_insert->child = g2;
872                repair_insert->child2 = NULL;
873                g2->parent = repair_insert;
874        }
875        // recurse
876        if (g2->child)  repairGeno(g2, 1);
877        if (g2->child2) repairGeno(g2, 2);
878}
879
880
881void f4_Cells::toF1Geno(SString &out)
882{
883        if (tmpcel) delete tmpcel;
884        tmpcel = new f4_Cell(-1, NULL, 0, GeneProps::standard_values);
885        out = "";
886        toF1GenoRec(0, out);
887        delete tmpcel;
888}
889
890
891void f4_Cells::toF1GenoRec(int curc, SString &out)
892{
893        int i, j, ccount;
894        f4_Cell *thisti;
895        f4_Cell *thneu;
896        char buf[200];
897
898        if (curc >= cell_count) return;
899
900        if (C[curc]->type != CELL_STICK) return;
901
902        thisti = C[curc];
903        if (thisti->dadlink != NULL)
904                *tmpcel = *(thisti->dadlink);
905
906        // adjust length, curvedness, etc.
907        tmpcel->P.propagateAlong(false);
908        while (tmpcel->P.length > thisti->P.length)
909        {
910                tmpcel->P.executeModifier('l');
911                out += "l";
912        }
913        while (tmpcel->P.length < thisti->P.length)
914        {
915                tmpcel->P.executeModifier('L');
916                out += "L";
917        }
918        while (tmpcel->P.curvedness > thisti->P.curvedness)
919        {
920                tmpcel->P.executeModifier('c');
921                out += "c";
922        }
923        while (tmpcel->P.curvedness < thisti->P.curvedness)
924        {
925                tmpcel->P.executeModifier('C');
926                out += "C";
927        }
928        while (thisti->rolling > 0.0f)
929        {
930                rolling_dec(&(thisti->rolling));
931                out += "R";
932        }
933        while (thisti->rolling < 0.0f)
934        {
935                rolling_inc(&(thisti->rolling));
936                out += "r";
937        }
938
939        // output X for this stick
940        out += "X";
941
942        // neurons attached to it
943        for (i = 0; i < cell_count; i++)
944        {
945                if (C[i]->type == CELL_NEURON)
946                {
947                        if (C[i]->dadlink == thisti)
948                        {
949                                thneu = C[i];
950                                out += "[";
951                                // ctrl
952                                //if (1 == thneu->ctrl) out += "@"; // old code; this can be easily generalized to any neuroclass if ever needed
953                                //if (2 == thneu->ctrl) out += "|";
954                                out += thneu->neuclass->name.c_str(); // not tested, but something like that
955                                // connections
956                                for (j = 0; j < thneu->conns_count; j++)
957                                {
958                                        if (j) out += ",";
959                                        sprintf(buf, "%d", thneu->conns[j]->from->nr - thneu->nr);
960                                        out += buf;
961                                        out += ":";
962                                        // connection weight
963                                        sprintf(buf, "%g", thneu->conns[j]->weight);
964                                        out += buf;
965                                }
966                                out += "]";
967                        }
968                }
969        }
970
971        // sticks connected to it
972        if (thisti->commacount >= 2)
973                out += "(";
974
975        ccount = 1;
976        for (i = 0; i < cell_count; i++)
977        {
978                if (C[i]->type == CELL_STICK)
979                {
980                        if (C[i]->dadlink == thisti)
981                        {
982                                while (ccount < (C[i])->anglepos)
983                                {
984                                        ccount++;
985                                        out += ",";
986                                }
987                                toF1GenoRec(i, out);
988                        }
989                }
990        }
991
992        while (ccount < thisti->commacount)
993        {
994                ccount++;
995                out += ",";
996        }
997
998        if (thisti->commacount >= 2)
999                out += ")";
1000}
1001
1002
1003
1004// to organize an f4 genotype in a tree structure
1005
1006f4_Node::f4_Node()
1007{
1008        name = "?";
1009        parent = NULL;
1010        child = NULL;
1011        child2 = NULL;
1012        pos = -1;
1013
1014        reps = 0;
1015        prop_symbol = '\0';
1016        prop_increase = false;
1017        conn_from = 0;
1018        conn_weight = 0.0;
1019        neuclass = NULL;
1020}
1021
1022f4_Node::f4_Node(string nname, f4_Node *nparent, int npos)
1023{
1024        name = nname;
1025        parent = nparent;
1026        child = NULL;
1027        child2 = NULL;
1028        pos = npos;
1029        if (parent) parent->addChild(this);
1030
1031        reps = 0;
1032        prop_symbol = '\0';
1033        prop_increase = false;
1034        conn_from = 0;
1035        conn_weight = 0.0;
1036        neuclass = NULL;
1037}
1038
1039f4_Node::f4_Node(char nname, f4_Node *nparent, int npos)
1040{
1041        name = nname;
1042        parent = nparent;
1043        child = NULL;
1044        child2 = NULL;
1045        pos = npos;
1046        if (parent) parent->addChild(this);
1047
1048        reps = 0;
1049        prop_symbol = '\0';
1050        prop_increase = false;
1051        conn_from = 0;
1052        conn_weight = 0.0;
1053        neuclass = NULL;
1054}
1055
1056f4_Node::~f4_Node()
1057{
1058        destroy();
1059}
1060
1061void f4_Node::print_tree(const f4_Node *root, int indent)
1062{
1063        for (int i = 0; i < indent; i++) printf(" ");
1064        printf("%s (%d)", root->name.c_str(), root->count());
1065        if (root->name == "[")
1066                printf("     from=%-3d  weight=%g", root->conn_from, root->conn_weight);
1067        printf("\n");
1068        if (root->child) print_tree(root->child, indent + 1);
1069        if (root->child2) print_tree(root->child2, indent + 1);
1070}
1071
1072int f4_Node::addChild(f4_Node *nchi)
1073{
1074        if (child == NULL)
1075        {
1076                child = nchi;
1077                return 0;
1078        }
1079        if (child2 == NULL)
1080        {
1081                child2 = nchi;
1082                return 0;
1083        }
1084        return -1;
1085}
1086
1087int f4_Node::removeChild(f4_Node *nchi)
1088{
1089        if (nchi == child2)
1090        {
1091                child2 = NULL;
1092                return 0;
1093        }
1094        if (nchi == child)
1095        {
1096                child = NULL;
1097                return 0;
1098        }
1099        return -1;
1100}
1101
1102int f4_Node::childCount()
1103{
1104        return int(child != NULL) + int(child2 != NULL); //0, 1 or 2
1105}
1106
1107int f4_Node::count() const
1108{
1109        int c = 1;
1110        if (child != NULL)  c += child->count();
1111        if (child2 != NULL) c += child2->count();
1112        return c;
1113}
1114
1115f4_Node* f4_Node::ordNode(int n)
1116{
1117        int n1;
1118        if (n == 0) return this;
1119        n--;
1120        if (child != NULL)
1121        {
1122                n1 = child->count();
1123                if (n < n1) return child->ordNode(n);
1124                n -= n1;
1125        }
1126        if (child2 != NULL)
1127        {
1128                n1 = child2->count();
1129                if (n < n1) return child2->ordNode(n);
1130                n -= n1;
1131        }
1132        return NULL;
1133}
1134
1135f4_Node* f4_Node::randomNode()
1136{
1137        int n = count();
1138        // pick a random node between 0 and n-1
1139        return ordNode(rndUint(n));
1140}
1141
1142f4_Node* f4_Node::randomNodeWithSize(int mn, int mx)
1143{
1144        // try random nodes, and accept if size in range
1145        // limit to maxlim tries
1146        int i, n, maxlim;
1147        f4_Node *nod = NULL;
1148        maxlim = count();
1149        for (i = 0; i < maxlim; i++)
1150        {
1151                nod = randomNode();
1152                n = nod->count();
1153                if ((n >= mn) && (n <= mx)) return nod;
1154        }
1155        // failed, doesn't matter
1156        return nod;
1157}
1158
1159void f4_Node::sprint(SString& out)
1160{
1161        char buf2[20];
1162        // special case: repetition code
1163        if (name == "#")
1164        {
1165                out += "#";
1166                sprintf(buf2, "%d", reps);
1167                out += buf2;
1168        }
1169        else {
1170                // special case: neuron connection
1171                if (name == "[")
1172                {
1173                        out += "[";
1174                        sprintf(buf2, "%d", conn_from);
1175                        out += buf2;
1176                        sprintf(buf2, ":%g]", conn_weight);
1177                        out += buf2;
1178                }
1179                else if (name == ":")
1180                {
1181                        sprintf(buf2, ":%c%c:", prop_increase ? '+' : '-', prop_symbol);
1182                        out += buf2;
1183                }
1184                else if (neuclass != NULL)
1185                {
1186                        out += "N:";
1187                        out += neuclass->name.c_str();
1188                }
1189                else
1190                {
1191                        out += name.c_str();
1192                }
1193        }
1194
1195        if (child != NULL)
1196                child->sprint(out);
1197        // if two children, make sure last char is a '>'
1198        if (childCount() == 2)
1199                if (out[0] == 0) out += ">"; else
1200                        if (out[out.length() - 1] != '>') out += ">";
1201
1202        if (child2 != NULL)
1203                child2->sprint(out);
1204        // make sure last char is a '>'
1205        if (out[0] == 0) out += ">"; else
1206                if (out[out.length() - 1] != '>') out += ">";
1207}
1208
1209void f4_Node::sprintAdj(char *& buf)
1210{
1211        unsigned int len;
1212        // build in a SString, with initial size
1213        SString out;
1214        out.reserve(int(strlen(buf)) + 2000);
1215
1216        sprint(out);
1217
1218        // very last '>' can be omitted
1219        len = out.length();
1220        if (len > 1)
1221                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 :)
1222        // copy back to string
1223        // if new is longer, reallocate buf
1224        if (len + 1 > strlen(buf))
1225        {
1226                buf = (char*)realloc(buf, len + 1);
1227        }
1228        strcpy(buf, out.c_str());
1229}
1230
1231f4_Node* f4_Node::duplicate()
1232{
1233        f4_Node *copy;
1234        copy = new f4_Node(*this);
1235        copy->parent = NULL;  // set later
1236        copy->child = NULL;
1237        copy->child2 = NULL;
1238        if (child != NULL)
1239        {
1240                copy->child = child->duplicate();
1241                copy->child->parent = copy;
1242        }
1243        if (child2 != NULL)
1244        {
1245                copy->child2 = child2->duplicate();
1246                copy->child2->parent = copy;
1247        }
1248        return copy;
1249}
1250
1251
1252void f4_Node::destroy()
1253{
1254        // children are destroyed (recursively) through the destructor
1255        if (child2 != NULL) delete child2;
1256        if (child != NULL) delete child;
1257}
1258
1259// scan genotype string and build tree
1260// return >1 for error (errorpos)
1261int f4_processRecur(const char* genot, int &pos_inout, f4_Node *parent)
1262{
1263        f4_Node *par = parent;
1264
1265        if (pos_inout >= (int)strlen(genot))
1266                return (int)strlen(genot) + 1;
1267
1268        while (pos_inout < (int)strlen(genot))
1269        {
1270//#define PRINT_PARSING_LOCATION
1271#ifdef PRINT_PARSING_LOCATION
1272                printf("%s\n", genot);
1273                for (int i = 0; i < pos_inout; i++) printf(" ");
1274                printf("^\n");
1275#endif
1276                switch (genot[pos_inout])
1277                {
1278                case '<':
1279                {
1280                        f4_Node *node = new f4_Node("<", par, pos_inout);
1281                        par = node;
1282                        pos_inout++; //move after '<'
1283                        int res = f4_processRecur(genot, pos_inout, par);
1284                        if (res) return res;
1285                        if (pos_inout < (int)strlen(genot))
1286                        {
1287                                res = f4_processRecur(genot, pos_inout, par);
1288                                if (res) return res;
1289                        }
1290                        else // ran out
1291                        {
1292                                //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>
1293                                return (int)strlen(genot) + 1;
1294                                //old silent repair:
1295                                //node = new f4_Node(">", par, int(strlen(genot)) - 1);
1296                        }
1297                        return 0;  // OK
1298                }
1299                case '>':
1300                {
1301                        new f4_Node(">", par, pos_inout);
1302                        pos_inout++; //move after '>'
1303                        return 0;  // OK
1304                }
1305                case '#':
1306                {
1307                        // repetition marker
1308                        ExtValue reps;
1309                        const char* end = reps.parseNumber(genot + pos_inout + 1, ExtPType::TInt);
1310                        if (end == NULL)
1311                                return pos_inout + 1; //error
1312                        f4_Node *node = new f4_Node("#", par, pos_inout);
1313                        node->reps = reps.getInt();
1314                        // skip number
1315                        pos_inout += end - (genot + pos_inout);
1316                        int res = f4_processRecur(genot, pos_inout, node);
1317                        if (res) return res;
1318                        if (pos_inout < (int)strlen(genot))
1319                        {
1320                                res = f4_processRecur(genot, pos_inout, node);
1321                                if (res) return res;
1322                        }
1323                        else // ran out
1324                        {
1325                                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?
1326                                //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):
1327                                //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)
1328                        }
1329                        return 0;  // OK
1330                }
1331                case ' ':
1332                case '\n':
1333                case '\r':
1334                case '\t':
1335                {
1336                        // whitespace: ignore
1337                        pos_inout++;
1338                        break;
1339                }
1340                case 'N':
1341                {
1342                        int forgenorange = pos_inout;
1343                        if (genot[pos_inout + 1] != ':')
1344                                return pos_inout + 1; //error
1345                        pos_inout += 2; //skipping "N:"
1346                        unsigned int neuroclass_begin = pos_inout;
1347                        char* neuroclass_end = (char*)genot + neuroclass_begin;
1348                        NeuroClass *neuclass = GenoOperators::parseNeuroClass(neuroclass_end, ModelEnum::SHAPETYPE_BALL_AND_STICK); //advances neuroclass_end
1349                        if (neuclass == NULL)
1350                                return pos_inout + 1; //error
1351                        pos_inout += neuroclass_end - genot - neuroclass_begin;
1352                        string neutype = string(genot + neuroclass_begin, genot + pos_inout);
1353                        f4_Node *node = new f4_Node(neutype, par, forgenorange);
1354                        node->neuclass = neuclass;
1355                        par = node;
1356                        // if it continues with a colon that determines a neuron parameter (e.g. N:N:+=: ), then let the switch case for colon handle this
1357                        break;
1358                }
1359                case ':':
1360                {
1361                        // neuron parameter  +! -! += -= +/ or -/
1362                        // 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
1363                        char prop_dir, prop_symbol, prop_end[2]; // prop_end is only to ensure that neuron parameter definition is completed
1364                        if (sscanf(genot + pos_inout, ":%c%c%1[:]", &prop_dir, &prop_symbol, &prop_end) != 3)
1365                                // error: incorrect format
1366                                return pos_inout + 1 + 1;
1367                        if (prop_dir != '-' && prop_dir != '+')
1368                                return pos_inout + 1 + 1; //error
1369                        switch (prop_symbol)
1370                        {
1371                        case '!':  case '=':  case '/':  break;
1372                        default:
1373                                return pos_inout + 1 + 1; //error
1374                        }
1375                        f4_Node *node = new f4_Node(":", par, pos_inout);
1376                        node->prop_symbol = prop_symbol;
1377                        node->prop_increase = prop_dir == '+' ? true : false; // + or -
1378                        par = node;
1379                        pos_inout += 4; //skipping :ds:
1380                        break;
1381                }
1382                case '[':
1383                {
1384                        double weight = 0;
1385                        int relfrom;
1386                        const char *end = parseConnection(genot + pos_inout, relfrom, weight);
1387                        if (end == NULL)
1388                                return pos_inout + 1; //error
1389
1390                        f4_Node *node = new f4_Node("[", par, pos_inout);
1391                        node->conn_from = relfrom;
1392                        node->conn_weight = weight;
1393                        par = node;
1394                        pos_inout += end - (genot + pos_inout);
1395                        break;
1396                }
1397                default: // 'X' and ',' and all modifiers and also invalid symbols - add a node, for invalid symbols build will give the error or repair
1398                {
1399                        //printf("any regular character '%c'\n", genot[pos_inout]);
1400                        //TODO here: read a continuous sequence of modifiers, sort and optimize ("collapse") it like in f1, then add to tree
1401                        f4_Node *node = new f4_Node(genot[pos_inout], par, pos_inout);
1402                        par = node;
1403                        pos_inout++;
1404                        break;
1405                }
1406                }
1407        }
1408
1409        // should end with a '>'
1410        if (par && par->name != ">")
1411        {
1412                //happens when pos_inout == strlen(genot)
1413                //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>.
1414                new f4_Node('>', par, int(strlen(genot)) - 1);
1415        }
1416
1417        return 0;  // OK
1418}
1419
1420int f4_process(const char *genot, f4_Node *root)
1421{
1422        int pos = 0;
1423        int res = f4_processRecur(genot, pos, root);
1424        if (res > 0)
1425                return res; //error
1426        else if (genot[pos] == 0) //parsed until the end - OK!
1427                return 0;
1428        else return pos + 1; //junk, unparsed genes after successful parsing, for example /*4*/<X>N:N>whatever or /*4*/<X>X>>>
1429}
1430
1431const char* parseConnection(const char *fragm, int& relfrom, double &weight)
1432{
1433        const char *parser = fragm;
1434        if (*parser != '[') return NULL;
1435        parser++;
1436        ExtValue val;
1437        parser = val.parseNumber(parser, ExtPType::TInt);
1438        if (parser == NULL) return NULL;
1439        relfrom = val.getInt();
1440        if (*parser != ':') return NULL;
1441        parser++;
1442        parser = val.parseNumber(parser, ExtPType::TDouble);
1443        if (parser == NULL) return NULL;
1444        weight = val.getDouble();
1445        if (*parser != ']') return NULL;
1446        parser++;
1447        return parser;
1448}
1449
1450/*
1451f4_Node* f4_processTree(const char* geno)
1452{
1453        f4_Node *root = new f4_Node();
1454        int res = f4_processRecur(geno, 0, root);
1455        if (res) return NULL;
1456        //DB( printf("test f4  "); )
1457        DB(
1458                if (root->child)
1459                {
1460                        char* buf = (char*)malloc(300);
1461                        DB(printf("(%d) ", root->child->count());)
1462                                buf[0] = 0;
1463                        root->child->sprintAdj(buf);
1464                        DB(printf("%s\n", buf);)
1465                                free(buf);
1466                }
1467        )
1468                return root->child;
1469}
1470*/
Note: See TracBrowser for help on using the repository browser.