Index: cpp/frams/genetics/genooperators.h
===================================================================
--- cpp/frams/genetics/genooperators.h	(revision 1273)
+++ cpp/frams/genetics/genooperators.h	(revision 1313)
@@ -1,4 +1,4 @@
 // This file is a part of Framsticks SDK.  http://www.framsticks.com/
-// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
+// Copyright (C) 1999-2024  Maciej Komosinski and Szymon Ulatowski.
 // See LICENSE.txt for details.
 
@@ -144,5 +144,5 @@
 	virtual int mutate(char *&geno, float& chg, int &method) { method = -1; chg = -1; return GENOPER_NOOPER; }
 
-	/**Crosses over two genotypes. It is sufficient to return only one child (in \a g1) and set \a chg1 only, then \a g2 must equal "".
+	/**Crosses over two genotypes. It is sufficient to return only one child (in \a g1) and set \a chg1 only, then \a g2 must be "".
 
 	Avoid unnecessary calls in your code. Every genotype argument passed to this
@@ -183,5 +183,6 @@
 	static const int NEUROCLASS_PROP_OFFSET = 100; //a NeuroClass property is identified by some functions below as a single-value integer index, yet a property is either "standard" or "extra" (two separate lists), hence this offset to tell one case from the other.
 
-	static int roulette(const double *probtab, const int count); 	///<returns random index according to probabilities in the \a probtab table or -1 if all probs are zero. \a count is the number of elements in \a probtab.
+	static int roulette(const double *probtab, const int count); ///<returns a random index according to probabilities in the \a probtab table or -1 if all probs are zero. \a count is the number of elements in \a probtab.
+	static int roulette(const vector<double> &probtab); ///<returns a random index according to probabilities in the \a probtab table or -1 if all probs are zero.
 	static bool getMinMaxDef(ParamInterface *p, int propindex, double &mn, double &mx, double &def); ///<perhaps a more useful (higher-level) way to obtain min/max/def info for integer and double properties. Returns true if min/max/def was really available (otherwise it is just invented).
 	static bool mutateRandomNeuroClassProperty(Neuro* n); ///<high-level neuron mutation function, will select and mutate a random property of Neuron's NeuroClass. Returns true if successful and some property was actually mutated. Could return false when the NeuroClass of the Neuron have no properties, or when a randomly selected property was not suitable for mutation (for example a string or another non-number type).
@@ -215,11 +216,13 @@
 	static void skipWS(char *&s); ///<advances pointer \a s skipping whitespaces.
 	static bool areAlike(char*, char*); ///<compares two text strings skipping whitespaces. Returns 1 when equal, 0 when different.
-	static char* strchrn0(const char *str, char ch); ///<like strchr, but does not find ascii=0 char in \a str.
-
-	static int getRandomChar(const char *choices, const char *excluded); ///<returns index of a random character from \a choices excluding \a excluded, or -1 when everything is excluded or \a choices is empty.
+	static char* strchr_no0(const char *str, char ch); ///<like strchr, but does not find ascii=0 char in \a str.
+
+	static double probOfModifier(const char* mod_def); //returns a probability of a modifier: either 1.0 (default) or parsed value if the probability is given in the appended parentheses (...). For example, "G(0.3)" will return 0.3, and "G" will return 1.0.
+	static char getRandomModifier(const char *choices); ///<returns a random character from \a choices (note that the special syntax with probabilities in parentheses is supported), or 0 when \a choices is empty or probabilities were insufficient for a random chance to choose some character.
+	static char getRandomColorModifier(const char *choices, const char *color_modifiers); //finds all color_modifiers in choices and returns a color modifier drawn randomly proportionally to the optional probabilities defined in choices. Returns 0 when \a choices does not have any color modifier with a positive probability.
 	static string simplifiedModifiers_rR(const string& str); ///<finds all 'r' and 'R' in \a str and returns the shortest sequence of 'r' and 'R that is equivalent to all these found in \a str.
 	static string simplifiedModifiersFixedOrder(const char *str_of_char_pairs, vector<int> &char_counts); ///<returns a sequence of chars from \a str_of_char_pairs based on how many times each char occurred in \a char_counts. Assume that an even-index char and the following odd-index char have the opposite influence, so they cancel out. We don't use this function, because a fixed order imposed by this function means that the number of different parameter values produced by a sequence of modifiers is lowered (N same-letter upper- and lower-case chars yield only 2*N different values). Due to how modifiers work, the effect of aaA, aAa, Aaa etc. is different (N same-letter upper- and lower-case chars yield 2^N different values), so simplifying modifiers should not impose any order, should not interfere with their original order, and should not cancel out antagonistic modifiers - see \a simplifiedModifiers() and geneprops_test.cpp.
 	//@}
-	static string simplifiedModifiers(const string &original); ///<from the \a original sequence removes modifiers that are too numerous (exceeding a defined threshold number), starting the removal from the least-significant, leftmost (="oldest" when interpreting the sequence from left to right) ones. Contrary to \a simplifiedModifiersFixedOrder(), this kind of simplification preserves 2^N different sequences for each upper/lower-case modifier and thus 2^N different values of a given property (see geneprops.cpp), but the values resulting from these sequences constitute a landscape not as easy for optimization as in the case of 2*N, where the effect of each mutation could be independent and additive (no epistasis). So for a given sequence length, the 2^N case allows for a higher resolution at the cost of a more rugged fitness landscape than the 2*N case.
+	static string simplifiedModifiers(const string &original, const char* colorgenes); ///<from the \a original sequence removes modifiers that are too numerous (exceeding a defined threshold number), starting the removal from the least-significant, leftmost (="oldest" when interpreting the sequence from left to right) ones. Contrary to \a simplifiedModifiersFixedOrder(), this kind of simplification preserves 2^N different sequences for each upper/lower-case modifier and thus 2^N different values of a given property (see geneprops.cpp), but the values resulting from these sequences constitute a landscape not as easy for optimization as in the case of 2*N, where the effect of each mutation could be independent and additive (no epistasis). So for a given sequence length, the 2^N case allows for a higher resolution at the cost of a more rugged fitness landscape than the 2*N case.
 };
 
