Index: /cpp/frams/genetics/fB/fB_oper.cpp
===================================================================
--- /cpp/frams/genetics/fB/fB_oper.cpp	(revision 1272)
+++ /cpp/frams/genetics/fB/fB_oper.cpp	(revision 1273)
@@ -1,5 +1,10 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2021  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
+
+//TODO parsing quotes/neurons seems too relaxed, for example the genotype aag"S""acalaaaafbc is considered valid
+//TODO Same with numbers: "----1.23" is valid
+//TODO reconsider: Horizontal gene transfer - copying a single random gene from each parent to the beginning of the other parent: should the gene be copied (seems to cause bloat!) or rather moved?
+//Neurons ("N") can grow even without using quotes and providing neuron classname in the genotype, for example aaaaabbccvaaapdgfddaalaandwddbaajt (this works likely as designed, but investigate and reconsider); also valid neuron definitions inside the genotype are sometimes not expressed
 
 #include <frams/util/sstring.h>
@@ -19,12 +24,12 @@
 	{ "Genetics: fB: Mutation", },
 	{ "Genetics: fB: Crossover", },
-	{ "fB_mut_substitution", 1, 0, "Substitution", "f 0 1 0.6", FIELD(mutationprobs[FB_SUBSTITUTION]), "Probability of mutation by changing single random letter in genotype", },
-	{ "fB_mut_insertion", 1, 0, "Insertion", "f 0 1 0.095", FIELD(mutationprobs[FB_INSERTION]), "Probability of mutation by inserting characters in random place of genotype", },
-	{ "fB_mut_nclassins", 1, 0, "Insertion of neuron class definition", "f 0 1 0.005", FIELD(mutationprobs[FB_NCLASSINS]), "Probability of mutation by inserting neuron class definition in random place of genotype", },
-	{ "fB_mut_deletion", 1, 0, "Deletion", "f 0 1 0.1", FIELD(mutationprobs[FB_DELETION]), "Probability of mutation by deleting random characters in genotype", },
-	{ "fB_mut_duplication", 1, 0, "Duplication", "f 0 1 0.0", FIELD(mutationprobs[FB_DUPLICATION]), "Probability of mutation by copying single *gene* of genotype and appending it to the beginning of this genotype", },
-	{ "fB_mut_translocation", 1, 0, "Translocation", "f 0 1 0.15", FIELD(mutationprobs[FB_TRANSLOCATION]), "Probability of mutation by replacing two substrings in genotype", },
-	{ "fB_cross_gene_transfer", 2, 0, "Horizontal gene transfer", "f 0 1 0.0", FIELD(crossoverprobs[FB_GENE_TRANSFER]), "Probability of crossing over by transferring single genes from both parents to beginning of each other", },
-	{ "fB_cross_crossover", 2, 0, "Crossing over", "f 0 1 1.0", FIELD(crossoverprobs[FB_CROSSING_OVER]), "Probability of crossing over by random distribution of genes from both parents to both children", },
+	{ "fB_mut_substitute", 1, 0, "Substitution", "f 0 100 1", FIELD(mutationprobs[FB_SUBSTITUTION]), "Relative probability of changing a single random character (or a neuron) in the genotype", },
+	{ "fB_mut_insert", 1, 0, "Insertion", "f 0 100 3", FIELD(mutationprobs[FB_INSERTION]), "Relative probability of inserting a random character in a random place of the genotype", },
+	{ "fB_mut_insert_neuron", 1, 0, "Insertion of a neuron", "f 0 100 3", FIELD(mutationprobs[FB_INSERTION_NEURON]), "Relative probability of inserting a neuron in a random place of genotype", },
+	{ "fB_mut_delete", 1, 0, "Deletion", "f 0 100 4", FIELD(mutationprobs[FB_DELETION]), "Relative probability of deleting a random character (or a neuron) in the genotype", },
+	{ "fB_mut_duplicate", 1, 0, "Duplication", "f 0 100 0", FIELD(mutationprobs[FB_DUPLICATION]), "Relative probability of copying a single *gene* of the genotype and appending it to the beginning of this genotype", },
+	{ "fB_mut_translocate", 1, 0, "Translocation", "f 0 100 4", FIELD(mutationprobs[FB_TRANSLOCATION]), "Relative probability of swapping two substrings in the genotype", },
+	{ "fB_cross_gene_transfer", 2, 0, "Horizontal gene transfer", "f 0 100 0", FIELD(crossoverprobs[FB_GENE_TRANSFER]), "Relative probability of crossing over by copying a single random gene from each parent to the beginning of the other parent", },
+	{ "fB_cross_crossover", 2, 0, "Crossing over", "f 0 100 100", FIELD(crossoverprobs[FB_CROSSING_OVER]), "Relative probability of crossing over by a random distribution of genes from both parents to both children", },
 	{ 0, },
 };
@@ -128,5 +133,5 @@
 	if (!genotype.getNextToken(pos, strdims, '\n'))
 	{
-		return GENOPER_OPFAIL;
+		return GENOPER_OK;
 	}
 	// parse dimension
@@ -134,5 +139,5 @@
 	if (!ExtValue::parseInt(strdims.c_str(), dims, true, false))
 	{
-		return GENOPER_OPFAIL;
+		return GENOPER_OK;
 	}
 	SString line;
