GridObjects#

In this section we describe the grid-object protocol, the grid-object registry, and how to write your own custom grid-objects.

The GridObject Protocol#

A grid-object is a type of python class which represents the contents of a Grid. A grid-object needs to inherit from GridObject and define some required attributes, properties, and classmethods.

class GridObject(*args, **kwargs)[source]

Represents the contents of a grid cell

abstract property state_index: int

State index of this grid-object

Return type

int

abstract property color: gym_gridverse.grid_object.Color

Color of this grid-object

Return type

Color

abstract property blocks_movement: bool

Whether this grid-object blocks the agent from moving on it

Return type

bool

abstract property blocks_vision: bool

Whether this grid-object blocks the agent’s vision.

Return type

bool

abstract property holdable: bool

Whether the agent can pick up this grid-object

Return type

bool

abstract classmethod can_be_represented_in_state()[source]

True iff state_index fully represents the grid-object state.

GridObjects may have an internal state which is not fully representable by a single integer state_index, e.g., a Box contains a reference to another GridObject as its content. The unfortunate implication is that this GridObject (and, by extension, any Grido or Environment which contains this type of GridObject) cannot produce a truly fully observable State representation, which becomes disallowed. However, the GridObject, Grid, and Environment may still be used to represent partially observable control tasks.

Return type

bool

abstract classmethod num_states()[source]

Number of internal states.

GridObjects themselves can have internal states, e.g., a Door may be open, closed, or locked. This classmethod return the number of possible states that this GridObject may have.

Return type

int

The GridObject Registry#

The grid_object module contains some pre-defined grid-objects and the grid_object_registry, a list-like object through which to register and retrieve grid-objects. Grid-objects are automatically registered through inheritance from GridObject. grid_object_registry has a names() method which returns the names of registered grid-object classes, and a from_name() method which returns the grid-object class associated with a name.

Custom GridObjects#

Custom grid-objects can be defined so long as they satisfy some basic rules; A custom grid-object:

Tip

We advise implementing each of the state_index, color, blocks_movement, blocks_vision, and holdable attributes as:

  • class attributes if all instances of the same grid-object class should have the same constant value, e.g., holdable is implemented as a class attribute because doors are never holdable.

  • instance attributes if different instances of the same grid-object class can have different values, but the value associated with each instances is constant and does not change, e.g., color is implemented as an instance attribute because doors can have different colors, but they don’t change colors.

  • properties if different instances of the same grid-boject class can have different values, and the value associated with each instance can also vary, e.g., state_index, blocks_movement, and blocks_vision are impemented as properties because they will depend on whether the door is open or closed, which is determined by a separate state attribute.

Attention

If any of the grid-object attributes are implemented as instance attributes, you’ll need to overwrite the respective GridObject abstract property. This can be done by simply specifying a class-level typehint:

class SomeColoredGridObject(GridObject):
  color: Color  # this typehint is required to make `color` an instance attribute

  def __init__(self, color: Color):
    self.color = color
    ...  # rest of initialization

  ...  # rest of class definition

Practical Examples 1 & 2#

Note

The examples shown here can be found in the examples/ folder.

In the followinng examples, we are going to create two grid-objects which thematically influence the agent movement. The first grid-objoect is an Ice, which may have two statuses: SMOOTH, which indicates that the agent may walk on it, and BROKEN wich indicates that the agent may not walk on it. Further, it may be thematically interesting to make the agent slip when moving on a SMOOTH Ice, continuing its movement until it hits an obstacle.

Important

Note that the grid-object only defines these properties and attributes of an Ice, while the behavior of the agent trying to walk on the tile should be implemented separately as a TransitionFunction (we leave this as an exercise).

from __future__ import annotations

import enum

from gym_gridverse.grid_object import Color, GridObject


class Ice(GridObject):
    """An icy tile which can be `smooth` or `broken`."""

    color = Color.NONE
    blocks_vision = False
    holdable = False

    class Status(enum.Enum):
        SMOOTH = 0
        BROKEN = 0

    def __init__(self, state: Ice.Status):
        self.state = state

    @classmethod
    def can_be_represented_in_state(cls) -> bool:
        return True

    @classmethod
    def num_states(cls) -> int:
        return len(Ice.Status)

    @property
    def state_index(self) -> int:
        return self.state.value

    @property
    def blocks_movement(self) -> bool:
        return self.is_smooth

    @property
    def is_smooth(self) -> bool:
        return self.state is Ice.Status.SMOOTH

    @property
    def is_broken(self) -> bool:
        return self.state is Ice.Status.BROKEN

    def __repr__(self):
        return f'{self.__class__.__name__}({self.state!s})'

The second grid-object is IceCleats, a holdable object which may be used to help an agent walk on a SMOOTH Ice without slipping. Once again, the behavior of an agent walking on Ice wihle holding IceCleats should be implemented by an appropriate TransitionFunction.

from gym_gridverse.grid_object import Color, GridObject


class IceCleats(GridObject):
    state_index = 0
    color = Color.NONE
    blocks_movement = False
    blocks_vision = False
    blocks_holdable = True

    @classmethod
    def can_be_represented_in_state(cls) -> bool:
        return True

    @classmethod
    def num_states(cls) -> int:
        return 1