[759] | 1 | // This file is a part of Framsticks SDK. http://www.framsticks.com/ |
---|
[1335] | 2 | // Copyright (C) 1999-2025 Maciej Komosinski and Szymon Ulatowski. |
---|
[759] | 3 | // See LICENSE.txt for details. |
---|
| 4 | |
---|
| 5 | #include "geneprops.h" |
---|
[1130] | 6 | #include <algorithm> |
---|
[759] | 7 | |
---|
| 8 | |
---|
[1335] | 9 | const GeneProps& GeneProps::get_standard_values() //this getter function ensures controlled initialization of the static standard_values object |
---|
| 10 | { |
---|
| 11 | static GeneProps standard_values; |
---|
| 12 | return standard_values; |
---|
| 13 | } |
---|
| 14 | |
---|
[759] | 15 | GeneProps::GeneProps() |
---|
| 16 | { |
---|
[1248] | 17 | Part_MinMaxDef default_part = Model::getDefPart(); |
---|
| 18 | Joint default_joint = Model::getDefJoint(); |
---|
| 19 | |
---|
[759] | 20 | length = 1.0; |
---|
| 21 | weight = 1.0; |
---|
[1248] | 22 | friction = default_part.friction; |
---|
[759] | 23 | curvedness = 0.0; |
---|
| 24 | twist = 0.0; |
---|
| 25 | energy = 1.0; |
---|
| 26 | |
---|
[1248] | 27 | // before 2023-05, the four fields below were aggregated and normalized using normalizeBiol4(), but this normalization only worked for f1 and f4 - other genetic encodings do not perform such normalization. If at all, this should be handled by the expdef depending on the goal of the experiment (e.g., to encourage specialization). |
---|
| 28 | muscle_power = 0.25; // "biological" property, same as findNeuroClass("BendMuscle or RotMuscle")->paraminterface->getDoubleById("power") |
---|
| 29 | assimilation = default_part.assim; // "biological" property |
---|
| 30 | stamina = default_joint.stamina; // "biological" property |
---|
| 31 | ingestion = default_part.ingest; // "biological" property |
---|
[759] | 32 | |
---|
[1248] | 33 | muscle_bend_range = 1.0; // same as findNeuroClass("BendMuscle")->paraminterface->getDoubleById("range") |
---|
[759] | 34 | muscle_reset_range = true; |
---|
| 35 | |
---|
[1248] | 36 | cred = default_part.vcolor.x; |
---|
| 37 | cgreen = default_part.vcolor.y; |
---|
| 38 | cblue = default_part.vcolor.z; |
---|
[759] | 39 | } |
---|
| 40 | |
---|
| 41 | void GeneProps::normalizeBiol4() |
---|
| 42 | { |
---|
| 43 | // make them sum to 1 |
---|
| 44 | double sum = muscle_power + assimilation + stamina + ingestion; |
---|
| 45 | if (sum == 0) |
---|
| 46 | { |
---|
| 47 | muscle_power = assimilation = stamina = ingestion = 0.25; |
---|
| 48 | } |
---|
| 49 | else |
---|
| 50 | { |
---|
| 51 | muscle_power /= sum; |
---|
| 52 | assimilation /= sum; |
---|
| 53 | stamina /= sum; |
---|
| 54 | ingestion /= sum; |
---|
| 55 | } |
---|
| 56 | } |
---|
| 57 | |
---|
[1242] | 58 | int GeneProps::executeModifier(char modif, GenePropsOps* ops) |
---|
| 59 | { |
---|
| 60 | if (ops == NULL) |
---|
[1260] | 61 | ops = getDefaultOps(); |
---|
[1242] | 62 | |
---|
| 63 | #define APPLY(name) ops->name->apply(name, modif) |
---|
| 64 | #define APPLY_AND_MAYBE_NORMALIZEBIOL4(name) {APPLY(name); if (ops->use_normalizebiol4) normalizeBiol4();} |
---|
| 65 | |
---|
| 66 | switch (toupper(modif)) |
---|
| 67 | { |
---|
| 68 | case 'L': APPLY(length); |
---|
| 69 | length = std::min(length, Model::getMaxJoint().d.x); break; |
---|
| 70 | break; |
---|
| 71 | |
---|
| 72 | case 'l': APPLY(length); |
---|
| 73 | length = std::max(length, Model::getMinJoint().d.x); break; |
---|
| 74 | break; |
---|
| 75 | |
---|
| 76 | case 'W': APPLY(weight); break; |
---|
| 77 | case 'F': APPLY(friction); break; |
---|
| 78 | case 'C': APPLY(curvedness); break; |
---|
| 79 | case 'Q': APPLY(twist); break; |
---|
| 80 | case 'E': APPLY(energy); break; |
---|
| 81 | |
---|
| 82 | case 'A': APPLY_AND_MAYBE_NORMALIZEBIOL4(assimilation); break; |
---|
| 83 | case 'I': APPLY_AND_MAYBE_NORMALIZEBIOL4(ingestion); break; |
---|
| 84 | case 'S': APPLY_AND_MAYBE_NORMALIZEBIOL4(stamina); break; |
---|
| 85 | case 'M': APPLY_AND_MAYBE_NORMALIZEBIOL4(muscle_power); break; |
---|
| 86 | |
---|
| 87 | case 'D': APPLY(cred); break; |
---|
| 88 | case 'G': APPLY(cgreen); break; |
---|
| 89 | case 'B': APPLY(cblue); break; |
---|
| 90 | |
---|
| 91 | default: return -1; |
---|
| 92 | } |
---|
| 93 | return 0; |
---|
| 94 | |
---|
| 95 | #undef APPLY |
---|
| 96 | #undef APPLY_AND_MAYBE_NORMALIZEBIOL4 |
---|
| 97 | } |
---|
| 98 | |
---|
[1246] | 99 | void GeneProps::propagateAlong(bool use_f1_muscle_reset_range, GenePropsOps* ops) |
---|
[759] | 100 | { |
---|
[1335] | 101 | const GeneProps& standard_values = get_standard_values(); |
---|
[1242] | 102 | length = 0.5 * length + 0.5 * standard_values.length; |
---|
[759] | 103 | weight += (standard_values.weight - weight) * 0.5; |
---|
| 104 | friction = 0.8 * friction + 0.2 * standard_values.friction; |
---|
| 105 | curvedness = 0.66 * curvedness; |
---|
| 106 | twist = 0.66 * twist; |
---|
| 107 | |
---|
| 108 | assimilation = 0.8 * assimilation + 0.2 * standard_values.assimilation; |
---|
| 109 | ingestion = 0.8 * ingestion + 0.2 * standard_values.ingestion; |
---|
| 110 | stamina = 0.8 * stamina + 0.2 * standard_values.stamina; |
---|
| 111 | muscle_power = 0.8 * muscle_power + 0.2 * standard_values.muscle_power; |
---|
| 112 | |
---|
[1246] | 113 | if (ops == NULL) |
---|
[1260] | 114 | ops = getDefaultOps(); |
---|
[1246] | 115 | if (ops->use_normalizebiol4) normalizeBiol4(); |
---|
[759] | 116 | |
---|
| 117 | if (use_f1_muscle_reset_range) |
---|
| 118 | { |
---|
| 119 | if (muscle_reset_range) muscle_bend_range = 1.0; else muscle_reset_range = true; |
---|
| 120 | } |
---|
| 121 | } |
---|
[1242] | 122 | |
---|
| 123 | //////////////////////////////////////////////////// |
---|
| 124 | |
---|
| 125 | void GenePropsOp::apply(double &value, char modif) const |
---|
| 126 | { |
---|
| 127 | if (isupper(modif)) |
---|
| 128 | value = increase(value); |
---|
| 129 | else |
---|
| 130 | value = decrease(value); |
---|
| 131 | } |
---|
| 132 | |
---|
[1260] | 133 | double GenePropsOp_Legacy::increase(double value) const |
---|
[1242] | 134 | { |
---|
| 135 | return value + (maxvalue - value) * change; |
---|
| 136 | } |
---|
| 137 | |
---|
[1260] | 138 | double GenePropsOp_Legacy::decrease(double value) const |
---|
[1242] | 139 | { |
---|
| 140 | return value + (minvalue - value) * revchange; |
---|
| 141 | } |
---|
| 142 | |
---|
[1260] | 143 | GenePropsOp_Legacy::GenePropsOp_Legacy(double minvalue, double maxvalue, double defvalue, double change, double revchange) |
---|
[1242] | 144 | { |
---|
| 145 | this->minvalue = minvalue; |
---|
| 146 | this->maxvalue = maxvalue; |
---|
| 147 | this->defvalue = defvalue; |
---|
| 148 | this->change = change; |
---|
| 149 | this->revchange = (revchange < 0) ? change : revchange; |
---|
| 150 | } |
---|
| 151 | |
---|
| 152 | GenePropsOp_Exponential::GenePropsOp_Exponential(double minvalue, double maxvalue, double defvalue, double change) |
---|
| 153 | :GenePropsOp_NormalizedAndScaled(change) |
---|
| 154 | { |
---|
| 155 | double mid = (maxvalue + minvalue) / 2; |
---|
| 156 | if (fabs(mid - defvalue) < 0.01) |
---|
| 157 | { |
---|
| 158 | linear = true; |
---|
| 159 | a = (maxvalue - minvalue) / 2; |
---|
| 160 | b = defvalue; |
---|
| 161 | } |
---|
| 162 | else |
---|
| 163 | { |
---|
| 164 | linear = false; |
---|
| 165 | a = -(maxvalue - defvalue) / (minvalue - defvalue); |
---|
| 166 | b = (minvalue * minvalue - 2 * defvalue * minvalue + defvalue * defvalue) / (minvalue + maxvalue - 2 * defvalue); |
---|
| 167 | c = (maxvalue * minvalue - defvalue * defvalue) / (minvalue + maxvalue - 2 * defvalue); |
---|
| 168 | log_a = log(a); |
---|
| 169 | } |
---|
| 170 | } |
---|
| 171 | |
---|
| 172 | double GenePropsOp_Exponential::scale(double value) const |
---|
| 173 | { |
---|
| 174 | if (linear) |
---|
| 175 | return a * value + b; |
---|
| 176 | else |
---|
| 177 | return pow(a, (value + 1)) * b + c; |
---|
| 178 | } |
---|
| 179 | |
---|
| 180 | double GenePropsOp_Exponential::scaleInv(double value) const |
---|
| 181 | { |
---|
| 182 | if (linear) |
---|
| 183 | return (value - b) / a; |
---|
| 184 | else |
---|
| 185 | return -(log_a - log(value / b - c / b)) / log_a; |
---|
| 186 | } |
---|
| 187 | |
---|
| 188 | //////////////////////////////////////////////////////////////////// |
---|
| 189 | |
---|
| 190 | |
---|
| 191 | GenePropsOps::~GenePropsOps() |
---|
| 192 | { |
---|
| 193 | delete length; |
---|
| 194 | delete curvedness; |
---|
| 195 | delete weight; |
---|
| 196 | delete friction; |
---|
| 197 | delete muscle_power; |
---|
| 198 | delete assimilation; |
---|
| 199 | delete stamina; |
---|
| 200 | delete ingestion; |
---|
| 201 | delete twist; |
---|
| 202 | delete energy; |
---|
| 203 | delete cred; |
---|
| 204 | delete cgreen; |
---|
| 205 | delete cblue; |
---|
| 206 | } |
---|
| 207 | |
---|
[1260] | 208 | GenePropsOps_Legacy::GenePropsOps_Legacy() |
---|
[1242] | 209 | { |
---|
[1335] | 210 | const GeneProps& standard_values = GeneProps::get_standard_values(); |
---|
| 211 | |
---|
[1242] | 212 | use_normalizebiol4 = true; |
---|
| 213 | |
---|
[1335] | 214 | length = new GenePropsOp_Legacy(0.33, 2.0, standard_values.length, 0.3); |
---|
| 215 | weight = new GenePropsOp_Legacy(0.5, 2.0, standard_values.weight, 0.3); |
---|
| 216 | friction = new GenePropsOp_Legacy(0, 4.0, standard_values.friction, 0.2); |
---|
| 217 | curvedness = new GenePropsOp_Legacy(-2, 2, standard_values.curvedness, 0.25); |
---|
| 218 | twist = new GenePropsOp_Legacy(-M_PI_2, M_PI_2, standard_values.twist, 0.3); |
---|
| 219 | energy = new GenePropsOp_Legacy(0, 10, standard_values.energy, 0.1); |
---|
[1242] | 220 | |
---|
[1335] | 221 | assimilation = new GenePropsOp_Legacy(0, 1, standard_values.assimilation, 0.8, 0.4); |
---|
| 222 | ingestion = new GenePropsOp_Legacy(0, 1, standard_values.ingestion, 0.8, 0.4); |
---|
| 223 | stamina = new GenePropsOp_Legacy(0, 1, standard_values.stamina, 0.8, 0.4); |
---|
| 224 | muscle_power = new GenePropsOp_Legacy(0, 1, standard_values.muscle_power, 0.8, 0.4); |
---|
[1242] | 225 | |
---|
[1335] | 226 | cred = new GenePropsOp_Legacy(0, 1, standard_values.cred, 0.25); |
---|
| 227 | cgreen = new GenePropsOp_Legacy(0, 1, standard_values.cgreen, 0.25); |
---|
| 228 | cblue = new GenePropsOp_Legacy(0, 1, standard_values.cblue, 0.25); |
---|
[1242] | 229 | } |
---|
| 230 | |
---|
[1260] | 231 | GenePropsOps_AllChange05::GenePropsOps_AllChange05() |
---|
[1242] | 232 | { |
---|
[1335] | 233 | const GeneProps& standard_values = GeneProps::get_standard_values(); |
---|
| 234 | |
---|
[1242] | 235 | use_normalizebiol4 = false; |
---|
[1260] | 236 | |
---|
| 237 | constexpr float CHANGE = 0.5; |
---|
[1242] | 238 | auto fields = { length,curvedness,weight,friction,muscle_power,assimilation,stamina,ingestion,twist,energy,cred,cgreen,cblue }; |
---|
[1246] | 239 | for (auto f : fields) |
---|
[1242] | 240 | { |
---|
[1260] | 241 | auto f_gpo = dynamic_cast<GenePropsOp_Legacy*>(f); |
---|
[1246] | 242 | if (f_gpo) |
---|
[1260] | 243 | f_gpo->change = f_gpo->revchange = CHANGE; |
---|
[1242] | 244 | } |
---|
[1260] | 245 | |
---|
| 246 | delete curvedness; |
---|
[1335] | 247 | curvedness = new GenePropsOp_Legacy(-M_PI_2, M_PI_2, standard_values.curvedness, CHANGE); |
---|
[1242] | 248 | } |
---|
| 249 | |
---|
| 250 | GenePropsOps_Exponential::GenePropsOps_Exponential() |
---|
| 251 | { |
---|
[1335] | 252 | const GeneProps& standard_values = GeneProps::get_standard_values(); |
---|
| 253 | length = new GenePropsOp_Exponential(0.33, 2.0, standard_values.length); |
---|
| 254 | weight = new GenePropsOp_Exponential(0.5, 2.0, standard_values.weight); |
---|
| 255 | friction = new GenePropsOp_Exponential(0, 4.0, standard_values.friction); |
---|
| 256 | curvedness = new GenePropsOp_Exponential(-2, 2, standard_values.curvedness); |
---|
| 257 | twist = new GenePropsOp_Exponential(-M_PI_2, M_PI_2, standard_values.twist); |
---|
| 258 | energy = new GenePropsOp_Exponential(0, 10, standard_values.energy); |
---|
[1242] | 259 | |
---|
[1335] | 260 | assimilation = new GenePropsOp_Exponential(0, 1, standard_values.assimilation); |
---|
| 261 | ingestion = new GenePropsOp_Exponential(0, 1, standard_values.ingestion); |
---|
| 262 | stamina = new GenePropsOp_Exponential(0, 1, standard_values.stamina); |
---|
| 263 | muscle_power = new GenePropsOp_Exponential(0, 1, standard_values.muscle_power); |
---|
[1242] | 264 | |
---|
[1335] | 265 | cred = new GenePropsOp_Exponential(0, 1, standard_values.cred); |
---|
| 266 | cgreen = new GenePropsOp_Exponential(0, 1, standard_values.cgreen); |
---|
| 267 | cblue = new GenePropsOp_Exponential(0, 1, standard_values.cblue); |
---|
[1242] | 268 | } |
---|
| 269 | |
---|
[1260] | 270 | GenePropsOps* GeneProps::getAllChange05Ops() |
---|
| 271 | { |
---|
| 272 | static GenePropsOps_AllChange05 ops; |
---|
| 273 | return &ops; |
---|
| 274 | } |
---|
[1242] | 275 | |
---|
[1260] | 276 | GenePropsOps* GeneProps::getLegacyOps() |
---|
[1242] | 277 | { |
---|
[1260] | 278 | static GenePropsOps_Legacy ops; |
---|
| 279 | return &ops; |
---|
[1242] | 280 | } |
---|
[1260] | 281 | |
---|
| 282 | static GenePropsOps* default_ops = NULL; |
---|
| 283 | |
---|
| 284 | GenePropsOps* GeneProps::getDefaultOps() |
---|
| 285 | { |
---|
| 286 | if (!default_ops) |
---|
| 287 | default_ops = getAllChange05Ops(); |
---|
| 288 | return default_ops; |
---|
| 289 | } |
---|
| 290 | |
---|
| 291 | void GeneProps::setDefaultOps(GenePropsOps* ops) |
---|
| 292 | { |
---|
| 293 | default_ops = ops; |
---|
| 294 | } |
---|