@@ -171,5 +176,5 @@
 			else
 			{
-				return GENOPER_OPFAIL;
+				return GENOPER_OK;
 			}
 		}
@@ -247,5 +252,5 @@
 	{
 		std::list<SString> tokenized = tokenizeSequence(line);
-		int rndid = rndUint(tokenized.size()); // select random letter from genotype
+		int rndid = rndUint((int)tokenized.size()); // select random letter from genotype
 		// increment/decrement character - when overflow happens, this method
 		// uses the "reflect" approach
@@ -282,9 +287,9 @@
 		break;
 	}
-	case FB_NCLASSINS:
+	case FB_INSERTION_NEURON:
 	{
 		std::list<SString> tokenized = tokenizeSequence(line);
 		std::list<SString>::iterator it = tokenized.begin();
-		int rndid = rndUint(tokenized.size()); // select random insertion point
+		int rndid = rndUint((int)tokenized.size()); // select random insertion point
 		std::advance(it, rndid);
 		NeuroClass *cls = getRandomNeuroClass(Model::SHAPETYPE_BALL_AND_STICK);
@@ -307,5 +312,5 @@
 		chg = 1.0 / line.length();
 		std::list<SString> tokenized = tokenizeSequence(line);
-		int rndid = rndUint(tokenized.size()); // select random insertion point
+		int rndid = rndUint((int)tokenized.size()); // select random insertion point
 		std::list<SString>::iterator it = tokenized.begin();
 		std::advance(it, rndid);
@@ -321,5 +326,5 @@
 		std::list<SString> tokenized = tokenizeSequence(line);
 		std::list<SString>::iterator it = tokenized.begin();
-		int rndid = rndUint(tokenized.size()); // select random deletion point
+		int rndid = rndUint((int)tokenized.size()); // select random deletion point
 		std::advance(it, rndid);
 		tokenized.erase(it);
@@ -343,5 +348,5 @@
 		for (int i = 0; i < 4; i++)
 		{
-			cuts[i] = rndUint(tokenized.size());
+			cuts[i] = rndUint((int)tokenized.size());
 		}
 		std::sort(cuts.begin(), cuts.end());
@@ -407,5 +412,5 @@
 	case FB_GENE_TRANSFER:
 	{
-		// get random gene from first parent
+		// get a random gene from the first parent
 		int choice = rndUint(fB_GenoHelpers::geneCount(parent1));
 		int start, end;
@@ -414,5 +419,5 @@
 		child2 = gene + parent2;
 		chg2 = (float)parent2.length() / (float)child2.length();
-		// do the same for second parent
+		// do the same for the second parent
 		choice = rndUint(fB_GenoHelpers::geneCount(parent2));
 		gene = fB_GenoHelpers::getGene(choice, parent2, start, end);
@@ -554,21 +559,41 @@
 		{
 			pos--;
-			if (isdigit(geno[pos]) == 0)
-			{
-				return GENSTYLE_CS(0, GENSTYLE_INVALID);
-			}
-		}
-		return GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD);
-	}
-	if (islower(ch) == 0)
-	{
-		return GENSTYLE_CS(0, GENSTYLE_INVALID);
-	}
-	uint32_t style = GENSTYLE_CS(GENCOLOR_TEXT, GENSTYLE_NONE);
-	if (ch == 'a' && pos > 0 && (geno[pos - 1] == 'a' || geno[pos - 1] == '\n'))
+			if (isdigit(geno[pos]) == 0) //going left we encountered some non-digit character
+			{
+				return GENSTYLE_CS(GENCOLOR_NUMBER, GENSTYLE_NONE); //so 'ch' is any digit in the genotype (neural property value etc.); for simplicity, digits as parts of neuroclass name or property name also get included here
+			}
+		}
+		return GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD); //only digits up to the beginning, so this is the dimensionality value
+	}
+	if (ch == '-' || ch == '.')
+		return GENSTYLE_CS(GENCOLOR_NUMBER, GENSTYLE_NONE);
+	if (ch == '"')
+		return GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); //quotes encompass neuron definitions. To further distinguish the text inside quotes from the text outside quotes, we would need to determine the number of '"' from the beginning, i.e. linear search through the entire genotype. We don't want to do it - it would mean the complexity of len(geno)^2 if performed for each symbol in the genotype independently, like this function does. Below we perform an approximate partial scan.
+	if (isupper(ch) || strchr("@|*", ch))
+		return GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); //neuroclass
+	if (strchr(":,=", ch))
+		return GENSTYLE_RGBS(150, 0, 150, GENSTYLE_NONE); //these symbols occur exclusively inside "...neuron...", so let's make the entire neuron section "...neuron..." more visually uniform by using the same violet color as the neuroclass name and quotes have
+	if (islower(ch)) //how to color the current lower-case letter?
+	{
+		static const int SCAN_RANGE = 8; //how many characters before the current one to scan to discover some context and find out if we are likely in the neuroclass name or the property name. Reduces computational complexity. Example genotype fragments: abcabc"T:r=0.9, ry=4.088, rz=1.213"abcabc or abc"N:in=0.0, fo=0.17, si=999.0"abc
+		int i = pos;
+		while (i > 0 && pos - i < SCAN_RANGE)
+		{
+			i--; //go back one char
+			if (isupper(geno[i]))
+				return GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); //neuroclass
+			if (geno[i] == ',' || geno[i] == ':') //this is what must occur before property name starts
+				return GENSTYLE_RGBS(255, 140, 0, GENSTYLE_BOLD); //property
+			if (!(isalpha(geno[i]) || isspace(geno[i]))) //going left we encountered any char that is not a letter or space
+				break;
+		}
+	}
+
+	uint32_t style = GENSTYLE_CS(GENCOLOR_TEXT, GENSTYLE_NONE); //if the current character did not fall into any of the above cases, assume default black style
+	if (ch == 'a' && (geno[pos + 1] == 'a' || (pos > 0 && geno[pos - 1] == 'a'))) //start codon, "aa"
 	{
 		style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_BOLD);
 	}
