Implementation

Interaction system

  1. Controller receives Stimuli and has access to both failed and successful actions tried during last quantum.

  2. Using the Entity's memory, the Controller's memory and the Entity's state, the Controller chooses an Action (and its parameters) for the next turn.

  3. At the next turn, the Entities trie to retrieve the chosen Action (in Actions pools). If it fails, the Entity does nothing; an error could be logged to detect broken Controller, but it's more probably a context change (ex: you can't fly anymore because the spell ended).

  4. The Action is resolved/performed. It implies Stimuli are sent to the StimuliDispatcher, self perception is sent to the Controller, and Entities are modified.

Performing an Action

Here we describe the simple sequence of messages needed for an Entity to perform an Action.

  1. The controller asks the Entity to perform an Action. When it does so, it invokes the perform(String actionName, Entity[] invokers, Entity[] receivers, Object[] parameters) method on the Entity.

  2. The Entity that is asked to perform the action looks up for the given Action in its individual list of Actions. If the Action is not available, the Entity looks up in the list maintained by the Influences it is influenced by in the priority order of these Influences. If the Action is still not found, it launches an Exception to indicate that it can not perform the action asked.

  3. Once the Action is located, the Entity retrieves the given Action object.

  4. The Entity then delegates the execution of the action to the retrieved Action object by invoking the perform(Entity[] invokers, Entity[] receivers, Object[] parameters) method on the Action.

The perform methods

In the case of Entity's perform method, the first argument is the name for the Action to be performed and not the Action in itself. This name will allow efficient lookup in actions lists and will not require the controller to obtain a reference to an action. By using this indirection level, it is possible to have several implementations of the same Action. It is the responsibility of the Entity to retrieve the appropriate Action. More precisely, the Action that will be used is the one that is first retrieved according to the priority order of the Entity's Influences.

Moreover, the controller needs to know about the result of an action, so, there should be some kind of return but not in a form of a simple return value. The controller will have two sources of information:

Hence the complete signature for the perform method would be: int perform(String actionName, Entity[] invokers, Object[] parameters) .

Parameters will a reference to an array of Objects, potentially null. Each Action will require a different set of parameters. The simplest case of parameter will be an Entity ID. (Example for action "take object": parameter = object EID). Other possible parameters type will be:

  1. - a Typed Value (time delay, date, distance, weight, etc..)

  2. - a Position (coordinates) in a place.

  3. - a Type of Entity. (an influence)

  4. - a Message

  5. - a Group of Entity (Group ID/Reference).

Return value of perform will be one of the following constants:

  1. - COMPLETED_ACTION

  2. - FAILED_ACTION

  3. - CONTINUING_ACTION

Decision Tree

Context

Each controlled entity maintains a list of actions planned for next turns. We will call an intention a reference to an action with all context parameters. The next action to perform will be determined by the success or failure of the previous actions: Intentions will be organized in structure called the Decision Tree

Obviously this list will be managed by the entity controller during the last phase of each turn. The controller will be able to know which actions have been declared impossible by the predicate system, which one have been executed during the turn, which one were delayed and remain in the list.

The controller will be able to cancel intentions which were not already started, and interrupt currently executed actions.

The result of an action will be known to the controller only through the perception system.

Long actions whose length is more than one turn will have a progress attribute, which is known to the controller. When the entity is the target of an external action while busy with an action, two situations are possible:

  1. the external action is coercive (for example, an attack) and the current long actions is immediately interrupted.

  2. the external action is not coercive, and the current action continues at least for the current turn.

In both cases, at the end of the turn, the controller can choose to react to the external action and interrupt the current action or to continue this action.

Interfaces exposed to entity controllers

At the end of each turn, all entity controller will be given the opportunity to update their Decision Tree. They will use the DecisionTree interface.

DecisionTree interface:

  1. IntentionRef program (long date, ActionId action,...); define an intention, schedule it in the list for date "date" and returns a reference for it. If many intentions are defined for the same date, this means, do the following actions as soon as possible, from this date in the order they have been defined.

  2. Iterator iterateOnIntentions(); Return an iterator on the set of all defined Intention in chronological order.

  3. void deprogram (IntentionRef ref); cancel an intention before it becomes active using its reference.

  4. void cancel (IntentionRef ref); cancel the current action (for long actions)

  5. Intention getIntention(IntentionRef ref); Retrieve the complete Intention data object using its reference

  6. IntentionRef getCurrentIntention(); Retrieve the reference of the intention of the current action.

  7. int getProgress(IntentionRef ref); Retrieve the value of the progress attribute of a long action.

Event message Each time an event concerning its decision tree happens, the controller will receive messages through a call back function: HandleEvent(Event e). The different decision tree messages will be:

  1. Intention_Started

  2. Intention_Completed

  3. Intention_Failed

  4. Intention_Interrupted

