Revision 11 as of 2009-09-22 08:56:46

Clear message

Teleo Reactive Ponder2


A teleo-reactive (T-R) program is a mid-level agent control program that robustly directs an agent toward a goal in a manner that continuously takes into account the agent's changing perceptions of a dynamic environment. T-R programs are written in a production-rule-like language and require a specialized interpreter [ ref ].

T-R programs typically have an ordered set of rules (a Rule Set) each of which contains a condition and one or more actions. The Rule Set then selects the first rule whose condition is true and starts executing the rule's actions. What follows afterwards depends upon the Rule Set's configuration and is described below.

Here is a simple example of a T-R rule set. It is for a robot truck that starts somewhere other than its depot. It's goal is to pick up a bin and put it down at the depot. When it starts off it may or may not be facing the bin (we assume that it is not next to it but that would work too). If it is not facing the bin then rule 6 would be invoked. If it is facing the bin the rule 7 would be invoked and it would move forwards to the bin.

So, if the rule set were continuously evaluated you can see that the truck would start turning to the left, then move forwards, pick up the bin, turn to the left again, go forwards and drop the bin off at the depot. This is bacause the lower numbered rules take precedence over the higher numbered ones. So, if rule 7 was being continuously executed then as soon as the truckwas facing the bin, rule 6 would take over and the truck would move forwards.





unloaded & at depot



loaded & at depot



loaded & facing depot

go forwards



turn left


next to bin

load bin


facing bin

go forwards


turn left

Example Notes


Rules have a condition and one or more actions. The rule can be asked if it is available to be run i.e. to evaluate its condition and return the result, true or false. The rule can also be told to execute its actions. A rule can handle two or more parallel sequences of actions, it does this by spawning off separate threads and waiting for them all to finish before indicating that its actions have been completed. Actions are normally blocks but may be Rule Sets or even other rules, more about this later, for the time being we will just use blocks. NB Conditions are likely to be run many times and at any time therefore condition blocks should not have any side effects.

A rule in PonderTalk is created as follows:



condition: aCondition

Creates a rule with the given condition block aCondition

condition: aCondition action: anAction

Creates a rule with the given condition block aCondition and the given action action anAction

condition: aCondition actions: anActionArray

Creates a rule with the given condition block aCondition and the given array of action in anActionArray

Example: Creating a new rule

Here we can see the TR rule factory being imported and then a new rule Managed Object is created with a condition that tests if myValue equals 23. Its action is a block that when activated moves the cart forwards. Notice that both the condition and action are blocks. Blocks are not executed when they are compiled but are activated later when needed; in this case, when the rule is told to evaluate its condition and when the rule is told to run it action(s).

   1 rule := root load: "TrRule".
   2 myrule := rule condition: [ myValue = 23 ] action: [ cart forwards ].

Once the rule has been created, more actions can be added to it, including parallel markers. The operations allowed on a rule are:



action: anAction

Adds an action to the rule

actions: anActionArray

Adds an array of actions to the rule. The actions are copied from the array, the array is discarded


Indicates that any following actions are to be run in parallel with the previously given actions. This command may be used many times for multiple parallelism

Example: Adding to a rule

In this example actions are added to the rule. Blocks can be defined and held in domains or variables so these can be to reference them. We show here some blocks being created and then being given to the rule. NB Rules are not (yet) themselves thread safe so any one instance of a rule can only be used at any one time. Do not give the rame rule to different rule sets.

Here we can see that two threads will be started when the rule is run. One thread will move the cart forwards, left, right and then stop it. The other thread will, independently, make the cart go fast, then slow, then fast again.

   1 checkValue := [ myValue = 23 ].
   2 root/actions at: "forwards" put: [ cart forwards ]
   3 myrule := rule condition: checkValue.
   4 myrule actions: #( root/actions/forwards [cart left] [cart right] ).
   5 myrule action: [ cart stop ]; parallel; action: [ cart fast. cart slow. cart fast ].

The following examples are the same. The rule runs blocks b1, b3 and b4 in parallel. b2 is run when b1 finishes. b5 is run when b4 finishes. The rule finishes when all parallel threads have finished.

   1 myrule action: b1; action: b2; parallel; action: b3; parallel; action: b4; action: b5.
   2 // The following is the same as the preceeeding line
   3 myrule actions: #( b1 b2 ); parallel; action: b3; parallel; actions: #( b4 b5 ).
   4 // The following is similar but not identical
   5 myrule action: [ b1 value. b2 value ]; parallel; action: b3; parallel; action: [ b4 value. b5 value ].

Stopping Rules

Rules can be stopped using the stop command. This is normally done by the system not the user. However the user should be aware how the rule manages stop commands. If a stop is received, sends a stop to the currently running action(s) and then waits for them to terminate. Once they have finished the rule returns control to the requester. Blocks are atomic as far as PonderTalk is concerned and cannot be interrupted (and therefore don't receive the stop). This should be taken into account when designing the rule's actions. In the above example, if either of the first two rules are used then a stop could cause the rule to terminate without having either run b2 or b5 or even either. In the third example, since blocks are atomic, if the rule receives a stop command then the rule will wait until all three threads have run to completion i.e. even if b2 hasn't started it will still be run when b1 finishes before the rule returns control.

Rule Sets

Rule Sets are the heart of TR programming. A rule set is given an ordered set of rules which it then manages. The rule set, when started, asks each rule in turn if it can be run, the first rule to respond that it can, gets run. The rule set then proceeds from the top evaluating the rules over and over again. Each time the rules are evaluated, the top-most rule is run. If the top-most rule is the currently running rule (and it is still running) then that rule is simply left alone. If, however, a rule with a higher priority (higher in the ordered list of rules) than the currently running rule is ready to run then the currently running rule is told to stop, when it has stopped, the higher priority rule is run.

A rule set in PonderTalk is created using one of the following messages:




Creates a new TrRuleSet

create: aName

Creates a new TrRuleSet with the name aName (used for trace messages)

Example: Creating a new rule set

Here we can see the TR rule set factory being imported and then a new rule set Managed Object being created:

   1 ruleset := root load: "TrRuleSet".
   2 myruleset := ruleset create.

Once the rule set has been created it can have rules added to its ordered sequence of rules. Rules can also explicitly be added at a certain position. Once everythig is set up the rule set can be told to start whilst giving it the percepts hash:



add: aRule

Adds aRule to the end of the ordered rule set.

at: anIndex put: aRule

Adds aRule at position anIndex to the rule set. Displaced rules get pushed down by one

run: thePercepts

runs the ruleset using the percepts found in thePercepts. The rule set is run in a separate thread so that it can be given new events or maybe stopped. This call immediately returns leaving the ruleset running.

Starting and Stopping TrPonder

It can sometimes be difficult to see what is happening in a TR program, especially if there are multiple threads running. The TrPonder system has a trace facility that can be turned on and off to help keep track of the rules being run. Debugging is turned



It can sometimes be difficult to see what is happening in a TR program, especially if there are multiple threads running. The TrPonder system has a trace facility that can be turned on and off to help keep track of the rules being run. Debugging is turned on with the TrRuleSet trace command:



trace: aBoolean

Sets all TR tracing on for all rulesets and rules if aBoolean is true. Tracing is set to false initially

Debugging is turned on and off for the whole TrPonder system running within an SMC. That means that all rule sets and rules will start generating tracing information to the Java console until tracing is turned off again.

   1 // Turn tracing on
   2 myruleset trace: true.
   3 // Turn tracing off
   4 myruleset trace: false.

Rule Set Behaviour Modifications

The TR program can behave in different ways depending on the way it is configured:

Constant Evaluation

In the strict sense of T-R programs, the Rule Set constantly reevaluates its rules' conditions. If a rule with a higher priority (earlier on in the ordered set) than the currently running rule becomes available to run then the currently running rule is stopped and the new rule is run.

No Interrupts

The Rule Set will not try to stop a currently running rule even if there is a higher priority ready available to run.

Run Once

The Rule Set can to told to run one rule and then terminate. This implies No Interrupts.

Run and Wait

The Rule Set can be told to run one rule and then wait for a new event to occur before re-evaluating all the rules' conditions.




Tells the receiver to reevaluate the rule conditions and to choose a (possibly) new rule to run

runOnce: aBoolean

Tells the rule set to run one rule and then terminate if aBoolean is true. The initial value is false


Tells the rule set to stop executing rules. If a rule is running then that rule is told to stop. This action returns after the currently running rule (if any) has been stopped

valueVars: thePercepts

This operation has this name to make it look the same as running a block with a variable hash

waitForEvent: aBoolean

Tells the rule set to wait for an event before running through the rule list again if aBoolean is true. Use of this option can remove unnecessary scans of the rule conditions. The initial value is false.

waitForRule: aBoolean

Tells the rule set to wait for completion of a rule rather than interrupting it as necessary if aBoolean is true. The initial value is false



The percepts are in reality global variables for one or more TR rule sets. The percepts managed object is derived from a PonderTalk Hash managed object which is a basic managed object within Ponder2. As such it needs to be created in the following way which ensures that the new object looks like a hash to other managed objects (see the source code for more details):

   1 pfactory := root load: "Percepts".
   2 percepts := pfactory create asHash.

The percepts object can now be used as a hash in the normal way, it can also be told to notify one or more rule sets should any values be written to it. The percepts object is given to a rue set when the rule set is run. The rule set hands it to each rule that is run, the rule hands it to each action. Blocks see the percepts values a global variables so block arguments are not required.


Percepts contain the global knowledge that the TR rule set shares with all the rules. The rule set is informed whenever the percepts change, depending upon the options used, this could trigger a new evaluation of the rules and therefore possibly a new rule to be run. The percepts in TrPonder is actually a PonderTalk Hash with some extra commands to enable it to communicate with the rule set.


PonderTalk blocks can take arguments from a Ponder2 hash managed object. They can also merge the values from a hash into their environment variables before executing the block's code. This allows values in a hash to be used as plain variables without declaring them as arguments. Thia can make a program look more complicated because it is not always clear where values are coming from, however it help in the case of T-R programs because many examples assume the existance of global variables which this construct allows us to do.

   1 percepts at: "size" put: 5.
   2 rule condition: [ size == 5 ] ...
   4 Otherwise we would need
   5 rule condition: [ :size | size == 5 ] ...
   6 The rule runs this as:
   7 ruleCondition valueVars: percepts



The examples above have all show actions as blocks. In addition to blocks, actions may also be rule sets or other rules.

Code Blocks

A code block is a non interruptible sequence of operations. If a long running action is to be made interruptible then it should be split into a sequence of actions then after one code block as finished the rule can be terminated before the next action is started. Conversely if there are several actions that should be atomic then they should be grouped into a single code block.

Rule Sets

Other Rules

If another rule is given as an action then it is simply told to execute its actions when the time comes. It's condition is ignored. This is useful if an action is to perform operations in parallel, then it can be described as a rule.


There are a few problems inherent in the way that PonderTalk's blocks work. A block is a closure, that is it contains a copy of its environment at the time the block is created and so it is difficult to maintain common information between blocks. This is where the usefulness of the percepts comes in. The copy of the environment is a shallow copy which means that only the values of the top level environment variables are copied not the objects that they refer to. Thus if a variable refers to a domain, then then new copy of the variable will refer to the same domain, in this way shared values can be facilitated.

   1 // Create a new hash
   2 var := #() asHash.
   3 var at: "size" put: 10.
   5 b1 := [ ... var at: "size" put: 12. ... var := 7. ..].
   6 b2 := [ ... root print: (var at: "size"). ... ].
   8 b2 value. // prints 10
   9 b1 value.
  10 b2 value. // prints 12
  12 b1 value. // error number does not understand "at:put:"
  13 b2 value. // prints 12
  15 var := 4.
  16 b2 value. // prints 12

After the blocks are created, the value of var is copied into each block but they still both refer to the same PonderTalk Hash managed object.

Download TrPonder

The TrPonder zip file can be downloaded here. It should be unzipped inside your Ponder2 installation. It will create some files in src/net/ponder2 and some files in src/resource. To run the example use the build.xml file you already have in the Ponder2 installation:

ant run -boot trmaze.p2