Index: framspy/evolalg/examples/standard.py
===================================================================
--- framspy/evolalg/examples/invalid.py	(revision 1128)
+++ framspy/evolalg/examples/standard.py	(revision 1131)
@@ -1,8 +1,13 @@
-import random
-
 import argparse
 import os
 import sys
 import numpy as np
+
+#TODO add new example: steadystate.py (analogous to standard.py) OR include steadysteate as a mode in this example or in niching_novelty.py
+#TODO extend both standard.py and steadystate.py to support >1 criteria (using DEAP's selNSGA2() and selSPEA2())
+#TODO add comments to all examples in this directory
+#TODO add to standard.py and steadystate.py evaluating each genotype in HOF N (configurable, default 10) times when the evolution ends instead of evaluating the last population as it is now in niching_novelty.py
+#TODO protect all examples against invalid genotypes (fill population until all genotypes are conrrectly evaluated). And maybe remove invalid.py if it overlaps with (is a subset of) other examples
+#TODO "debug" mode, displaying Step-based class names and their arguments so it is easy to see what happens during evolution
 
 from evolalg.base.lambda_step import LambdaStep
@@ -22,4 +27,8 @@
 
 
+
+EVAL_LIFESPAN_BEHAVIOR = False  # if False, standard evaluation criteria can be used as fitness as defined by the -opt parameter. If True, it is assumed that the expdef provides custom dictionary fields in evaluation and they need to be handled specifically in python source code below (this could be parametrized in command-line too, but the syntax would be complex)
+
+
 def ensureDir(string):
     if os.path.isdir(string):
@@ -33,12 +42,14 @@
         description='Run this program with "python -u %s" if you want to disable buffering of its output.' % sys.argv[
             0])
-    parser.add_argument('-path', type=ensureDir, required=True, help='Path to Framsticks without trailing slash.')
-    # parser.add_argument('-opt', required=True,
-    #                     help='optimization criteria : vertpos, velocity, distance, vertvel, lifespan, numjoints, numparts, numneurons, numconnections. Single or multiple criteria.')
-    parser.add_argument('-lib', required=False, help="Filename of .so or .dll with framsticks library")
+    parser.add_argument('-path', type=ensureDir, required=True, help='Path to the Framsticks library without trailing slash.')
+    parser.add_argument('-opt', required=True,
+                        help='optimization criteria : vertpos, velocity, distance, vertvel, lifespan, numjoints, numparts, numneurons, numconnections (or other as long as it is provided by the .sim file and its .expdef). Single or multiple criteria.')
+    parser.add_argument('-lib', required=False, help="Filename of .so or .dll with the Framsticks library")
     parser.add_argument('-genformat', required=False, default="1",
                         help='Genetic format for the demo run, for example 4, 9, or B. If not given, f1 is assumed.')
-
-    parser.add_argument("-popsize", type=int, default=50, help="Size of population, default 50.")
+    parser.add_argument('-sim', required=False, default="eval-allcriteria.sim", help="Name of the .sim file with all parameter values")
+    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.")
     return parser.parse_args()
 
@@ -49,15 +60,11 @@
 
 def print_population_count(pop):
-    print("Current:",len(pop))
-    return pop # Each step must return a population
-
-
-def random_remove(pop):
-    return [_ for _ in pop if random.randint(0, 1) == 0]
+    print("Current:", len(pop))
+    return pop  # Each step must return a population
 
 
 def main():
     parsed_args = parseArguments()
-    frams_lib = FramsticksLib(parsed_args.path, parsed_args.lib, "eval-movement.sim")
+    frams_lib = FramsticksLib(parsed_args.path, parsed_args.lib, parsed_args.sim)
 
     hall_of_fame = HallOfFameStatistics(100, "fitness")
@@ -73,12 +80,18 @@
     ])
 
-    fitness_remove = UnionStep([
+    fitness_remove = UnionStep(
+        [
         FitnessStep(frams_lib, fields={"velocity": "fitness", "data->recording": "recording"},
-                    fields_defaults={"velocity": None, "data->recording": None}),
-        FieldRemove("recording", None),
-        print_population_count,  # Stages can be also any Callable
-    ])
+                    fields_defaults={"velocity": None, "data->recording": None})  # custom definitions and handling
+        if EVAL_LIFESPAN_BEHAVIOR else
+        FitnessStep(frams_lib, fields={parsed_args.opt: "fitness", }, fields_defaults={})
+        ]
+        +
+        ([FieldRemove("recording", None)] if EVAL_LIFESPAN_BEHAVIOR else [])
+        +
+        [print_population_count]  # Stages can also be any Callable
+    )
 
-    selection = TournamentSelection(5, copy=True)
+    selection = TournamentSelection(parsed_args.tournament, copy=True, fit_attr="fitness")
     new_generation_steps = [
         FramsCrossAndMutate(frams_lib, cross_prob=0.2, mutate_prob=0.9),
@@ -94,7 +107,9 @@
                    statistics_union]
 
-    end_steps = [PopulationSave("halloffame.gen", provider=hall_of_fame.halloffame, fields={"genotype": "genotype",
-                                                                                           "fitness": "fitness",
-                                                                                           "custom": "recording"})]
+    end_steps = [PopulationSave("halloffame.gen", provider=hall_of_fame.halloffame,
+                                fields={"genotype": "genotype", "fitness": "fitness", "custom": "recording"}
+                                if EVAL_LIFESPAN_BEHAVIOR
+                                else {"genotype": "genotype", "fitness": "fitness"}
+                                )]
 
     experiment = Experiment(init_population=init_stages,
@@ -106,5 +121,5 @@
                             )
     experiment.init()
-    experiment.run(3)
+    experiment.run(parsed_args.generations)
     for ind in hall_of_fame.halloffame:
         print("%g\t%s" % (ind.fitness, ind.genotype))