Interface exposed to the game scheduler

As the game controller will only manage the Decision Tree through a well defined interface, the Decision Tree implementation is a protected data structure. It can then be used by the framework to schedule actions which are not directly under the control of the controller: for example, the game can schedule the contact between the character and the ground after a jump, at the time determined for the end of the fall..After a deadly wound, the combat management code can schedule an action "die" for the next turn.

This means that only a subset of the defined actions are accessible to the entity controllers. The other actions will be used internally by the game system.

To make use a this facility, the simulation classes will have access to more functionalities of the Decision Tree than the controller itself. We will have to provide a more powerful interface in this case. In the context of the simulation, this interface will be called ActionTreeInterface. This interface will extend DecisionTreeInterface and will also define:

  1. Iterator iterateOnActions(): returns an iterator on the set of all defined Actions (Intentions + Actions) in chronological order.

  2. setStarted(IntentionRef ref): marks an intention as current.

  3. setCompleted(IntentionRef ref): marks an intention as completed; removes from list and send event to the controller.

  4. setProgress(IntentionRef ref, int value): updates the progress attribute of an intention.

  5. setFailed(IntentionRef ref): marks an intention as failed. Removes from list and send event to the controller.

  6. setInterrupted(IntentionRef ref): marks an intention as interrupted. Sends event to the controller.

Action scheduling

Once the action has been chosen by the controller, it is processed by the framework in three steps:

  1. Actions scheduling: what are the planned actions for the entity, are these actions possible, does the entity has enough time to perform them?

  2. Actions simulations: potential modifications of game and entities properties, planning of possible consequent actions.

  3. The action impacts the game and stimuli are send, allowing the controllers to plan future actions.

Actions scheduling is done by the framework, by example in the Entity class. A simple way to express this is to speak of the 'Action scheduler'.

The Action scheduler examines the Decision Tree at each turn, and decides which actions are performed. We can list the following cases:

  1. Simplest case: no current action inherited from the last turn, and one action in IL. The actions is checked for acceptability using the 'MayStart' Predicate. If the results is negative, the action is marked as failed. If the result is positive, the control is given to the action, with a reference to the Context.

  2. Long action case: one action is inherited from the last turn and marked current. The Action scheduler check if this action can continue using the 'MayContinue' predicate. If ok, the action continues, if not it is marked as interrupted.

  3. Consecutive actions case: more than one action can be performed in one turn, if they are short enough. The time taken by each performed action is known to the AS because this information is provided in the return of the perform function.

  4. No action case. Nothing to do. Current action reference remains null.

For each phase, we will provide some examples to make things clearer.

Action simulation

The action can be implemented as singleton. All state will be managed externally, through the Intention objects. So it is the time to define more precisely what an Intention is:

Each Action will be free to define what it wants to store in its state data. This data will be private and only useful to the Action code.

In the this section we should describe exactly which interfaces are available to performed actions. We already proposed a list of them:

We needs to define full access interfaces to the Perception System and the Event Dispatching System.

Controller planning

The controller interface will contain a call back method use to provide CPU time to the AI in order to plan or reconsider its Decision Tree. This function may be called plan.

Example

Example 1. Raoul trying to pick up flowers

Raoul the baker is in vacation and walk in the mountains. He sees some beautiful flowers and thinks they would make a great gift for his girlfriend Gertrude. So Raoul's fine AI controller plans the following consecutive actions:

  1. walk towards the flowers.

  2. pick up the flowers.

This plan is translated into the Decision Tree of the Raoul Entity. More precisely, the IL now contains two elements: - an Intention object referencing the Action "go" with a parameter equal to the EntityID of the flowers. - an Intention object referencing the Action "take" with a parameter equal to the EntityID of the flowers. Both Intention object are still simple only intention, the action have not started, and they don't reference any Action state objects.

First scenario, Raoul successfully perform both actions. During the next quanta, the game check for the possibility to perform the first action, and compute a positive answer. The entity Raoul really tries to perform this"go" action. First we goes through Action initialisation code, which takes 2 parameters: the subject Entity, the Intention Object. This initialisation phase creates an ActionState instance where it puts data it needs. In this example the data object can contain Raoul's initial position, current position and target position in terms of place coordinates. The target position is extracted from the Memory of Raoul: the flowers belong to his field of vision, if they would not, the action initialization would have failed (and the action too). After initialization, the Intention object in the tree contains the reference to the newly created ActionState object.

Then Raouls starts to actually perform the action, "Go" becomes the current action. perform() is called. The method checks the path and the distance between current and target position. If there is no way that exist and that Raoul can use, the Action fails (exemple if target = a cloud). If there is a way, the Action computes the time the move will take. If this delay is longer than the remaining time in the quanta, the action updates its state (current position = position at the end of the quanta)and the entity position in the place and return the "Action continuing" value. If the action ends before the end of the quanta, the action update the Entity state, discard its state and return the "Action completed" value.

