Index: framspy/evolalg/examples/niching_novelty.py
===================================================================
--- framspy/evolalg/examples/niching_novelty.py	(revision 1137)
+++ framspy/evolalg/examples/niching_novelty.py	(revision 1138)
@@ -64,9 +64,14 @@
                         help='Dissimilarity measure, default = frams', choices=list(Dissim))
     parser.add_argument('-fit', required=False, default=Fitness.raw, type=Fitness,
-                        help=' Fitness criteria DEFAULT = raw', choices=list(Fitness))
+                        help=' Fitness criteria, default = raw', choices=list(Fitness))
     parser.add_argument('-popsize', type=int, default=50, help="Population size, default 50.")
     parser.add_argument('-generations', type=int, default=5, help="Number of generations, default 5.")
     parser.add_argument('-tournament', type=int, default=5, help="Tournament size, default 5.")
-    parser.add_argument('-num_parts', type=int, default=None, help="Maximum number of Parts. Default: no limit")
+
+    parser.add_argument('-max_numparts', type=int, default=None, help="Maximum number of Parts. Default: no limit")
+    parser.add_argument('-max_numjoints', type=int, default=None, help="Maximum number of Joints. Default: no limit")
+    parser.add_argument('-max_numneurons', type=int, default=None, help="Maximum number of Neurons. Default: no limit")
+    parser.add_argument('-max_numconnections', type=int, default=None, help="Maximum number of Neural connections. Default: no limit")
+
     parser.add_argument('-checkpoint_path', required=False, default=None, help="Path to the checkpoint file")
     parser.add_argument('-checkpoint_interval', required=False, type=int, default=100, help="Checkpoint interval")
@@ -83,11 +88,38 @@
 
 
-class NumPartsGreater(Remove):
-    def __init__(self, numparts):
-        super(NumPartsGreater, self).__init__()
-        self.numparts = numparts
+class NumPartsHigher(Remove):
+    def __init__(self, max_number):
+        super(NumPartsHigher, self).__init__()
+        self.max_number = max_number
 
     def remove(self, individual):
-        return individual.numparts > self.numparts
+        return individual.numparts > self.max_number
+
+
+class NumJointsHigher(Remove):
+    def __init__(self, max_number):
+        super(NumJointsHigher, self).__init__()
+        self.max_number = max_number
+
+    def remove(self, individual):
+        return individual.numjoints > self.max_number
+
+
+class NumNeuronsHigher(Remove):
+    def __init__(self, max_number):
+        super(NumNeuronsHigher, self).__init__()
+        self.max_number = max_number
+
+    def remove(self, individual):
+        return individual.numneurons > self.max_number
+
+
+class NumConnectionsHigher(Remove):
+    def __init__(self, max_number):
+        super(NumConnectionsHigher, self).__init__()
+        self.max_number = max_number
+
+    def remove(self, individual):
+        return individual.numconnections > self.max_number
 
 
@@ -110,9 +142,9 @@
 def create_experiment():
     parsed_args = parseArguments()
-    frams = FramsticksLib(parsed_args.path, parsed_args.lib,
+    frams_lib = FramsticksLib(parsed_args.path, parsed_args.lib,
                           parsed_args.sim)
     # Steps for generating first population
     init_stages = [
-        FramsPopulation(frams, parsed_args.genformat, parsed_args.popsize)
+        FramsPopulation(frams_lib, parsed_args.genformat, parsed_args.popsize)
     ]
 
@@ -123,5 +155,5 @@
     # Procedure for generating new population. This steps will be run as long there is less than
     # popsize individuals in the new population
-    new_generation_stages = [FramsCrossAndMutate(frams, cross_prob=0.2, mutate_prob=0.9)]
+    new_generation_stages = [FramsCrossAndMutate(frams_lib, cross_prob=0.2, mutate_prob=0.9)]
 
     # Steps after new population is created. Executed exacly once per generation.
@@ -131,9 +163,15 @@
     # Fitness
 
-    fitness_raw = FitnessStep(frams, fields={parsed_args.opt: "fitness_raw", "numparts": "numparts"},
-                              fields_defaults={parsed_args.opt: None, "numparts": float("inf")},
+    fitness_raw = FitnessStep(frams_lib, fields={parsed_args.opt: "fitness_raw",
+                                             "numparts": "numparts",
+                                             "numjoints": "numjoints",
+                                             "numneurons": "numneurons",
+                                             "numconnections": "numconnections"},
+                              fields_defaults={parsed_args.opt: None, "numparts": float("inf"),
+                                               "numjoints": float("inf"), "numneurons": float("inf"),
+                                               "numconnections": float("inf")},
                               evaluation_count=1)
 
-    fitness_end = FitnessStep(frams, fields={parsed_args.opt: "fitness_raw"},
+    fitness_end = FitnessStep(frams_lib, fields={parsed_args.opt: "fitness_raw"},
                               fields_defaults={parsed_args.opt: None},
                               evaluation_count=100)  # evaluate the contents of the last population 100 times (TODO replace this approach and evaluate HOF instead of the last population)
@@ -141,8 +179,15 @@
     remove = []
     remove.append(FieldRemove("fitness_raw", None))  # Remove individuals if they have default value for fitness
-    if parsed_args.num_parts is not None:
-        # This could be also implemented by "LambdaRemove(lambda x: x.numparts > parsed_args.num_parts)
-        # But this will not serialize in checkpoint.
-        remove.append(NumPartsGreater(parsed_args.num_parts))
+    if parsed_args.max_numparts is not None:
+        # This could be also implemented by "LambdaRemove(lambda x: x.numparts > parsed_args.num_parts)"
+        # But this would not serialize in checkpoint.
+        remove.append(NumPartsHigher(parsed_args.max_numparts))
+    if parsed_args.max_numjoints is not None:
+        remove.append(NumJointsHigher(parsed_args.max_numjoints))
+    if parsed_args.max_numneurons is not None:
+        remove.append(NumNeuronsHigher(parsed_args.max_numneurons))
+    if parsed_args.max_numconnections is not None:
+        remove.append(NumConnectionsHigher(parsed_args.max_numconnections))
+
     remove_step = UnionStep(remove)
 
@@ -158,5 +203,5 @@
         dissim = LevenshteinDissimilarity(reduction="mean", output_field="dissim")
     elif parsed_args.dissim == Dissim.frams:
-        dissim = FramsDissimilarity(frams, reduction="mean", output_field="dissim")
+        dissim = FramsDissimilarity(frams_lib, reduction="mean", output_field="dissim")
 
     if parsed_args.fit == Fitness.raw:
Index: framspy/run-evolalg-examples.cmd
===================================================================
--- framspy/run-evolalg-examples.cmd	(revision 1137)
+++ framspy/run-evolalg-examples.cmd	(revision 1138)
@@ -8,3 +8,3 @@
 python -m evolalg.examples.standard          -path %DIR_WITH_FRAMS_LIBRARY%   -opt numneurons
 
-python -m evolalg.examples.niching_novelty   -path %DIR_WITH_FRAMS_LIBRARY%   -opt velocity
+python -m evolalg.examples.niching_novelty   -path %DIR_WITH_FRAMS_LIBRARY%   -opt velocity     -max_numparts 6
