Utility Reasoner

Utility reasoner allows you to use utility systems out of the box. To make it work you only need to provide blueprints mapped with evaluators.

Persistence

Persistence is an optional parameter determining a score bonus for the currently executed behavior. If the entity has no assigned behavior, everything will be processed as usual. Otherwise, the current behavior will be evaluated again and its score will be multiplied by (1 + persistence). The purpose of persistence is to allow you to force entities to swap behaviors only if new behavior options are significantly better than the current one, to prevent erratic behavior.

Operation Stack

While using this reasoner, suspended behaviors are put on top of the stack and can be retrieved later if they are still legal. Behaviors from the stack also get a persistence bonus to their score.

Example

If your character is running to a point of interest, but gets ambushed by enemies and decides to fight back the nearest one, run behavior is suspended and put on top of the stack. When the character finishes fighting targeted enemy, if persistence is high enough, they will abort fighting other enemies and resume suspended "run" behavior. Otherwise, bonus will be too small and the character will commit to fighting the rest of the enemies. However, if somehow "run" behavior gets delegalized (for example: the character ran out of stamina), it will be removed from the stack.

Code Example

  • C++

  • C#

//Curves
std::shared_ptr<LinearFunction> attackCurve{ new LinearFunction{1, 0} };
std::shared_ptr<LinearFunction> healCurve{ new LinearFunction{-1, 1} };

//Consideration
std::shared_ptr<SimpleConsideration> healthPercentageConsideration{ new SimpleConsideration{} };

//Evaluators
auto attackEvaluator = std::make_shared<UtilityEvaluator<EntityBlackboardPair>( rail::WeightEvaluationMethod::MULTIPLY,
{
  {{healthPercentageConsideration}, attackCurve} //consideration mapped with curve
} );
auto healEvaluator = std::make_shared<UtilityEvaluator<EntityBlackboardPair>( grail::WeightEvaluationMethod::MULTIPLY,
{
  {{healthPercentageConsideration}, healCurve} //consideration mapped with curve
} );

//Blueprints - last 2 template arguments are skipped because they are defaulted to EntityBlackboardPair and AIEntity
grail::Blueprint<grail::Behavior> fightBlueprint("fight", Fight::ProduceContext, Fight::ProduceInstance);
grail::Blueprint<grail::Behavior> healBlueprint("heal", GetMedkit::ProduceContext, GetMedkit::ProduceInstance);

//Blueprints mapped with evaluators
std::map<std::shared_ptr<grail::IEvaluator<grail::EntityBlackboardPair>>, grail::Blueprint<grail::Behavior>> blueprintOptions
{
  {attackEvaluator, fightBlueprint},
  {healEvaluator, healBlueprint}
};

grail::AIManager manager{ 0 }; //manager without worker threads
auto entity = std::make_shared<AIEntity>(); // constructed new entity
entity->SetReasoner(std::make_unique<grail::UtilityReasoner>(std::move(blueprintOptions))); //assign constructed reasoner to entity
manager.RegisterAIEntity(soldier, 0); //register entity in manager
...

manager.UpdateReasoners(); //allow entities to query their reasoners
...
manager.UpdateEntities(delta); //allow entities to update their behaviors' state
//Curves
var attackCurve = new LinearFunction(1, 0);
var healCurve = new LinearFunction(-1, 1);

//Consideration
var healthPercentageConsideration = new SimpleConsideration();

//Evaluators
var attackEvaluator = new UtilityEvaluator<EntityBlackboardPair>(WeightEvaluationMethod.MULTIPLY);
attackEvaluator.AddCurve(healthPercConsideration, attackCurve); //consideration mapped with curve
var healEvaluator = new UtilityEvaluator<EntityBlackboardPair>(WeightEvaluationMethod.MULTIPLY);
healEvaluator.AddCurve(healthPercConsideration, healCurve); //consideration mapped with curve

//Blueprints
var fightBlueprint = new Blueprint<Behavior, EntityBlackboardPair, AIEntity>("fight", Fight.ProduceContext, Fight.ProduceInstance);
var healBlueprint = new Blueprint<Behavior, EntityBlackboardPair, AIEntity>("heal", GetMedkit.ProduceContext, GetMedkit.ProduceInstance);

//Blueprints mapped with evaluators
var blueprintOptions = new Dictionary<IEvaluator<EntityBlackboardPair>, Blueprint<Behavior, EntityBlackboardPair, AIEntity>>()
{
  {attackEvaluator, fightBlueprint},
  {healEvaluator, healBlueprint}
};

var manager = new AIManager(0); //manager without worker threads
var entity = new AIEntity //constructed new entity
{
  Reasoner = new UtilityReasoner(blueprintOptions) //assign constructed reasoner to entity
};

manager.RegisterAIEntity(soldier, 0); //register entity in manager
...

manager.UpdateReasoners(); //allow entities to query their reasoners
...
manager.UpdateEntities(delta); // allow entities to update their behaviors' state.