-	else if (ch == 'z' && pos > 0 && geno[pos - 1] == 'z')
+	else if (ch == 'z' && (geno[pos + 1] == 'z' || (pos > 0 && geno[pos - 1] == 'z'))) //stop codon, "zz"
 	{
 		style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_BOLD);
Index: /cpp/frams/genetics/fB/fB_oper.h
===================================================================
--- /cpp/frams/genetics/fB/fB_oper.h	(revision 1272)
+++ /cpp/frams/genetics/fB/fB_oper.h	(revision 1273)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -11,17 +11,17 @@
 /** @name Codes for general fB mutation types */
 //@{
-#define FB_SUBSTITUTION  0 ///<Relative probability of mutation by changing single random letter in genotype (substitution)
-#define FB_INSERTION     1 ///<Relative probability of mutation by inserting characters in random place of genotype
-#define FB_NCLASSINS     2 ///<Relative probability of mutation by inserting neuron class definition in random place of genotype
-#define FB_DELETION      3 ///<Relative probability of mutation by deleting random characters in genotype
-#define FB_DUPLICATION   4 ///<Relative probability of mutation by copying single *gene* of genotype and appending it to the beginning of this genotype
-#define FB_TRANSLOCATION 5 ///<Relative probability of mutation by replacing two substrings in genotype
-#define FB_MUT_COUNT     6 ///<Count of mutation types
+#define FB_SUBSTITUTION     0 ///<Relative probability of changing a single random character (or a neuron) in the genotype
+#define FB_INSERTION        1 ///<Relative probability of inserting a random character in a random place of the genotype
+#define FB_INSERTION_NEURON 2 ///<Relative probability of inserting a neuron in a random place of genotype
+#define FB_DELETION         3 ///<Relative probability of deleting a random character (or a neuron) in the genotype
+#define FB_DUPLICATION      4 ///<Relative probability of copying a single *gene* of the genotype and appending it to the beginning of this genotype
+#define FB_TRANSLOCATION    5 ///<Relative probability of swapping two substrings in the genotype
+#define FB_MUT_COUNT        6 ///<Count of mutation types
 //@}
 
 /** @name Codes for fB cross over types */
 //@{
-#define FB_GENE_TRANSFER 0 ///<Relative probability of crossing over by transferring single genes from both parents to beginning of each other
-#define FB_CROSSING_OVER 1 ///<Relative probability of crossing over by random distribution of genes from both parents to both children
+#define FB_GENE_TRANSFER 0 ///<Relative probability of crossing over by copying a single random gene from each parent to the beginning of the other parent
+#define FB_CROSSING_OVER 1 ///<Relative probability of crossing over by a random distribution of genes from both parents to both children
 #define FB_XOVER_COUNT   2 ///<Count of crossing over types
 //@}
@@ -48,5 +48,5 @@
 	int crossOver(char *&g1, char *&g2, float& chg1, float& chg2);
 
-	virtual const char* getSimplest() { return "5\naaazz"; }
+	virtual const char* getSimplest() { return "3\naaazz"; }
 
 	uint32_t style(const char *geno, int pos);
Index: /cpp/frams/genetics/fH/fH_oper.cpp
===================================================================
--- /cpp/frams/genetics/fH/fH_oper.cpp	(revision 1272)
+++ /cpp/frams/genetics/fH/fH_oper.cpp	(revision 1273)
@@ -14,11 +14,11 @@
 {
 	{ "Genetics: fH", 1, FH_OPCOUNT + FH_ADD_OPCOUNT, },
-	{ "fH_mut_addition", 0, 0, "Add element", "f 0 100 30", FIELD(operations[FH_ADD]), "Probability of adding new element to genotype", },
-	{ "fH_mut_add_joint", 0, 0, " - add joint", "f 0 100 33", FIELD(addoperations[FH_ADD_STICK]), "Probability of adding new stick handle", },
-	{ "fH_mut_add_neuron", 0, 0, " - add neuron", "f 0 100 33", FIELD(addoperations[FH_ADD_NEURO]), "Probability of adding new neuron handle", },
-	{ "fH_mut_add_connection", 0, 0, " - add neural connection", "f 0 100 33", FIELD(addoperations[FH_ADD_CONN]), "Probability of adding new neuron connection handle", },
-	{ "fH_mut_deletion", 0, 0, "Delete element", "f 0 100 10", FIELD(operations[FH_DEL]), "Probability of removing element from genotype", },
-	{ "fH_mut_handle", 0, 0, "Modify vectors of handles", "f 0 100 30", FIELD(operations[FH_HANDLE]), "Probability of changing values in vectors of handle", },
-	{ "fH_mut_property", 0, 0, "Modify properties of handles", "f 0 100 30", FIELD(operations[FH_PROP]), "Probability of changing properties of handles", },
+	{ "fH_mut_addition", 0, 0, "Add element", "f 0 100 4", FIELD(operations[FH_ADD]), "Probability of adding a new element", },
+	{ "fH_mut_add_joint", 0, 0, " - add joint", "f 0 100 4", FIELD(addoperations[FH_ADD_STICK]), "Probability of adding a new stick handle", },
+	{ "fH_mut_add_neuron", 0, 0, " - add neuron", "f 0 100 3", FIELD(addoperations[FH_ADD_NEURO]), "Probability of adding a new neuron handle", },
+	{ "fH_mut_add_connection", 0, 0, " - add neural connection", "f 0 100 1", FIELD(addoperations[FH_ADD_CONN]), "Probability of adding a new neuron connection handle", },
+	{ "fH_mut_deletion", 0, 0, "Delete element", "f 0 100 4", FIELD(operations[FH_DEL]), "Probability of removing an element", },
+	{ "fH_mut_handle", 0, 0, "Modify vectors of handles", "f 0 100 1", FIELD(operations[FH_HANDLE]), "Probability of changing values in vectors of a handle", },
+	{ "fH_mut_property", 0, 0, "Modify properties of handles", "f 0 100 4", FIELD(operations[FH_PROP]), "Probability of changing properties of handles", },
 	{ 0, },
 };
