source: cpp/frams/genetics/f1/f1_conv.cpp @ 1244

Last change on this file since 1244 was 1244, checked in by Maciej Komosinski, 11 months ago

Removed scaling when calculating Creature (Model) energy from the energy that results from the "Ee" modifiers

  • Property svn:eol-style set to native
File size: 14.0 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2021  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "f1_conv.h"
6#include <common/log.h>
7#include <frams/util/multirange.h>
8#include <frams/util/multimap.h>
9#include <frams/genetics/geneprops.h>
10#include <ctype.h>
11#include <assert.h>
12#include <algorithm>
13
14//#define v1f1COMPATIBLE //as in ancient Framsticks 1.x
15
16class Builder
17{
18public:
19        Builder(const char*g, int mapping = 0) :invalid(0), genbegin(g), usemapping(mapping), first_part_mapping(NULL), own_first_part_mapping(true), model_energy(0), model_energy_count(0) {}
20        ~Builder() { if (own_first_part_mapping) SAFEDELETE(first_part_mapping); }
21        char tmp[222];
22        bool invalid;
23        Model model;
24        const char *genbegin;
25        SList neuro_f1_to_f0; // neuro_f1_to_f0(f1_refno) = actual neuro pointer
26        Neuro *last_f1_neuro;
27        SyntParam *neuro_cls_param;
28
29        struct Connection
30        {
31                int n1, n2; double w;
32                Connection(int _n1, int _n2, double _w) :n1(_n1), n2(_n2), w(_w) {}
33        };
34
35        SListTempl<Connection> connections;
36        int usemapping;
37        MultiRange range;
38        MultiRange *first_part_mapping;
39        bool own_first_part_mapping;
40        double lastjoint_muscle_power;
41        double model_energy;
42        int model_energy_count;
43        void grow(int part1, const char*g, Pt3D k, GeneProps c, int branching_part);
44        void setPartMapping(int p, const char* g);
45        int growJoint(int part1, int part2, Pt3D &angle, GeneProps &c, const char *g);
46        int growPart(GeneProps &c, const char *g);
47        const char *skipNeuro(const char *z);
48        const char* growNeuro(const char* t, GeneProps &c, int&);
49        void growConnection(const char* begin, const char* colon, const char* end, GeneProps& props);
50        int countBranches(const char*g, SList &out);
51        SyntParam* lastNeuroClassParam();
52        void addClassParam(const char* name, double value);
53        void addClassParam(const char* name, const char* value);
54
55        const MultiRange* makeRange(const char*g) { return makeRange(g, g); }
56        const MultiRange* makeRange(const char*g, const char*g2);
57        Part *getLastPart() { return getLastJoint()->part2; }
58        Neuro *getLastNeuro() { return model.getNeuro(model.getNeuroCount() - 1); }
59        Joint *getLastJoint() { return model.getJoint(model.getJointCount() - 1); }
60        void addOrRememberInput(int n1, int n2, double w)
61        {
62                //if (!addInput(n1,n2,w,false))
63                connections += Connection(n1, n2, w);
64        }
65        bool addInput(int n1, int n2, double w, bool final)
66        {
67                if ((n1 < 0) || (n2 < 0) || (n1 >= neuro_f1_to_f0.size()) || (n2 >= neuro_f1_to_f0.size()))
68                {
69                        if (final) logPrintf("GenoConvF1", "addInput", LOG_WARN,
70                                "illegal neuron connection %d <- %d (ignored)", n1, n2);
71                        return 0;
72                }
73                Neuro *neuro = (Neuro*)neuro_f1_to_f0(n1);
74                Neuro *input = (Neuro*)neuro_f1_to_f0(n2);
75                neuro->addInput(input, w);
76                return 1;
77        }
78        void addPendingInputs()
79        {
80                for (int i = 0; i < connections.size(); i++)
81                {
82                        Connection *c = &connections(i);
83                        addInput(c->n1, c->n2, c->w, true);
84                }
85        }
86};
87
88const MultiRange* Builder::makeRange(const char*g, const char*g2)
89{
90        if (!usemapping) return 0;
91        range.clear();
92        range.add(g - genbegin, g2 - genbegin);
93        return &range;
94}
95
96/** main conversion function - with conversion map support */
97SString GenoConv_f1::convert(SString &i, MultiMap *map, bool using_checkpoints)
98{
99        const char* g = i.c_str();
100        Builder builder(g, map ? 1 : 0);
101        builder.model.open(using_checkpoints);
102        builder.grow(-1, g, Pt3D_0, GeneProps::standard_values, -1); // uses Model::addFromString() to create model elements
103        if (builder.invalid) return SString();
104        builder.addPendingInputs();
105        builder.model.startenergy = (builder.model_energy_count > 0) ? (builder.model_energy / builder.model_energy_count) : 1.0;
106        builder.model.close(); // model is ready to use now
107        if (map) builder.model.getCurrentToF0Map(*map); // generate f1-to-f0 conversion map
108        return builder.model.getF0Geno().getGenes();
109}
110
111void Builder::setPartMapping(int p, const char* g)
112{
113        if (!usemapping) return;
114        const MultiRange *r = makeRange(g);
115        if (p < 0)
116        { //special case: mapping the part which is not yet created
117                if (first_part_mapping) first_part_mapping->add(*r);
118                else { first_part_mapping = new MultiRange(*r); own_first_part_mapping = true; }
119        }
120        else
121                model.getPart(p)->addMapping(*r);
122}
123
124void Builder::grow(int part1, const char*g, Pt3D k, GeneProps c, int branching_part)
125{
126        int hasmuscles = 0;
127        k += Pt3D(c.twist, 0, c.curvedness);
128        while (1)
129        {
130                if (c.executeModifier(*g) == 0)
131                {
132                        setPartMapping(part1, g);
133                }
134                else
135                {
136                        switch (*g)
137                        {
138                        case 0: return;
139                        case ',': case ')': setPartMapping(branching_part, g); return;
140                        case 'R': k.x += 0.7853; setPartMapping(part1, g); break; // 45 degrees = pi/4 like in f4
141                        case 'r': k.x -= 0.7853; setPartMapping(part1, g); break;
142                        case '[': //neuron
143                                //              setdebug(g-(char*)geny,DEBUGNEURO | !l_neu);
144                                if (model.getJointCount())
145                                        g = growNeuro(g + 1, c, hasmuscles);
146                                else
147                                {
148                                        logMessage("GenoConv_F1", "grow", 1, "Illegal neuron position (ignored)");
149                                        g = skipNeuro(g + 1);
150                                }
151                                break;
152                        case 'X':
153                        {
154                                int freshpart = 0;
155                                //setdebug(g-(char*)geny,DEBUGEST | !l_est);
156                                if (part1 < 0) //initial grow
157                                {
158                                        if (model.getPartCount() > 0)
159                                                part1 = 0;
160                                        else
161                                        {
162                                                part1 = growPart(c, g);
163                                                freshpart = 1;
164                                                if (first_part_mapping)
165                                                {
166                                                        //mapping was defined before creating this initial Part -> put it into the Part
167                                                        assert(own_first_part_mapping);
168                                                        model.getPart(part1)->setMapping(*first_part_mapping);
169                                                        delete first_part_mapping;
170                                                        //first_part_mapping can be still used later but from now on it references the internal Part mapping
171                                                        first_part_mapping = model.getPart(part1)->getMapping();
172                                                        own_first_part_mapping = false;
173                                                }
174                                        }
175                                }
176                                if (!freshpart)
177                                {
178                                        Part *part = model.getPart(part1);
179                                        part->density = ((part->mass*part->density) + 1.0 / c.weight) / (part->mass + 1.0); // v=m*d
180                                        //                      part->volume+=1.0/c.weight;
181                                        part->mass += 1.0;
182                                }
183                                model_energy += c.energy;
184                                model_energy_count++;
185
186                                int part2 = growPart(c, g);
187                                growJoint(part1, part2, k, c, g);
188                                //              est* e = new est(*s,*s2,k,c,zz,this);
189
190                                // attenuate properties as they are propagated along the structure
191                                c.propagateAlong(true);
192
193                                model.checkpoint();
194                                grow(part2, g + 1, Pt3D_0, c, branching_part);
195                                return;
196                        }
197                        case '(':
198                        {
199                                setPartMapping(part1, g);
200                                SList ga;
201                                int i, count;
202                                count = countBranches(g + 1, ga);
203                                c.muscle_reset_range = false;
204                                c.muscle_bend_range = 1.0 / count;
205                                for (i = 0; i < count; i++)
206                                        grow(part1, (char*)ga(i), k + Pt3D(0, 0, -M_PI + (i + 1)*(2 * M_PI / (count + 1))), c, part1);
207                                return;
208                        }
209                        case ' ': case '\t': case '\n': case '\r': break;
210                        default: invalid = 1; return;
211                        }
212                }
213                g++;
214        }
215}
216
217SyntParam* Builder::lastNeuroClassParam()
218{
219        if (!neuro_cls_param)
220        {
221                NeuroClass *cls = last_f1_neuro->getClass();
222                if (cls)
223                {
224                        neuro_cls_param = new SyntParam(last_f1_neuro->classProperties());
225                        // this is equivalent to:
226                        //              SyntParam tmp=last_f1_neuro->classProperties();
227                        //              neuro_cls_param=new SyntParam(tmp);
228                        // interestingly, some compilers eliminate the call to new SyntParam,
229                        // realizing that a copy constructor is redundant when the original object is
230                        // temporary. there are no side effect of such optimization, as long as the
231                        // copy-constructed object is exact equivalent of the original.
232                }
233        }
234        return neuro_cls_param;
235}
236
237void Builder::addClassParam(const char* name, double value)
238{
239        lastNeuroClassParam();
240        if (neuro_cls_param)
241                neuro_cls_param->setDoubleById(name, value);
242}
243
244void Builder::addClassParam(const char* name, const char* value)
245{
246        lastNeuroClassParam();
247        if (neuro_cls_param)
248        {
249                ExtValue e(value);
250                const ExtValue &re(e);
251                neuro_cls_param->setById(name, re);
252        }
253}
254
255int Builder::countBranches(const char*g, SList &out)
256{
257        int gl = 0;
258        out += (void*)g;
259        while (gl >= 0)
260        {
261                switch (*g)
262                {
263                case 0: gl = -1; break;
264                case '(': case '[': ++gl; break;
265                case ')': case ']': --gl; break;
266                case ',': if (!gl) out += (void*)(g + 1);
267                }
268                g++;
269        }
270        return out.size();
271}
272
273int Builder::growJoint(int part1, int part2, Pt3D &angle, GeneProps &c, const char *g)
274{
275        double len = std::min(2.0, c.length);
276        sprintf(tmp, "p1=%d,p2=%d,dx=%lg,rx=%lg,ry=%lg,rz=%lg,stam=%lg,vr=%g,vg=%g,vb=%g",
277                part1, part2, len, angle.x, angle.y, angle.z, c.stamina, c.cred, c.cgreen, c.cblue);
278        lastjoint_muscle_power = c.muscle_power;
279        return model.addFromString(Model::JointType, tmp, makeRange(g));
280}
281
282int Builder::growPart(GeneProps &c, const char *g)
283{
284        sprintf(tmp, "dn=%lg,fr=%lg,ing=%lg,as=%lg,vr=%g,vg=%g,vb=%g",
285                1.0 / c.weight, c.friction, c.ingestion, c.assimilation, c.cred, c.cgreen, c.cblue);
286        return model.addFromString(Model::PartType, tmp, makeRange(g));
287}
288
289const char *Builder::skipNeuro(const char *z)
290{
291        for (; *z; z++) if ((*z == ']') || (*z == ')')) break;
292        return z - 1;
293}
294
295const char* Builder::growNeuro(const char* t, GeneProps& props, int &hasmuscles)
296{
297        const char*neuroend = skipNeuro(t);
298        last_f1_neuro = model.addNewNeuro();
299        neuro_cls_param = NULL;
300        last_f1_neuro->attachToPart(getLastPart());
301        const MultiRange *mr = makeRange(t - 1, neuroend + 1);
302        if (mr) last_f1_neuro->addMapping(*mr);
303        neuro_f1_to_f0 += last_f1_neuro;
304
305        SString clsname;
306        bool haveclass = 0;
307        while (*t && *t <= ' ') t++;
308        const char* next = (*t) ? (t + 1) : t;
309        while (*next && *next <= ' ') next++;
310        if (*t && *next != ',' && *next != ']') // old style muscles [|rest] or [@rest]
311                switch (*t)
312        {
313                case '@': if (t[1] == ':') break;
314                        haveclass = 1;
315                        //              if (!(hasmuscles&1))
316                        {
317                                hasmuscles |= 1;
318                                Neuro *muscle = model.addNewNeuro();
319                                sprintf(tmp, "@:p=%lg", lastjoint_muscle_power);
320                                muscle->addInput(last_f1_neuro);
321                                muscle->setDetails(tmp);
322                                muscle->attachToJoint(getLastJoint());
323                                if (usemapping) muscle->addMapping(*makeRange(t));
324                        }
325                        t++;
326                        break;
327                case '|': if (t[1] == ':') break;
328                        haveclass = 1;
329                        //              if (!(hasmuscles&2))
330                        {
331                                hasmuscles |= 2;
332                                Neuro *muscle = model.addNewNeuro();
333                                sprintf(tmp, "|:p=%lg,r=%lg", lastjoint_muscle_power, props.muscle_bend_range);
334                                muscle->addInput(last_f1_neuro);
335                                muscle->setDetails(tmp);
336                                muscle->attachToJoint(getLastJoint());
337                                if (usemapping) muscle->addMapping(*makeRange(t));
338                        }
339                        t++;
340                        break;
341        }
342        while (*t && *t <= ' ') t++;
343        bool finished = 0;
344        const char *begin = t;
345        const char* colon = 0;
346        SString classparams;
347        while (!finished)
348        {
349                switch (*t)
350                {
351                case ':': colon = t; break;
352                case 0: case ']': case ')': finished = 1;
353                        // NO break!
354                case ',':
355                        if (!haveclass && !colon && t > begin)
356                        {
357                                haveclass = 1;
358                                SString clsname(begin, t - begin);
359                                clsname = trim(clsname);
360                                last_f1_neuro->setClassName(clsname);
361                                NeuroClass *cls = last_f1_neuro->getClass();
362                                if (cls)
363                                {
364                                        if (cls->getPreferredLocation() == 2)
365                                                last_f1_neuro->attachToJoint(getLastJoint());
366                                        else if (cls->getPreferredLocation() == 1)
367                                                last_f1_neuro->attachToPart(getLastPart());
368
369                                        lastNeuroClassParam();
370                                        //special handling: muscle properties (can be overwritten by subsequent property assignments)
371                                        if (!strcmp(cls->getName().c_str(), "|"))
372                                        {
373                                                neuro_cls_param->setDoubleById("p", lastjoint_muscle_power);
374                                                neuro_cls_param->setDoubleById("r", props.muscle_bend_range);
375                                        }
376                                        else if (!strcmp(cls->getName().c_str(), "@"))
377                                        {
378                                                neuro_cls_param->setDoubleById("p", lastjoint_muscle_power);
379                                        }
380                                }
381                        }
382                        else if (colon && (colon > begin) && (t > colon))
383                                growConnection(begin, colon, t, props);
384                        if (t[0] != ',') t--;
385                        begin = t + 1; colon = 0;
386                        break;
387                }
388                t++;
389        }
390        SAFEDELETE(neuro_cls_param);
391        return t;
392}
393void Builder::growConnection(const char* begin, const char* colon, const char* end, GeneProps& props)
394{
395        while (*begin && *begin <= ' ') begin++;
396        int i;
397        if (isdigit(begin[0]) || (begin[0] == '-'))
398        {
399                double conn_weight = ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str());
400                paInt relative = ExtValue::getInt(trim(SString(begin, colon - begin)).c_str(), false);
401                int this_refno = neuro_f1_to_f0.size() - 1;
402                addOrRememberInput(this_refno, this_refno + relative, conn_weight);
403        }
404        else if ((i = last_f1_neuro->extraProperties().findIdn(begin, colon - begin)) >= 0)
405        {
406                last_f1_neuro->extraProperties().setFromString(i, colon + 1);
407        }
408        else if (isupper(begin[0]) || strchr("*|@", begin[0]))
409        {
410                SString clsname(begin, colon - begin);
411                trim(clsname);
412                Neuro *receptor = model.addNewNeuro();
413                receptor->setClassName(clsname);
414                NeuroClass *cls = receptor->getClass();
415                if (cls)
416                {
417                        if (cls->getPreferredLocation() == 2) receptor->attachToJoint(getLastJoint());
418                        else if (cls->getPreferredLocation() == 1) receptor->attachToPart(getLastPart());
419                }
420                last_f1_neuro->addInput(receptor, ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
421                if (usemapping) receptor->addMapping(*makeRange(begin, end - 1));
422        }
423        else if ((begin[0] == '>') && (begin[1]))
424        {
425                Neuro *out = model.addNewNeuro();
426                out->addInput(last_f1_neuro, ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
427                out->setClassName(SString(begin + 1, end - colon - 1));
428                if (begin[1] == '@')
429                {
430                        sprintf(tmp, "p=%lg", lastjoint_muscle_power);
431                        out->setClassParams(tmp);
432                }
433                else if (begin[1] == '|')
434                {
435                        sprintf(tmp, "p=%lg,r=%lg", lastjoint_muscle_power, props.muscle_bend_range);
436                        out->setClassParams(tmp);
437                }
438                NeuroClass *cls = out->getClass();
439                if (cls)
440                {
441                        if (cls->getPreferredLocation() == 2) out->attachToJoint(getLastJoint());
442                        else if (cls->getPreferredLocation() == 1) out->attachToPart(getLastPart());
443                }
444                if (usemapping) out->addMapping(*makeRange(begin, end - 1));
445        }
446        else if (*begin == '!') addClassParam("fo", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
447        else if (*begin == '=') addClassParam("in", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
448        else if (*begin == '/') addClassParam("si", ExtValue::getDouble(trim(SString(colon + 1, end - (colon + 1))).c_str()));
449        else if (islower(begin[0]))
450        {
451                SString name(begin, colon - begin);
452                SString value(colon + 1, end - (colon + 1));
453                addClassParam(name.c_str(), value.c_str());
454        }
455}
Note: See TracBrowser for help on using the repository browser.