HybridUtilityPlanner

A planner that combines Utility AI's value-based action picking with goal-satisfaction termination — the "iterate then stop" mode.

The shape it expresses. Some reducer-style pipelines benefit from doing all opportunistic work (research, enrichment, context gathering) before a single terminal action that synthesises the outcome. Pure GOAP A* doesn't pick those opportunistic actions — it minimises path cost to the goal and skips anything off the critical path. Pure UtilityPlanner picks by value but, on a real (satisfiable) goal, gives up at step 1 if no single action reaches the goal; on NIRVANA it iterates forever, even past goal satisfaction. This planner is the missing middle.

How it works. Register two goals on the agent:

At each tick the planner generates a plan per goal. For NIRVANA it returns the highest-netValue achievable action as a 1-step plan; that's how research actions get picked while they still have positive value and haven't been locked out by canRerun=false. For the real goal it returns an empty plan once the goal is already satisfied, or a 1-step plan if a single action reaches it, or null. The host process picks the highest-net-value plan across goals — empty plan has netValue=0, beating NIRVANA's negative-value tail once research is exhausted. The process then sees plan.isComplete() and terminates cleanly.

Difference from UtilityPlanner. UtilityPlanner only checks "is the goal already achievable?" when there are zero achievable actions. That means once your real goal is satisfied but other (low-value, no-op) actions remain achievable, UtilityPlanner keeps picking them. HybridUtilityPlanner checks "is the goal already satisfied?" first, so satisfied-goal returns an empty plan even when other actions could still run. This is the natural termination signal for two-goal patterns.

When to choose this over UtilityPlanner.

  • You have a real terminal goal AND want greedy value-based intermediate scheduling.

  • You're combining NIRVANA with a satisfiable goal and need clean termination once the real goal is reached.

  • You don't want the A* multi-step cost-minimization of GOAP.

For pure-iteration use cases (chat surfaces, exploratory loops), keep UtilityPlanner + NIRVANA — the over-firing behavior is benign there.

Constructors

Link copied to clipboard
constructor(worldStateDeterminer: WorldStateDeterminer)

Functions

Link copied to clipboard

Return the best plan to any goal

open fun bestValuePlanToAnyGoal(system: PlanningSystem, excludedActionNames: Set<String>): ConditionPlan?

Return the best plan to any goal, excluding specified actions. Used to prevent infinite loops when replanning.

Link copied to clipboard

Return the best plan to each goal from the present world state. The plans (one for each goal) are sorted by net value, descending.

Link copied to clipboard
open override fun planToGoal(actions: Collection<Action>, goal: Goal): ConditionPlan?

Plan from here to the given goal Return null if no plan found

Link copied to clipboard
open override fun prune(planningSystem: ConditionPlanningSystem): ConditionPlanningSystem

Return a PlanningSystem that excludes all actions that cannot help achieve one of the goals from the present world state.

Link copied to clipboard

Current world state