AI Planning is a process of realization of strategies or action sequences. Hierarchical Task Networks are a subset of AI Planning approaches where tasks(actions) can require calling other tasks or be composed of multiple methods.
A task that is composed of multiple methods is called a composite task. A composite task is planned successfully when one of its methods is planned to run. A method and a primitive task can only call other tasks or operators. Operators are atomic identifiers of the underlying process and are represented by Strings. This project is inspired by AI Pro's article on HTNs
Many research projects in HTNs are driven by the LISP community (e.g. SHOP2) which often drives new developers away. This project implements a python-like syntax parser to allow for a friendlier HTN experience. Sample problems can be found in htn-problems folder. Overall the syntax uses newlines and tabulation as means of introducing expression separators and blocks. The following keywords are recognized:
task
- represents the beginning of a task statement. Full syntax:task NAME(preconditions):
whereNAME
is any non-keyword string of alphanumeric characters andpreconditions
is a boolean expressionmethod
- represents the beginning of a method statement. Methods can be defined insidetask
s only. Full syntax:method NAME(preconditions):
whereNAME
is any non-keyword string of alphanumeric characters andpreconditions
is a boolean expression.else
- syntactic sugar formethod DontNAME(!preconditions):
- inverts the preconditions of a previous method. Since this is an equivalent of a method,else
can only appear in atask
body.effects
- represents the beginning oftask
's effects block. Effects usually contain variable assignment statements. Must appear on the same identation level astask
without any blank lines before the last task.or
,and
,not
- syntactic sugar for|
,&
,!
boolean operations.true
,false
- syntactic sugar for1
and0
literals. Internally all expressions operate on Rust'si32
type.
Example:
task Main:
method FindTrunk(WsTrunkHealth == 0): # precondition - can only run this is WsTrunkHealth is 0
UprootTrunk()
Main() # Supports recursion, will plan Main task again.
method Attack(WsCanSeeEnemy): # name(conditions)
AttackEnemy()
Main()
else: # syntactic sugar for "method DontAttack(!WsCanSeeEnemy)"
CheckBridge()
Main()
method Wait:
wait()
Main()
task AttackEnemy:
method AttackWithTrunk(WsTrunkHealth > 0):
NavigateToEnemy()
DoTrunkSlam()
task DoTrunkSlam(WsTrunkHealth > 0):
DoTrunkSlamOperator()
effects:
WsTrunkHealth -= 1