Ultimately, in PX-MBT we want to be able to run test cases using an emotive test agent. The agent is 'emotive', with the idea that it can simulate human emotion during the test run. However, before we can get to that point the agent will also need the following inputs, so we need to prepare them as well:
- Specify which 'events' in the game are considered relevant for emotion.
- Define a so-called 'player characterization', specifying how events affect the player's preception on his/her playing goals.
- Provide a concretization function, this was discussed here.
- Specify which information should be written to the trace file.
The easiest way to run an emotive agent is by running it through an PX-agent runner. The constructor of this runner shows where the above four 'inputs' need to be plugged in:
PXTestAgentRunner(Function<Void,EmotiveTestAgent> agentConstructor,
XUserCharacterization playerCharacterization, // input-2
SyntheticEventsProducer eventsProducer, // input-1
... concretizationFunction, // input-3
... customStateInstrumenter, // input-4
... )
This is done by implementing the abstract cass SyntheticEventsProducer
from aplib. The main method to implement is:
generateCurrentEvents()
. The events-producer has access to the test agent, and therefore also its state. This state might keep tracks of events generated by the game under test. In this case you only need to copy these events to the events-list maintained by the producer. However, this is not a likely scenario, as game events are usually internal (not exposed to an external agent), so you will have to define yourself what events should be generated based on the game state. Usually, this will in turn also need the agent to also keep track its previous state. An event can be defined as occurring when a certain difference between the current and previous state occurs. E.g. an event See-fire can be generated when in the current state the agent sees fire object which it does not see in the previous state. An event Ouch can be generated when the agent's health decreases with respect to its previous health.
Events generated by a SyntheticEventsProducer
are not meant to trigger any functional behavior. The producer's role is just to keep track of 'events' of interest that occur during a test run. At each update cycle of the test agent, this method generateCurrentEvents()
will be invoked to calculate the events that occur in that update, if there is any.
An example of an implementation SyntheticEventsProducer
can be seen in the class MiniDungeonEventsProducer
.
To write a so-called player characterizaiton you need to implements the abstract class XUserCharacterization
(which in turn extends the class UserCharacterization
from JOCC). The main method to implement is:
-
eventEffect(event, beliefbase)
A 'beliefbase' keeps track of the player's (well, represented by the test agent) goals when playing the game. An obvious example is "to win the game" as a goal. For each goal, the beliefbase maintains the current perceived likelihood of eventually achieving the goal. Note that we want to know the likelihood as preceived by the player. We don't want to know the actual likelihood, even if know how to calculate the latter.
Given an event e that just occur, the method
eventEffect(e,beliefbase)
models how the player responds to that event in his/her feel on the likelihood of achieving each goal in the beliefbase. Ultimately, this perceived likelihood is the information that drives most of the emotion simulation. E.g. when the perceived likelihood of achieving a goal G, the emotion fear (towards G) might be triggered. When the perceived likelihood becomes 100%, the emotion joy is triggered. When the goal is actually achieved (not just perceived as certainty) the emotion satisfaction is triggered.When used in PX-MBT, the beliefbase will be an instance of the class
OCCBeliefBase
which also holds a reference to the agent's state, so you can use that information as well when modelling event-effects. -
desirabilityAppraisalRule(goals_status,eventName,goalName)
to specify how desirable an event is towards achieving a goal.
An example of an implementation can be seen in the class MiniDungeonPlayerCharacterization
.
Input-3: concretization: this was discussed here
When an emotive test agent is run, it will generate a trace file. By default, it will write its emotion state at every update cycle to the trace. The emotion state consists of six emotions (based on the OCC theory we mentioned before): hope, fear, joy, distress, satisfaction, and dissatisfaction. The emotions are goal-oriented emotions, so for each goal G kept in the aforementioned beliefbase, we have all those six emotions towards G.
Quite often we also want to trace other information as well, for example time, the agent's location, and its physical health. To do this you need to specify your own tracing function. It is a function of type:
Function<EmotiveTestAgent,Function<SimpleState,Pair<String,Number>[]>>
Given an agent a and its current state S, such a function f(a,S) (or, in actual Java syntax it would be: f.apply(a).apply(S)
) it produces an array of pairs of (x,val) where x is a property name e.g. "health" and val is its value. These pairs are then written to the trace file.
As an example, below we show here the code of the default tracing function. The important things to note are: (1) how to get to the agent emotion state (through agent.getEmotionState()
). And (2), the parameter state
will be bound to the agent functional state, so if you want to trace other information e.g. time stamp you can get it through this state
(if it does contain time stamp, in this example; it depends of course on your implementation of state
).
var defaultStateInstrumenter = agent -> state -> {
E = (OCCState) agent.getEmotionState() ;
var goals = getOCCgoals() ;
int NumGoals = goals.size() ;
Pair<String,Number>[] emotions = new Pair[6*NumGoals] ;
int k = 0 ;
for(var g : goals) {
int j = k*NumGoals ;
emotions[j] = new Pair<String,Number>("" + EmotionType.Hope + "_" + g, E.hope(g)) ;
emotions[j+1] = new Pair<String,Number>("" + EmotionType.Joy + "_" + g, E.joy(g)) ;
emotions[j+2] = new Pair<String,Number>("" + EmotionType.Satisfaction + "_" + g , E.satisfaction(g)) ;
...
k++ ;
}
return emotions ;
}