Creating an Entity

Step 6: Create Controllable Entities

Now that the main part of the Interface stands, we want to model one or more controllable Entities. To let the Interface know what can be done by these Entities, and what they perceive, we need to implement some methods.

We start by creating a new class for the Entity, we will name it Entity but another name is fine as well. Especially when having multiple (different) entities it might be useful to use other names.

public class Entity {
    private Hanoi controller;

    public Entity(Hanoi controller) { this.controller = controller; }

    ... Percepts and Actions here ...
}

We pass the environment instance to the Entity, (seen before in step 5) so that we can easily call the methods that are available within the environment.

Percepts

Now we need to define what information can be send / perceived from the environment. This information will become available to your agent platform through Percepts.

@AsPercept(name = "disc", multiplePercepts = true, multipleArguments = true, filter = Filter.Type.ONCE)
public List<List<Integer>> discs() {
    List<List<Integer>> discs = new ArrayList<~>();

    if (controller.getPins() != null) {
        for (Pin pin : controller.getPins()) {
            Disc disc = pin.discs;
            while (disc != null) {
                // Add information to the lists.
            }
        }
    }

    return discs;
}

The example above collects all data from the current discs in the Hanoi environment. By using the @AsPercept annotation, the interface knows that this method will generate Percepts for the agent side. The annotation takes a couple of arguments;

  • name: the name of the percept.
  • multiplePercepts: indicating whether this percept consists out of multiple (separate) percepts, or a single percept.
  • multipleArguments: indicating whether this percept has multiple arguments in it.
  • filter: indicating the filter type for this percept (send once, always, on change, etc. See section below.).

A percept can return several types of data. Most standard Java types are supported, like intStringfloat, etc. If they're not supported you can add support for them manually. An example of a really simple percept is shown below;

@AsPercept(name = "pins")
public int pins() { return controller.getPins().length; }

Filter types

All percepts can get a filter. The primary use of a percept filter is to reduce the amount of percepts that the agent receives. Particularly, to filter out percepts that stay the same over long periods of time. The following filter types are available:

  • ALWAYS: Return all percepts always. This is the default filter setting of percepts.
  • ONCE: Return all percepts only the first time that the agent asks for its percepts.
  • ON_CHANGE: Return only the percepts if something in the percepts changed. Return an empty list if nothing changed
  • ON_CHANGE_NEG: Return only the newly appeared percepts, plus not(p) for all percepts pthat disappeared.

To determine if a percept object was changed, the raw percept object (before translation, see below) is compared with the previous percept object.

Regarding filtering, every percept object is coming from an @AsPercept, so also 'multiple' percepts are viewed as a single object (a list with percept objects). So if you return a list of objects and one element of that list changes, the whole list is considered as changed.

Actions

In a similar way we define the actions available to the agents. Actions can change the state of the Environment.

@AsAction(name = "move")
public void moveDisc(int from, int to) {

    // Perform checks..

    // Execute actual command.
    controller.moveDisc(controller.getPins()[from], controller.getPins()[to]);
}

The name attribute within the annotation defines the name of the action as it is available to the agents. The parameters of the method define what information should be provided in order to perform the action. Make sure to check for all possible conditions (invalid parameters etc.) in order to avoid Exceptions.

Translation between eis.iilang and java.object

As you may have noticed, eis2java adds an abstraction mechanism on top of the basic eis.iilangstructure. In contrast with the EnvironmentInterfaceStandard, eis2java provides java-typed objects in its interface. You saw this already where we wrote as action moveDisc(int from, int to) rather than moveDisk(Identifier disk, Identifier to).

To give an example:

  • on the EIS interface layer we can have a percept on('a','b') as an eis.iilang object. 'on' is an eis.iilang.Function and the Parameters are eis.iilang.identifiers 'a' and 'b'.
  • On the eis2java layer we could have a java object of class 'on' with two class fields, say String belowString ontop; The @AsPercept can directly return such an 'on(a,b)' java class object.

To pass objects back and forth through eis2java, percept objects have to be translated into eis.iilang.Parameter objects. When percepts go from EIS to the Agent, we have to translate an object to parameter. When an action goes from an Agent to EIS we have to translate from Parameter to Object.

eis2java determines automatically which translators are needed. To do this, it uses introspection to determine the parameters required for the @AsAction function, and it can determine directly from the objects coming out of the @AsPercept functions.

Translation between percept and object is done by Java2Parameter and Parameter2Java. A translator-factory is available to manage translations: eis2java.translation.Translator.

A number of translators are provided by EIS: Boolean, Char, Double, Float, integer, Long, Number, Short, String.

If you want to use other percept objects, you need to do a few things

  • Create a class T representing your percept object
  • This class must properly implement equals. This is because the percept filter (see above) uses this directly on percepts objects to check percept changes.
  • Create translators: a Java2Parameter<T> and a Parameter2Java<T> that translates between eis.iilang objects and objects of type T
  • Register these translators with the eis2java.translation.Translator. Usually this is done in a static code block in your Entity.