Planners: goal representation

In Grail, planner goals are represented as collections of functions with bool return type. Why not just a single one, you ask? If you define your goal as a list of subgoals, it’s much easier to come up with a sensible planner heuristic - you may use Hamming distance between the current state and the goal or penalize unsatisfied goal conditions in a different way.

Goal class

To bridge the abstract goal representation with your game’s world, Grail provides the Goal class. For reference, see the example below:

  • C++

  • C#

class MyGoal : public grail::Goal
{
public:
  MyGoal() = default;

  // Inherited via Goal
  virtual bool IsReached(const grail::AIEntity& entity) const override
  {
    //return true if the goal is reached, false otherwise
  }

  //this method specifies goal conditions in the abstract plan space
  virtual void SetPlanSpaceConditions(grail::Planner& planner, const grail::ObjectIndexBinding&) const override
  {
    planner.PushCondition([](const grail::WorldState& state)
    {
      return state.GetParameterValue<int>("int_param") > 0;
    });
  }
};
public class MyGoal : Goal
{
  public override bool IsReached(AIEntity entity)
  {
    //return true if the goal is reached, false otherwise
  }

  public override void SetPlanSpaceConditions(Planner planner, ObjectIndexBinding binding)
  {
    planner.PushCondition(state => state.GetParameterValue<int>("int_param") > 0);
  }
}

Goal selection

Grail does not make any assumptions on goal selection method. The only thing you have to do is to provide an implementation of GoalSelector class (or IGoalSelector interface in C#). For reference, see the example below:

  • C++

  • C#

class DummyGoalSelector : public grail::planning::GoalSelector
{
public:
  DummyGoalSelector() = default;

  virtual void UpdateGoal(grail::planning::IGoalAcceptor& goalAcceptor, grail::AIEntity& entity) override
  {
    //if no goal is currently chosen, set one
    if (goalAcceptor.GetCurrentGoal() == nullptr)
    {
      goalAcceptor.SetNewGoal(std::make_unique<MyGoal>(), entity);
    }
  }

private:
  std::string itemToCraft;
};
class DummyGoalSelector : IGoalSelector
{
  public void UpdateGoal(IGoalAcceptor goalAcceptor, AIEntity entity)
  {
    //if no goal is currently chosen, set one
    if (goalAcceptor.CurrentGoal  == null)
    {
      goalAcceptor.SetNewGoal(new MyGoal(), entity);
    }
  }
};