The same procedure applie for the second Action. We suppose the quanta is still not over for the entity. First the game check if the action is possible. We suppose it is. Then it initialize it. Again, the initialization succeeds, before the flowers are known to Raoul from his visual memory.

The perform method this time does not compute the length of the action, because the "Take" action is supposed to take a constant time. But it checks that the object of the action is close enough from Raoul. The check succeed, then the flowers are removed from the scene and put into Raoul's inventory. The action returns the constant "Action performed".

In fact, Raoul may not have totally used its time quanta, but his Decision Tree is now empty. So he does not perform anything more. The simulation thread can process another entity.

Next time it will be activated, Raoul's controller will know that both actions in IL have been tried and in some way completed. From his perceptions, he will see that the flowers are gone from the place, and that they are now in his backpack.

Other scenario: the action fails: at the next turn, the game checks the predicates for the first action: 'walking'. It appears that Raoul is not experienced enough to climb up to the flowers. So the first actions fails. As a consequence, the second action is also impossible. Both fails, and Raoul does nothing this turn.

Let's describe more precisely what happens: the initialization of the first action " succeeds as in the first case. But the perform method, while evaluating the actions, estimates that the path to the flowers is almost vertical, and that would be too difficult for the beginner alpinist that Raoul is. So the methods returns the constant value "Action Failure". Raoul's time is also translated with an estimation of the time it would take to him to give up climbing to the flowers.

The second action "pick up" initializes too. But again, while the perform method is called, it returns "Action Failure" because the flowers are out of reach from Raoul's hand. And no other actions are in Raoul's Decision Tree.

During the next quanta, Raoul's controller will know from the Decision Tree, that both actions "failed". Raoul will know its new position through the perception system.

Other examples: complex action parameters.

  1. Raoul open his door with his key: perform("Open" ActionID, Raoul's Entity ID, [door of Midgaard's bakery EID, Raoul's key EID]) Object []: object to open, tool/object used to open (optional).

  2. Raoul prepares bread pasta: perform("Cooking/Mix" ActionID, Raoul's Entity ID, [kneader (container) EID, flour bag EID, water container EID, salt container EID ]) Object []: 1st element: target container, other elements: ingredients containers.

  3. Raoul prepares bread from pasta: perform("Cooking/Setup" ActionID, Raoul's Entity ID, [kneader EID, Bread TypeId]) Object []: 1st element:ingredient which to give form, 2st element: Type of product to be prepared.

  4. Raoul puts breads into the oven: perform("Move" ActionID, Raoul's Entity ID, [Oven EID, Bread1 EID, Bread2 EID, Bread3 EID,...]) Object []: 1st element: Target position, other: Objects to be moved.

  5. Raoul wait 10 minutes perform("Wait" ActionID, Raoul's Entity ID, [time delay=10min Value]) Object []: 1st element: time delay Value.

  6. Raoul says to his wife the breads are ready: perform("Talk" ActionID, Raoul's Entity ID, [Raoul's wife EID, Message]) Object []: 1st element: target Entity of message, next: message.

Action consequences

Example 2. Picking up a flower

The flower is removed from the place, and stimuli are emitted. The (cut) flower is added to the character's inventory.

Example 3. Jumping in a deep hole

Stimuli describing the move are emitted. An action 'falling' is planned for the time of crash.

Example 4. Falling on a surface

Stimuli describing the actions are emitted. If the shock is strong enough, the entity will be killed. In such a case, an action "die" is planned for the next turn.

Long actions and "hibernation mode" of entities

( Not in M1? To be completed.) Place in the game in where no players are active and where there is no perception should save CPU power. This can be done by using special long actions called hibernation actions, during which no action planning is done by AI controllers. The Stimuli Dispatcher will be able to interrupt these actions, which will effectively "wake up" the entities. Again, the stimuli dispatcher will also be responsible to decide to start of the hibernation.

Example 5. In the bakery

Raoul the baker has not seen any visitors for the last few turns. The StimuliDispatcher of the 'Bakery' Place knows there is no visitor around. It sends a special message to the Entity Raoul to signify that the place will start to hibernate. Raoul's controller then starts a long action "continuous baking": continuous baking means that Raoul will bake bread continuously until something interrupts him. The result of the actions will only be computed at the time of this interruption. For example, if Raoul bakes continuously for 30 turns, he will obtain 20 pieces of breads and 10 sandwiches.

It should be possible to avoid this extra complexity and at the same time building a game CPU savvy: build the AI of all common entities so that their common state would be a long action similar to 'continuous baking'. In the case of Raoul, the Dispatcher will interrupt the action when someone enters the Bakery, we don't need to provide extra code for CPU resource management.

Implementation data

The DecisionTree will be implemented with (To be completed).