Scenario#

protocol enki_env.Scenario#

A scenario is a generator of world.

It exposes a callable that accepts a random seed and returns a pyenki.World.

>>> world = scenario(seed=0)

The callable has an optional argument to copy the random generator from another world, which is passed during the environment reset with seed=None.

Users have two ways to implement a scenario:

  1. The can write a callable that conforms to Scenario.__call__(), where they must pass the random seed to the world and possibly copy the random generator, e.g.:

    def my_scenario(seed: int, copy_rng_from: pyenki.World = None) -> pyenki.World:
        world = pyenki.World(seed=seed)
        if copy_rng_from:
            world.copy_random_generator(copy_rng_from)
        ...
        return world
    
  2. They can specialize a sub-class of BaseScenario, implementing one or both of BaseScenario.make() and BaseScenario.init(). This class takes care of setting random seeds and generators in its implementation of BaseScenario.__call__().

    For example

    class MyScenario
    
        def make(self) -> pyenki.World:
            return pyenki.World(radius=100)
    
        def init(self, pyenki.World: world) -> None:
            robot = pyenki.Thymio2()
            robot.angle = world.random_generator.uniform(0, math.pi * 2)
            world.add_object(world)
    

Users should use pyenki.World.random_generator for random sampling to ensure reproducibility.

Classes that implement this protocol must have the following methods / attributes:

__call__(seed: int, /, copy_rng_from: pyenki.World | None = None) pyenki.World#

Generates of a world with a given random seed.

Seed:

The random seed.

Copy_rng_from:

Another world to inherit the random generator from, if provided.

Returns:

The generated world.

class enki_env.BaseScenario#

Base class for implementations of enki_env.Scenario protocol.

Users can specialize sub-classes by implementing one or both of BaseScenario.make() and BaseScenario.init().

For example

class MyScenario

    def make(self) -> pyenki.World:
        return pyenki.World(radius=100)

    def init(self, pyenki.World: world) -> None:
        robot = pyenki.Thymio2()
        robot.angle = world.random_generator.uniform(0, math.pi * 2)
        world.add_object(world)
__call__(seed: int, /, copy_rng_from: pyenki.World | None = None) pyenki.World#

Implements Scenario protocol.

  1. calls BaseScenario.make()

  2. sets the random seed and generator

  3. calls BaseScenario.init()

Seed:

The random seed.

Copy_rng_from:

Another world to inherit the random generator from, if provided.

Returns:

The world.

init(world: pyenki.World) None#

Virtual method that initializes a given world.

The base implementation does nothing. Can be overridden by sub-classes to add objects.

Important

Any random sampling should use pyenki.World.random_generator to ensure reproducibility.

Parameters:

world – The generated world

make() pyenki.World#

Virtual method that returns a world.

The base implementation returns a empty world with no boundaries. Can be overridden by sub-classes to add a boundary.

Important

Should defer random sampling to BaseScenario.init().

Seed:

the random seed.

Returns:

the world.