@@ -53,5 +53,5 @@
 		return 1;
 	}
-	return 0;
+	return GENOPER_OK;
 }
 
@@ -63,12 +63,12 @@
 	fH_Builder builder;
 	int err = builder.parseGenotype(geno);
-	// if parsing failed, then it is impossible to repair genotype
+	// if parsing failed, then it is impossible to repair the genotype
 	if (err != 0)
 	{
-		return GENOPER_OPFAIL;
-	}
-	// method removes definitions of neurons that have invalid genotype
+		return GENOPER_OK;
+	}
+	// method removes definitions of neurons that have an invalid genotype
 	int amount = builder.removeNeuronsWithInvalidClasses();
-	// if there were any warnings, then rewrite genotype
+	// if there were any warnings, then rewrite the genotype
 	if (eh.getErrorWarningCount() > 0 || amount > 0)
 	{
Index: /cpp/frams/genetics/fL/fL_general.cpp
===================================================================
--- /cpp/frams/genetics/fL/fL_general.cpp	(revision 1272)
+++ /cpp/frams/genetics/fL/fL_general.cpp	(revision 1273)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -9,9 +9,9 @@
 #include <iterator>
 
-const char *fL_part_names[FL_PART_PROPS_COUNT] = { "dn", "fr", "ing", "as" };
-const char *fL_part_fullnames[FL_PART_PROPS_COUNT] = { "details", "friction", "ingestion", "assimilation" };
-
-const char *fL_joint_names[FL_JOINT_PROPS_COUNT] = { "stif", "rotstif", "stam" };
-const char *fL_joint_fullnames[FL_JOINT_PROPS_COUNT] = { "stiffness", "rotation stiffness", "stamina" };
+const char *fL_part_names[FL_PART_PROPS_COUNT] = { "fr" }; // "dn", "ing", "as" };
+const char *fL_part_fullnames[FL_PART_PROPS_COUNT] = { "friction" }; // "density", "ingestion", "assimilation" };
+
+const char *fL_joint_names[FL_JOINT_PROPS_COUNT] = { "rotstif" }; //"stif", , "stam" }; //see the comment to fH_joint_names in fH_general.cpp
+const char *fL_joint_fullnames[FL_JOINT_PROPS_COUNT] = { "rotation stiffness" }; //"stiffness", "stamina" };
 
 #define FIELDSTRUCT fL_Word
@@ -502,10 +502,9 @@
 	fL_Word *stick = new fL_Word(true);
 	stick->name = "S";
-	stick->npar = 8;
+	stick->npar = FL_PART_PROPS_COUNT + FL_JOINT_PROPS_COUNT + 1; //MacKo 2023-07: was hardcoded "8" but crashed when the number of props in Part and Joint was decreased; I think it should be like it is now (4+3+1 - or another value depending on prop counts)
 	for (int i = 0; i < FL_PART_PROPS_COUNT; i++)
 	{
 		stick->mut.addProperty(NULL, fL_part_names[i], "s", fL_part_fullnames[i], fL_part_fullnames[i], PARAM_CANOMITNAME, 0, -1);
 	}
-
 	for (int i = 0; i < FL_JOINT_PROPS_COUNT; i++)
 	{
@@ -1096,6 +1095,5 @@
 					{
 						delete newpart;
-						logMessage("fL_Builder", "developModel", LOG_ERROR,
-							"Error parsing word parameter");
+						logMessage("fL_Builder", "developModel", LOG_ERROR, "Error parsing word parameter");
 						return 1;
 					}
@@ -1123,6 +1121,5 @@
 						if (word->parevals[FL_PART_PROPS_COUNT + i]->evaluateRPN(jointprop) != 0)
 						{
-							logMessage("fL_Builder", "developModel", LOG_ERROR,
-								"Error parsing word parameter");
+							logMessage("fL_Builder", "developModel", LOG_ERROR, "Error parsing word parameter");
 							delete newjoint;
 							return 1;
@@ -1153,4 +1150,34 @@
 				}
 				model->addNeuro(neu);
+
+
+				// MacKo 2023-07: embody (i.e., attach to body) sensors and effectors.
+				// Ad hoc modification; should be reconsidered as it was added without reminding and understanding of the entire logic of the phenotype development and assumptions, and may not be "the best performing" or "the most reasonable" approach. Without this embodiment, genotypes with sensors and effectors not attached to body had warnings (such neurons "could not be initialized") so such genotypes were not accepted when creatwarnfail==1; even with creatwarnfail==0, non-embodied sensor and effector neurons were useless.
+				//printf("Neuron: -------------- %s\n", details.c_str());
+				switch (neu->getClass()->getPreferredLocation())
+				{
+				case NeuroClass::PrefLocation::PREFER_PART: //attach to currstate.currpart
+					if (currstate.currpart != NULL)
+						neu->attachToPart(currstate.currpart);
+					break;
+				case NeuroClass::PrefLocation::PREFER_JOINT: //attach to the last joint incident with currstate.currpart
+					if (currstate.currpart != NULL)
+					{
+						Joint *j = NULL;
+						for (int i = 0; i < model->getJointCount(); i++)
+						{
+							Joint *jj = model->getJoint(i);
+							if (jj->part1 == currstate.currpart || jj->part2 == currstate.currpart)
+								j = jj;
+						}
+						if (j != NULL)
+							neu->attachToJoint(j);
+					}
+					break;
+				default:
+					break;
+				}
+
+
 				if (using_mapping) neu->addMapping(IRange(word->begin, word->end));
 				if (neu->getClass()->getPreferredInputs() != 0)
@@ -1251,5 +1278,5 @@
 			{
 				connsbuffer[i].second->addInput(connsbuffer[i].second, weight);
-				if (using_mapping) neu->addMapping(
+				if (using_mapping) connsbuffer[i].second->addMapping( //MacKo 2023-07: changed 'neu' (which is NULL here so crashed) to connsbuffer[i].second (no guarantee this is a proper fix, only inspired by the analogy to the difference in both branches of "if" in the line above)
 					IRange((*connsbuffer[i].first)->begin,
 						(*connsbuffer[i].first)->end));
Index: /cpp/frams/genetics/fL/fL_general.h
===================================================================
--- /cpp/frams/genetics/fL/fL_general.h	(revision 1272)
+++ /cpp/frams/genetics/fL/fL_general.h	(revision 1273)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2018  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -25,11 +25,11 @@
 /** @name Constants used in fL methods */
 //@{
-#define FL_PART_PROPS_COUNT   4 ///<Count of part properties
-#define FL_JOINT_PROPS_COUNT  3 ///<Count of joint properties
+#define FL_PART_PROPS_COUNT   1 ///<Count of part properties
+#define FL_JOINT_PROPS_COUNT  1 ///<Count of joint properties
 #define FL_PE_NEURO_DET       "d" ///<Id of details type definition in f0_neuro_paramtab
 #define FL_PE_CONN_WEIGHT     "w" ///<Id of weight type definition in f0_neuroconn_paramtab
 #define FL_PE_CONN_ATTR       "attr" ///<Id of attractor of neural connection
 #define FL_DEFAULT_LENGTH     1.0 ///<Default length of a stick in fL encoding
-#define FL_MINIMAL_LENGTH     0.0 ///<Minimal length of a stick in fL encoding
+#define FL_MINIMAL_LENGTH     0.1 ///<Minimal length of a stick in fL encoding
 #define FL_MAXIMAL_LENGTH     2.0 ///<Maximal length of a stick in fL encoding
 #define FL_MAXITER           "100.0" ///<Maximal iteration available in fL
@@ -404,8 +404,8 @@
 
 	/**
-	 * Developes L-System from given genotype and builds Framsticks Model from it.
-	 * When using_checkpoints is enabled, method generates checkpoint for each
-	 * step defined in timestamp.
-	 * @param neededtime reference to a time value after stopping development (usually it will be equal to time specified in the time field, unless the number of allowed words will be exceeded earlier)
+	 * Develops an L-System from a given genotype and builds a Framsticks Model from it.
+	 * When using_checkpoints is enabled, this method generates a checkpoint for each
+	 * step defined in the timestamp.
+	 * @param neededtime reference to a time value after stopping development (usually it will be equal to the time specified in the time field, unless the number of allowed words will be exceeded earlier)
 	 * @return final model from a fL genotype
 	 */
@@ -413,7 +413,7 @@
 
 	/**
-	 * Creates new checkpoint for a given model based on current state of genotype.
-	 * @param model reference to model
-	 * @return 0 if developing went successfully, 1 otherwise
+	 * Creates a new checkpoint for a given model based on the current state of the genotype.
+	 * @param model reference to the model
+	 * @return 0 if the development was successfull, 1 otherwise
 	 */
 	int buildModelFromSequence(Model *model);
Index: /cpp/frams/genetics/fL/fL_oper.cpp
===================================================================
--- /cpp/frams/genetics/fL/fL_oper.cpp	(revision 1272)
+++ /cpp/frams/genetics/fL/fL_oper.cpp	(revision 1273)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -8,4 +8,11 @@
 #include <algorithm>
 
+
+
+//TODO fix: occasionally happens (note that fL extensively uses Param parsing, see fL_word_paramtab): Param.loadSingleLine: Unknown property 'Word.w0(n1' (ignored)
+//TODO reconsider and maybe improve sensor and effector embodiment; see "MacKo 2023-07: embody" in fL_general.cpp.
+
+
+
 #define FIELDSTRUCT Geno_fL
 static ParamEntry geno_fL_paramtab[] =
@@ -14,29 +21,29 @@
 	{"Genetics: fL: Probabilities of mutating axiom and rules", },
 	{"Genetics: fL: Probabilities of mutation types", },
-	{"fL_maxdefinedwords", 0, 0, "Maximum number of defined words", "d 0 100 10", FIELD(maxdefinedwords), "Maximum number of words that can be defined in L-System", },
-
-	{"fL_axm_mut_prob", 1, 0, "Axiom mutation", "f 0 1 0.2", FIELD(groupprobabilities[FL_AXM_WORD_MUT_PROB]), "Probability of performing mutation operations on axiom", },
-	{"fL_rul_mut_prob", 1, 0, "Rule's successor mutation", "f 0 1 0.8", FIELD(groupprobabilities[FL_RUL_WORD_MUT_PROB]), "Probability of performing mutation operations on the successor of random rule", },
-
-	{"fL_mut_addition", 2, 0, "Addition of word to sequence", "f 0 1 0.2", FIELD(operations[FL_ADD_WORD]), "Probability of adding random existing word to the axiom or one of successors", },
-
-	{"fL_mut_add_stick", 2, 0, " - addition of stick", "f 0 1 0.2", FIELD(addtypes[FL_ADD_STICK]), "Probability of adding stick", },
-	{"fL_mut_add_neuro", 2, 0, " - addition of neuron", "f 0 1 0.2", FIELD(addtypes[FL_ADD_NEURO]), "Probability of adding neuron", },
-	{"fL_mut_add_conn", 2, 0, " - addition of neuron connection", "f 0 1 0.2", FIELD(addtypes[FL_ADD_CONN]), "Probability of adding connection", },
-	{"fL_mut_add_rot", 2, 0, " - addition of rotation words", "f 0 1 0.2", FIELD(addtypes[FL_ADD_ROT]), "Probability of adding one of rotation words", },
-	{"fL_mut_add_branch", 2, 0, " - addition of branched stick", "f 0 1 0.2", FIELD(addtypes[FL_ADD_BRANCH]), "Probability of adding branch with rotation and stick", },
-	{"fL_mut_add_other", 2, 0, " - addition of defined words", "f 0 1 0.4", FIELD(addtypes[FL_ADD_OTHER]), "Probability of adding other word, defined in genotype", },
-
-	{"fL_mut_worddefaddition", 2, 0, "Addition of new word definition", "f 0 1 0.05", FIELD(operations[FL_ADD_WDEF]), "Probability of adding new word definition to the genotype", },
-	{"fL_mut_ruleaddition", 2, 0, "Addition of new rule definition", "f 0 1 0.1", FIELD(operations[FL_ADD_RULE]), "Probability of adding new rule definition for existing word", },
-	{"fL_mut_rulecond", 2, 0, "Modification of rule condition", "f 0 1 0.1", FIELD(operations[FL_CHG_COND]), "Probability of modifying random rule condition", },
-
-	{"fL_mut_changeword", 2, 0, "Change of random word", "f 0 1 0.3", FIELD(operations[FL_CHG_WORD]), "Probability of changing word name or formula of a random word from axiom or one of successors", },
-	{"fL_mut_changeword_formula", 2, 0, " - change of formula", "f 0 1 0.7", FIELD(chgoperations[FL_CHG_WORD_FORMULA]), "Probability of changing formula in word", },
-	{"fL_mut_changeword_name", 2, 0, " - change of name", "f 0 1 0.3", FIELD(chgoperations[FL_CHG_WORD_NAME]), "Probability of changing name in word", },
-
-	{"fL_mut_changeiter", 2, 0, "Change of L-System iteration", "f 0 1 0.3", FIELD(operations[FL_CHG_ITER]), "Probability of changing number of iterations of L-Systems", },
-	{"fL_mut_changeiter_step", 2, 0, "Step of iteration changing", "f 0 1 1.0", FIELD(iterchangestep), "Minimal step that should be used for changing iterations in L-Systems", },
-	{"fL_mut_deletion", 2, 0, "Deletion of random word", "f 0 1 0.2", FIELD(operations[FL_DEL_WORD]), "Probability of deleting random word from axiom or random successor (also deletes rule if there is only one word in successor)", },
+	{"fL_maxdefinedwords", 0, 0, "Maximum number of defined words", "d 0 100 10", FIELD(maxdefinedwords), "Maximum number of words that can be defined in the L-System", },
+
+	{"fL_axm_mut_prob", 1, 0, "Axiom mutation", "f 0 100 4", FIELD(groupprobabilities[FL_AXM_WORD_MUT_PROB]), "Probability of performing mutation operations on axiom", },
+	{"fL_rul_mut_prob", 1, 0, "Rule's successor mutation", "f 0 100 1", FIELD(groupprobabilities[FL_RUL_WORD_MUT_PROB]), "Probability of performing mutation operations on the successor of a random rule", },
+
+	{"fL_mut_addition", 2, 0, "Addition of a word to a sequence", "f 0 100 4", FIELD(operations[FL_ADD_WORD]), "Probability of adding a random existing word to the axiom or to one of successors", },
+
+	{"fL_mut_add_stick", 2, 0, " - addition of a stick", "f 0 100 1", FIELD(addtypes[FL_ADD_STICK]), "Probability of adding a stick", },
+	{"fL_mut_add_neuro", 2, 0, " - addition of a neuron", "f 0 100 4", FIELD(addtypes[FL_ADD_NEURO]), "Probability of adding a neuron", },
+	{"fL_mut_add_conn", 2, 0, " - addition of a neuron connection", "f 0 100 4", FIELD(addtypes[FL_ADD_CONN]), "Probability of adding a neuron connection", },
+	{"fL_mut_add_rot", 2, 0, " - addition of rotation words", "f 0 100 2", FIELD(addtypes[FL_ADD_ROT]), "Probability of adding one of rotation words", },
+	{"fL_mut_add_branch", 2, 0, " - addition of a branched stick", "f 0 100 4", FIELD(addtypes[FL_ADD_BRANCH]), "Probability of adding a branch with a rotation and a stick", },
+	{"fL_mut_add_other", 2, 0, " - addition of defined words", "f 0 100 1", FIELD(addtypes[FL_ADD_OTHER]), "Probability of adding another word defined in the genotype", },
+
+	{"fL_mut_worddefaddition", 2, 0, "Addition of a new word definition", "f 0 100 1", FIELD(operations[FL_ADD_WDEF]), "Probability of adding a new word definition to the genotype", },
+	{"fL_mut_ruleaddition", 2, 0, "Addition of a new rule definition", "f 0 100 1", FIELD(operations[FL_ADD_RULE]), "Probability of adding a new rule definition for an existing word", },
+	{"fL_mut_rulecond", 2, 0, "Modification of a rule condition", "f 0 100 1", FIELD(operations[FL_CHG_COND]), "Probability of modifying a random rule condition", },
+
+	{"fL_mut_changeword", 2, 0, "Change a random word", "f 0 100 4", FIELD(operations[FL_CHG_WORD]), "Probability of changing a word name or a formula of a random word from an axiom or one of successors", },
+	{"fL_mut_changeword_formula", 2, 0, " - change of a formula", "f 0 100 4", FIELD(chgoperations[FL_CHG_WORD_FORMULA]), "Probability of changing a formula in a word", },
+	{"fL_mut_changeword_name", 2, 0, " - change of a name", "f 0 100 2", FIELD(chgoperations[FL_CHG_WORD_NAME]), "Probability of changing a name in a word", },
+
+	{"fL_mut_changeiter", 2, 0, "Change the number of iterations", "f 0 100 1", FIELD(operations[FL_CHG_ITER]), "Probability of changing the number of iterations of the L-System", },
+	{"fL_mut_changeiter_step", 2, 0, "Step of the iteration change", "f 0 1 1.0", FIELD(iterchangestep), "The minimal step that should be used for changing iterations in the L-System", },
+	{"fL_mut_deletion", 2, 0, "Deletion of a random word", "f 0 100 4", FIELD(operations[FL_DEL_WORD]), "Probability of deleting a random word from an axiom or a random successor (also deletes the rule if there is only one word in the successor)", },
 	{ 0, },
 };
@@ -66,19 +73,18 @@
 	if (builder.countSticksInSequence(&builder.genotype) == 0)
 	{
-		return GENOPER_OPFAIL;
+		return 1;
 	}
 	double neededtime = 0;
 	Model *m = builder.developModel(neededtime);
-	if (!m)
-	{
-		return GENOPER_OPFAIL;
+	if (m == NULL)
+	{
+		return 1;
 	}
 	if (!m->isValid())
 	{
 		delete m;
-		return GENOPER_OPFAIL;
+		return 1;
 	}
 	delete m;
-
 
 	return GENOPER_OK;
@@ -93,12 +99,16 @@
 	if (err != 0)
 	{
-		return err;
+		return GENOPER_OK;
 	}
 	double neededtime = 0;
 	Model *m = builder.developModel(neededtime);
+	if (m == NULL)
+	{
+		return GENOPER_OK;
+	}
 	if (!m->isValid())
 	{
 		delete m;
-		return GENOPER_OPFAIL;
+		return GENOPER_OK;
 	}
 	if (neededtime != builder.time)
@@ -171,5 +181,5 @@
 	else
 	{
-		int rid = rndUint(creature->rules.size());
+		int rid = rndUint((unsigned int)creature->rules.size());
 		list = &creature->rules[rid]->objsucc;
 		numparams = creature->rules[rid]->objpred->npar;
@@ -303,5 +313,5 @@
 		if (creature->rules.size() > 0)
 		{
-			int ruleid = rndUint(creature->rules.size());
+			int ruleid = rndUint((unsigned int)creature->rules.size());
 			if (!creature->rules[ruleid]->condeval)
 			{
@@ -340,5 +350,5 @@
 		if (wordswithnorules.size() > 0)
 		{
-			int predid = rndUint(wordswithnorules.size());
+			int predid = rndUint((unsigned int)wordswithnorules.size());
 			fL_Rule *newrule = new fL_Rule(0, 0);
 			fL_Word *pred = new fL_Word();
@@ -352,5 +362,5 @@
 		else if (creature->rules.size() > 0)
 		{
-			int ruleid = rndUint(creature->rules.size());
+			int ruleid = rndUint((unsigned int)creature->rules.size());
 			fL_Rule *newrule = new fL_Rule(0, 0);
 			fL_Word *pred = new fL_Word();
@@ -455,5 +465,5 @@
 		else
 		{
-			int rndid = rndUint(list->size());
+			int rndid = rndUint((unsigned int)list->size());
 			std::list<fL_Word *>::iterator it = list->begin();
 			std::advance(it, rndid);
@@ -482,5 +492,5 @@
 		int tmp = 0;
 		std::list<fL_Word *> *list = selectRandomSequence(creature, numpars, tmp);
-		int rndid = rndUint(list->size());
+		int rndid = rndUint((unsigned int)list->size());
 		std::list<fL_Word *>::iterator it = list->begin();
 		std::advance(it, rndid);
@@ -531,5 +541,5 @@
 		int tmp = 0;
 		std::list<fL_Word *> *list = selectRandomSequence(creature, numpars, tmp);
-		int rndid = rndUint(list->size());
+		int rndid = rndUint((unsigned int)list->size());
 		std::list<fL_Word *>::iterator selectedword = list->begin();
 		std::advance(selectedword, rndid);
@@ -545,5 +555,5 @@
 			int numpars = 0;
 			std::list<fL_Word *> *list = selectRandomSequence(creature, numpars, tmp);
-			int rndid = rndUint(list->size());
+			int rndid = rndUint((unsigned int)list->size());
 			std::list<fL_Word *>::iterator it = list->begin();
 			std::advance(it, rndid);
@@ -577,5 +587,5 @@
 				if (available.size() > 0)
 				{
-					int newnameid = rndUint(available.size());
+					int newnameid = rndUint((unsigned int)available.size());
 					(*selectedword)->name = available[newnameid]->name;
 				}
@@ -752,5 +762,5 @@
 		for (int i = 0; i < numselrules; i++)
 		{
-			int rulid = rndUint(from->rules.size());
+			int rulid = rndUint((unsigned int)from->rules.size());
 			fL_Rule *rul = from->rules[rulid];
 			fL_Rule *newrule = new fL_Rule(0, 0);
@@ -894,5 +904,5 @@
 	else if (strchr("<>$[]&\\ @|*", ch) != NULL) // other allowed symbols and special neuron symbols
 	{
-		style = GENSTYLE_CS(GENCOLOR_TEXT, ch=='[' || ch==']' ? GENSTYLE_BOLD : GENSTYLE_NONE);
+		style = GENSTYLE_CS(GENCOLOR_TEXT, ch == '[' || ch == ']' ? GENSTYLE_BOLD : GENSTYLE_NONE);
 	}
 	return style;
Index: /cpp/frams/genetics/genman.cpp
===================================================================
--- /cpp/frams/genetics/genman.cpp	(revision 1272)
+++ /cpp/frams/genetics/genman.cpp	(revision 1273)
@@ -12,5 +12,5 @@
 
 
-#define GENMAN_REPEAT_FAILED 100 //how many times GenMan tries to repeat a mutation or crossover when the operator does not return acceptable genotype
+#define GENMAN_REPEAT_FAILED 100 //how many times GenMan tries to repeat a mutation or crossover when the operator does not return an acceptable genotype
 #define STRINGIFY_1(x) #x
 #define STRINGIFY(x) STRINGIFY_1(x) //this second-level macro allows the parameter to be a macro itself and to stringify its value, not its name
@@ -575,5 +575,6 @@
 		if (g[i] == '<') latex += "$<$"; else if (g[i] == '>') latex += "$>$"; else
 			if (g[i] == '-') latex += "$-$"; else if (g[i] == '|') latex += "$|$"; else
-				if (g[i] == '$') latex += "\\$"; else if (g[i] == '%') latex += "\\%"; else latex += g[i];
+				if (g[i] == '$') latex += "\\$"; else if (g[i] == '%') latex += "\\%"; else
+					if (g[i] == '#') latex += "\\#"; else latex += g[i];
 		if ((i % 3) == 0 && g[i] == ' ') latex += "\n"; //for readability, insert some newlines into latex...
 		if (i % 10 == 0) latex += "{\\hskip 0pt}"; // https://tex.stackexchange.com/questions/33526/automatic-line-breaking-of-long-lines-of-text
Index: /cpp/frams/genetics/genooperators.h
===================================================================
--- /cpp/frams/genetics/genooperators.h	(revision 1272)
+++ /cpp/frams/genetics/genooperators.h	(revision 1273)
@@ -164,9 +164,9 @@
 	virtual const char* getSimplest() { return NULL; }
 
-	/**You may want to have your genotype colored. This method provides desired character styles for genes.
+	/**Provides color styles for individual characters of the genotype. For efficiency,
+	this function may be approximate and do not perform the full, proper analysis of the syntax.
 	\param geno genotype
 	\param pos 0-based char offset
-	\retval number-encoded visual style (and validity) of the genotype char at \a geno[pos].
-	Assume white background.
+	\retval number-encoded visual style (and validity) of the genotype char at \a geno[pos]. Assume white background.
 	\sa GENSTYLE_* macros, like GENSTYLE_BOLD*/
 	virtual uint32_t style(const char *geno, int pos) { return GENSTYLE_RGBS(0, 0, 0, GENSTYLE_NONE); }
