diff --git a/benchmarks/run_all_examples.py b/benchmarks/run_all_examples.py
index 57dd6a930a65e421e4395050d6b82b3383096286..509e232416db525c28ce3d69ac84ad1657c533eb 100644
--- a/benchmarks/run_all_examples.py
+++ b/benchmarks/run_all_examples.py
@@ -13,6 +13,7 @@ for entry in [entry for entry in importlib_resources.contents('examples') if
               and entry.endswith(".py")
               and '__init__' not in entry
               and 'demo.py' not in entry
+              and 'DELETE' not in entry
               ]:
     with path('examples', entry) as file_in:
         print("")
@@ -22,4 +23,6 @@ for entry in [entry for entry in importlib_resources.contents('examples') if
         print("Running {}".format(entry))
         print("*****************************************************************")
         with swap_attr(sys, "stdin", StringIO("q")):
-            runpy.run_path(file_in, run_name="__main__")
+            runpy.run_path(file_in, run_name="__main__", init_globals={
+                'argv': ['--sleep-for-animation=False']
+            })
diff --git a/examples/custom_observation_example.py b/examples/custom_observation_example.py
deleted file mode 100644
index 8b1de6aa4e303469d30983d30333fbfda89c1d1e..0000000000000000000000000000000000000000
--- a/examples/custom_observation_example.py
+++ /dev/null
@@ -1,225 +0,0 @@
-import random
-import time
-
-import numpy as np
-
-from flatland.core.env_observation_builder import ObservationBuilder
-from flatland.core.grid.grid_utils import coordinate_to_position
-from flatland.envs.observations import TreeObsForRailEnv
-from flatland.envs.predictions import ShortestPathPredictorForRailEnv
-from flatland.envs.rail_env import RailEnv
-from flatland.envs.rail_generators import random_rail_generator, complex_rail_generator
-from flatland.envs.schedule_generators import complex_schedule_generator
-from flatland.utils.rendertools import RenderTool
-
-random.seed(100)
-np.random.seed(100)
-
-
-class SimpleObs(ObservationBuilder):
-    """
-    Simplest observation builder. The object returns observation vectors with 5 identical components,
-    all equal to the ID of the respective agent.
-    """
-
-    def __init__(self):
-        self.observation_space = [5]
-
-    def reset(self):
-        return
-
-    def get(self, handle):
-        observation = handle * np.ones((5,))
-        return observation
-
-
-env = RailEnv(width=7,
-              height=7,
-              rail_generator=random_rail_generator(),
-              number_of_agents=3,
-              obs_builder_object=SimpleObs())
-
-# Print the observation vector for each agents
-obs, all_rewards, done, _ = env.step({0: 0})
-for i in range(env.get_num_agents()):
-    print("Agent ", i, "'s observation: ", obs[i])
-
-
-class SingleAgentNavigationObs(TreeObsForRailEnv):
-    """
-    We derive our bbservation builder from TreeObsForRailEnv, to exploit the existing implementation to compute
-    the minimum distances from each grid node to each agent's target.
-
-    We then build a representation vector with 3 binary components, indicating which of the 3 available directions
-    for each agent (Left, Forward, Right) lead to the shortest path to its target.
-    E.g., if taking the Left branch (if available) is the shortest route to the agent's target, the observation vector
-    will be [1, 0, 0].
-    """
-
-    def __init__(self):
-        super().__init__(max_depth=0)
-        self.observation_space = [3]
-
-    def reset(self):
-        # Recompute the distance map, if the environment has changed.
-        super().reset()
-
-    def get(self, handle):
-        agent = self.env.agents[handle]
-
-        possible_transitions = self.env.rail.get_transitions(*agent.position, agent.direction)
-        num_transitions = np.count_nonzero(possible_transitions)
-
-        # Start from the current orientation, and see which transitions are available;
-        # organize them as [left, forward, right], relative to the current orientation
-        # If only one transition is possible, the forward branch is aligned with it.
-        if num_transitions == 1:
-            observation = [0, 1, 0]
-        else:
-            min_distances = []
-            for direction in [(agent.direction + i) % 4 for i in range(-1, 2)]:
-                if possible_transitions[direction]:
-                    new_position = self._new_position(agent.position, direction)
-                    min_distances.append(self.distance_map[handle, new_position[0], new_position[1], direction])
-                else:
-                    min_distances.append(np.inf)
-
-            observation = [0, 0, 0]
-            observation[np.argmin(min_distances)] = 1
-
-        return observation
-
-
-env = RailEnv(width=7,
-              height=7,
-              rail_generator=complex_rail_generator(nr_start_goal=10, nr_extra=1, min_dist=5, max_dist=99999, seed=0),
-              schedule_generator=complex_schedule_generator(),
-              number_of_agents=1,
-              obs_builder_object=SingleAgentNavigationObs())
-
-obs = env.reset()
-env_renderer = RenderTool(env, gl="PILSVG")
-env_renderer.render_env(show=True, frames=True, show_observations=True)
-for step in range(100):
-    action = np.argmax(obs[0]) + 1
-    obs, all_rewards, done, _ = env.step({0: action})
-    print("Rewards: ", all_rewards, "  [done=", done, "]")
-    env_renderer.render_env(show=True, frames=True, show_observations=True)
-    time.sleep(0.1)
-    if done["__all__"]:
-        break
-env_renderer.close_window()
-
-
-class ObservePredictions(TreeObsForRailEnv):
-    """
-    We use the provided ShortestPathPredictor to illustrate the usage of predictors in your custom observation.
-
-    We derive our observation builder from TreeObsForRailEnv, to exploit the existing implementation to compute
-    the minimum distances from each grid node to each agent's target.
-
-    This is necessary so that we can pass the distance map to the ShortestPathPredictor
-
-    Here we also want to highlight how you can visualize your observation
-    """
-
-    def __init__(self, predictor):
-        super().__init__(max_depth=0)
-        self.observation_space = [10]
-        self.predictor = predictor
-
-    def reset(self):
-        # Recompute the distance map, if the environment has changed.
-        super().reset()
-
-    def get_many(self, handles=None):
-        '''
-        Because we do not want to call the predictor seperately for every agent we implement the get_many function
-        Here we can call the predictor just ones for all the agents and use the predictions to generate our observations
-        :param handles:
-        :return:
-        '''
-
-        self.predictions = self.predictor.get(custom_args={'distance_map': self.distance_map})
-
-        self.predicted_pos = {}
-        for t in range(len(self.predictions[0])):
-            pos_list = []
-            for a in handles:
-                pos_list.append(self.predictions[a][t][1:3])
-            # We transform (x,y) coodrinates to a single integer number for simpler comparison
-            self.predicted_pos.update({t: coordinate_to_position(self.env.width, pos_list)})
-        observations = {}
-
-        # Collect all the different observation for all the agents
-        for h in handles:
-            observations[h] = self.get(h)
-        return observations
-
-    def get(self, handle):
-        '''
-        Lets write a simple observation which just indicates whether or not the own predicted path
-        overlaps with other predicted paths at any time. This is useless for the task of navigation but might
-        help when looking for conflicts. A more complex implementation can be found in the TreeObsForRailEnv class
-
-        Each agent recieves an observation of length 10, where each element represents a prediction step and its value
-        is:
-         - 0 if no overlap is happening
-         - 1 where n i the number of other paths crossing the predicted cell
-
-        :param handle: handeled as an index of an agent
-        :return: Observation of handle
-        '''
-
-        observation = np.zeros(10)
-
-        # We are going to track what cells where considered while building the obervation and make them accesible
-        # For rendering
-
-        visited = set()
-        for _idx in range(10):
-            # Check if any of the other prediction overlap with agents own predictions
-            x_coord = self.predictions[handle][_idx][1]
-            y_coord = self.predictions[handle][_idx][2]
-
-            # We add every observed cell to the observation rendering
-            visited.add((x_coord, y_coord))
-            if self.predicted_pos[_idx][handle] in np.delete(self.predicted_pos[_idx], handle, 0):
-                # We detect if another agent is predicting to pass through the same cell at the same predicted time
-                observation[handle] = 1
-
-        # This variable will be access by the renderer to visualize the observation
-        self.env.dev_obs_dict[handle] = visited
-
-        return observation
-
-
-# Initiate the Predictor
-CustomPredictor = ShortestPathPredictorForRailEnv(10)
-
-# Pass the Predictor to the observation builder
-CustomObsBuilder = ObservePredictions(CustomPredictor)
-
-# Initiate Environment
-env = RailEnv(width=10,
-              height=10,
-              rail_generator=complex_rail_generator(nr_start_goal=5, nr_extra=1, min_dist=8, max_dist=99999, seed=0),
-              schedule_generator=complex_schedule_generator(),
-              number_of_agents=3,
-              obs_builder_object=CustomObsBuilder)
-
-obs = env.reset()
-env_renderer = RenderTool(env, gl="PILSVG")
-
-# We render the initial step and show the obsered cells as colored boxes
-env_renderer.render_env(show=True, frames=True, show_observations=True, show_predictions=False)
-
-action_dict = {}
-for step in range(100):
-    for a in range(env.get_num_agents()):
-        action = np.random.randint(0, 5)
-        action_dict[a] = action
-    obs, all_rewards, done, _ = env.step(action_dict)
-    print("Rewards: ", all_rewards, "  [done=", done, "]")
-    env_renderer.render_env(show=True, frames=True, show_observations=True, show_predictions=False)
-    time.sleep(0.5)
diff --git a/examples/custom_observation_example_01_SimpleObs.py b/examples/custom_observation_example_01_SimpleObs.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a2515b789031bf7aec4eeb4a5e9fc495a045bf
--- /dev/null
+++ b/examples/custom_observation_example_01_SimpleObs.py
@@ -0,0 +1,44 @@
+import random
+
+import numpy as np
+
+from flatland.core.env_observation_builder import ObservationBuilder
+from flatland.envs.rail_env import RailEnv
+from flatland.envs.rail_generators import random_rail_generator
+
+random.seed(100)
+np.random.seed(100)
+
+
+class SimpleObs(ObservationBuilder):
+    """
+    Simplest observation builder. The object returns observation vectors with 5 identical components,
+    all equal to the ID of the respective agent.
+    """
+
+    def __init__(self):
+        self.observation_space = [5]
+
+    def reset(self):
+        return
+
+    def get(self, handle):
+        observation = handle * np.ones((5,))
+        return observation
+
+
+def main():
+    env = RailEnv(width=7,
+                  height=7,
+                  rail_generator=random_rail_generator(),
+                  number_of_agents=3,
+                  obs_builder_object=SimpleObs())
+
+    # Print the observation vector for each agents
+    obs, all_rewards, done, _ = env.step({0: 0})
+    for i in range(env.get_num_agents()):
+        print("Agent ", i, "'s observation: ", obs[i])
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/custom_observation_example_02_SingleAgentNavigationObs.py b/examples/custom_observation_example_02_SingleAgentNavigationObs.py
new file mode 100644
index 0000000000000000000000000000000000000000..b16a4e3e5a6378418b120f691248097fcdd82cb8
--- /dev/null
+++ b/examples/custom_observation_example_02_SingleAgentNavigationObs.py
@@ -0,0 +1,103 @@
+import getopt
+import random
+import sys
+import time
+
+import numpy as np
+
+from flatland.envs.observations import TreeObsForRailEnv
+from flatland.envs.rail_env import RailEnv
+from flatland.envs.rail_generators import complex_rail_generator
+from flatland.envs.schedule_generators import complex_schedule_generator
+from flatland.utils.rendertools import RenderTool
+
+random.seed(100)
+np.random.seed(100)
+
+
+class SingleAgentNavigationObs(TreeObsForRailEnv):
+    """
+    We derive our bbservation builder from TreeObsForRailEnv, to exploit the existing implementation to compute
+    the minimum distances from each grid node to each agent's target.
+
+    We then build a representation vector with 3 binary components, indicating which of the 3 available directions
+    for each agent (Left, Forward, Right) lead to the shortest path to its target.
+    E.g., if taking the Left branch (if available) is the shortest route to the agent's target, the observation vector
+    will be [1, 0, 0].
+    """
+
+    def __init__(self):
+        super().__init__(max_depth=0)
+        self.observation_space = [3]
+
+    def reset(self):
+        # Recompute the distance map, if the environment has changed.
+        super().reset()
+
+    def get(self, handle):
+        agent = self.env.agents[handle]
+
+        possible_transitions = self.env.rail.get_transitions(*agent.position, agent.direction)
+        num_transitions = np.count_nonzero(possible_transitions)
+
+        # Start from the current orientation, and see which transitions are available;
+        # organize them as [left, forward, right], relative to the current orientation
+        # If only one transition is possible, the forward branch is aligned with it.
+        if num_transitions == 1:
+            observation = [0, 1, 0]
+        else:
+            min_distances = []
+            for direction in [(agent.direction + i) % 4 for i in range(-1, 2)]:
+                if possible_transitions[direction]:
+                    new_position = self._new_position(agent.position, direction)
+                    min_distances.append(self.distance_map[handle, new_position[0], new_position[1], direction])
+                else:
+                    min_distances.append(np.inf)
+
+            observation = [0, 0, 0]
+            observation[np.argmin(min_distances)] = 1
+
+        return observation
+
+
+def main(args):
+    try:
+        opts, args = getopt.getopt(args, "", ["sleep-for-animation=", ""])
+    except getopt.GetoptError as err:
+        print(str(err))  # will print something like "option -a not recognized"
+        sys.exit(2)
+    sleep_for_animation = True
+    for o, a in opts:
+        if o in ("--sleep-for-animation"):
+            sleep_for_animation = bool(a)
+        else:
+            assert False, "unhandled option"
+
+    env = RailEnv(width=7,
+                  height=7,
+                  rail_generator=complex_rail_generator(nr_start_goal=10, nr_extra=1, min_dist=5, max_dist=99999,
+                                                        seed=0),
+                  schedule_generator=complex_schedule_generator(),
+                  number_of_agents=1,
+                  obs_builder_object=SingleAgentNavigationObs())
+
+    obs = env.reset()
+    env_renderer = RenderTool(env, gl="PILSVG")
+    env_renderer.render_env(show=True, frames=True, show_observations=True)
+    for step in range(100):
+        action = np.argmax(obs[0]) + 1
+        obs, all_rewards, done, _ = env.step({0: action})
+        print("Rewards: ", all_rewards, "  [done=", done, "]")
+        env_renderer.render_env(show=True, frames=True, show_observations=True)
+        if sleep_for_animation:
+            time.sleep(0.1)
+        if done["__all__"]:
+            break
+    env_renderer.close_window()
+
+
+if __name__ == '__main__':
+    if 'argv' in globals():
+        main(argv)
+    else:
+        main(sys.argv[1:])
diff --git a/examples/custom_observation_example_03_ObservePredictions.py b/examples/custom_observation_example_03_ObservePredictions.py
new file mode 100644
index 0000000000000000000000000000000000000000..00a3d6252ce6d93754c3bfd9c629ad78d45f348d
--- /dev/null
+++ b/examples/custom_observation_example_03_ObservePredictions.py
@@ -0,0 +1,153 @@
+import getopt
+import random
+import sys
+import time
+
+import numpy as np
+
+from flatland.core.grid.grid_utils import coordinate_to_position
+from flatland.envs.observations import TreeObsForRailEnv
+from flatland.envs.predictions import ShortestPathPredictorForRailEnv
+from flatland.envs.rail_env import RailEnv
+from flatland.envs.rail_generators import complex_rail_generator
+from flatland.envs.schedule_generators import complex_schedule_generator
+from flatland.utils.rendertools import RenderTool
+
+random.seed(100)
+np.random.seed(100)
+
+
+class ObservePredictions(TreeObsForRailEnv):
+    """
+    We use the provided ShortestPathPredictor to illustrate the usage of predictors in your custom observation.
+
+    We derive our observation builder from TreeObsForRailEnv, to exploit the existing implementation to compute
+    the minimum distances from each grid node to each agent's target.
+
+    This is necessary so that we can pass the distance map to the ShortestPathPredictor
+
+    Here we also want to highlight how you can visualize your observation
+    """
+
+    def __init__(self, predictor):
+        super().__init__(max_depth=0)
+        self.observation_space = [10]
+        self.predictor = predictor
+
+    def reset(self):
+        # Recompute the distance map, if the environment has changed.
+        super().reset()
+
+    def get_many(self, handles=None):
+        '''
+        Because we do not want to call the predictor seperately for every agent we implement the get_many function
+        Here we can call the predictor just ones for all the agents and use the predictions to generate our observations
+        :param handles:
+        :return:
+        '''
+
+        self.predictions = self.predictor.get(custom_args={'distance_map': self.distance_map})
+
+        self.predicted_pos = {}
+        for t in range(len(self.predictions[0])):
+            pos_list = []
+            for a in handles:
+                pos_list.append(self.predictions[a][t][1:3])
+            # We transform (x,y) coodrinates to a single integer number for simpler comparison
+            self.predicted_pos.update({t: coordinate_to_position(self.env.width, pos_list)})
+        observations = {}
+
+        # Collect all the different observation for all the agents
+        for h in handles:
+            observations[h] = self.get(h)
+        return observations
+
+    def get(self, handle):
+        '''
+        Lets write a simple observation which just indicates whether or not the own predicted path
+        overlaps with other predicted paths at any time. This is useless for the task of navigation but might
+        help when looking for conflicts. A more complex implementation can be found in the TreeObsForRailEnv class
+
+        Each agent recieves an observation of length 10, where each element represents a prediction step and its value
+        is:
+         - 0 if no overlap is happening
+         - 1 where n i the number of other paths crossing the predicted cell
+
+        :param handle: handeled as an index of an agent
+        :return: Observation of handle
+        '''
+
+        observation = np.zeros(10)
+
+        # We are going to track what cells where considered while building the obervation and make them accesible
+        # For rendering
+
+        visited = set()
+        for _idx in range(10):
+            # Check if any of the other prediction overlap with agents own predictions
+            x_coord = self.predictions[handle][_idx][1]
+            y_coord = self.predictions[handle][_idx][2]
+
+            # We add every observed cell to the observation rendering
+            visited.add((x_coord, y_coord))
+            if self.predicted_pos[_idx][handle] in np.delete(self.predicted_pos[_idx], handle, 0):
+                # We detect if another agent is predicting to pass through the same cell at the same predicted time
+                observation[handle] = 1
+
+        # This variable will be access by the renderer to visualize the observation
+        self.env.dev_obs_dict[handle] = visited
+
+        return observation
+
+
+def main(args):
+    try:
+        opts, args = getopt.getopt(args, "", ["sleep-for-animation=", ""])
+    except getopt.GetoptError as err:
+        print(str(err))  # will print something like "option -a not recognized"
+        sys.exit(2)
+    sleep_for_animation = True
+    for o, a in opts:
+        if o in ("--sleep-for-animation"):
+            sleep_for_animation = bool(a)
+        else:
+            assert False, "unhandled option"
+
+    # Initiate the Predictor
+    custom_predictor = ShortestPathPredictorForRailEnv(10)
+
+    # Pass the Predictor to the observation builder
+    custom_obs_builder = ObservePredictions(custom_predictor)
+
+    # Initiate Environment
+    env = RailEnv(width=10,
+                  height=10,
+                  rail_generator=complex_rail_generator(nr_start_goal=5, nr_extra=1, min_dist=8, max_dist=99999,
+                                                        seed=0),
+                  schedule_generator=complex_schedule_generator(),
+                  number_of_agents=3,
+                  obs_builder_object=custom_obs_builder)
+
+    obs = env.reset()
+    env_renderer = RenderTool(env, gl="PILSVG")
+
+    # We render the initial step and show the obsered cells as colored boxes
+    env_renderer.render_env(show=True, frames=True, show_observations=True, show_predictions=False)
+
+    action_dict = {}
+    for step in range(100):
+        for a in range(env.get_num_agents()):
+            action = np.random.randint(0, 5)
+            action_dict[a] = action
+        obs, all_rewards, done, _ = env.step(action_dict)
+        print("Rewards: ", all_rewards, "  [done=", done, "]")
+        env_renderer.render_env(show=True, frames=True, show_observations=True, show_predictions=False)
+        if sleep_for_animation:
+            time.sleep(0.5)
+
+
+if __name__ == '__main__':
+    if 'argv' in globals():
+        main(argv)
+    else:
+        main(sys.argv[1:])
diff --git a/flatland_2.0.md b/flatland_2.0.md
index 686141171dba2577976cfc876a910323ab79ae58..bb8fde87654684312400d7901824bf9f4b85007d 100644
--- a/flatland_2.0.md
+++ b/flatland_2.0.md
@@ -57,7 +57,7 @@ env = RailEnv(
 )
 ```
 
-You can see that you now need bot a `rail_generator` and a `schedule_generator` to generate a level. These need to work nicely together. The `rail_generator` will only generate the railway infrastructure and provide hints to the `schedule_generator` about where to place agents. The `schedule_generator` will then generate a schedule, meaning it places agents at different train stations and gives them tasks by providing individual targets.
+You can see that you now need both a `rail_generator` and a `schedule_generator` to generate a level. These need to work nicely together. The `rail_generator` will only generate the railway infrastructure and provide hints to the `schedule_generator` about where to place agents. The `schedule_generator` will then generate a schedule, meaning it places agents at different train stations and gives them tasks by providing individual targets.
 
 You can tune the following parameters in the `sparse_rail_generator`: