Planner Reasoner

The Planner Reasoner class enables you to use Grail’s planning algorithm to govern behaviors of AI Entities, by managing goal selection, state and action translation, the number of the planner’s iterations and the execution of behavior sequences.

If you wish learn about the concept of planning or use planners in a different context, see the Grail Planner page.

To make Planner Reasoner work properly, you have to provide it with some information about the game world and various goals that can be chosen. The following sections describe the required work.

To see a complete example, see Planner Reasoner: Quickstart page.

Domain definition, state and action translation

For the planner to work, you must provide a domain definition, i.e., declare all possible objects and action types that can be considered by the planner.

The other most important thing needed by the planner is a game state description - because Grail’s planning algorithm operates on abstract game states, you have to provide logic that will translate the state of your game to an abstract representation digestable by the planner.

Additionally, you have to provide a piece of code that will translate action sequences to behaviors that can be executed by an entity.

To help you with these tasks, Grail provides the DomainTranslator class that you can derive from. See this page for more details.

Callbacks

Planner Reasoner allows you to define callbacks to be executed at important stages of its operation:

  • planning succeeded - a plan has been found and the agent will start executing a sequence of behaviors,

  • plan executed - the current plan has been executed,

  • planning failed - no plan could be found and the agent will do nothing until it receives a new goal,

  • partial plan found - no plan leading to the goal state could be found, but the user allowed for partial plans (see the Partial plans section in the Planner documentation and code examples in the Planner Quickstart); the agent will execute the plan that brings it as close as possible to the goal, according to the heuristic passed by the user.

They get called once per planning attempt for a given goal. The following code example show how to define them in code.

  • C++

  • C#

reasoner->BindPlanningSucceededCallback([](const grail::Plan& plan, const grail::planner::Goal& goal)
{
  std::cout << "Planning succeeded\n";
});

reasoner->BindPlanExecutedCallback([]
{
  std::cout << "Plan executed\n";
});

reasoner->BindPlanningFailedCallback([](const Goal& goal)
{
  std::cout << "Planning failed\n";
});

reasoner->BindPartialPlanFoundCallback([](const grail::Plan& plan, const grail::planner::Goal& goal)
{
  std::cout << "Partial plan found\n";
});
reasoner.OnPlanningSucceeded = (plan, goal) =>
{
  Console.WriteLine("Planning succeeded");
};

reasoner.OnPlanExecuted += () =>
{
  Console.WriteLine("Plan executed");
};

reasoner.OnPlanningFailed = goal =>
{
  Console.WriteLine("Planning failed");
};

reasoner.OnPartialPlanFound = (plan, goal) =>
{
  Console.WriteLine("Partial plan found");
};

Complete Code Example

The following code example shows how the concepts and tools described above can be put together.

  • C++

  • C#

auto pool = std::make_unique<grail::planner::MemoryPool>();

//You have to define game state and action translation logic by providing a class derived from grail::planner::DomainTranslator
auto translator = std::make_unique<MyDomainTranslator>(*pool);

//You can define the goal selection method by implementing IGoalSelector interface
auto goalSelector = std::make_unique<MyGoalSelector>();
auto entity = std::make_shared<grail::AIEntity>();

grail::planner::PlannerReasoner::Config config;
config.iterationsPerFrame = 20;
config.maxIterations = 50000;
config.maxPlanLength = 20;
config.usePartialPlans = true;

//assign the reasoner to the entity
entity->SetReasoner(std::make_unique<grail::planner::PlannerReasoner>(*pool,
  goalSelector,
  std::move(translator),
  config));

//register the entity
manager.RegisterAIEntity(entity, 0);
//You have to define game state and action translation logic by providing a class derived from grail::DomainTranslator
var translator = new MyDomainTranslator();

//You can define the goal selection method by implementing IGoalSelector interface
var goalSelector = new MyGoalSelector();
var entity = new AIEntity();

var config = new PlannerReasoner.Config()
{
  IterationsPerFrame = 20,
  MaxIterations = 50000,
  MaxPlanLength = 20,
  UsePartialPlans = true
}

//assign the reasoner to the entity
entity.SetReasoner(new PlannerReasoner(goalSelector, translator, config));

//register the entity
manager.RegisterAIEntity(entity, 0);