Revision 23 as of 2009-09-22 15:33:23

Clear message

Teleo Reactive Ponder2

This page is a work in progress


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.






unloaded & at depot



loaded & at depot



loaded & facing depot

go forwards



turn left


next to bin

load bin


facing bin

go forwards


turn left

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.

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 because the lower numbered rules take precedence over the higher numbered ones. So, if rule 7 was being continuously executed then as soon as the truck were facing the bin, rule 6 would take over and the truck would move forwards.

Note that if someone moved the bin while the truck was approaching it (rule 6) then rule 6's condition woluld no longer be true and rule 7 would be executed again until the truck were facing the bin again.

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 run b2 or b5 or even both. In the third example, if the rule receives a stop command then the rule will wait until all three threads have run to completion because blocks are atomic. I.e. even if b2 hasn't started it will still be run when b1 finishes before the action finishes.

Rule Sets

Rule Sets are the heart of T-R 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

Starting and Stopping TrPonder

A rule set is started by sending it a run command along with the percepts that the rule set and all its rules will be using. The start is asynchronous, a new thread is started for the rule set. The stop is synchronous i.e. the stop does not return until the rule set has stopped its currently running rule. A rule set can explicitly be told to reevaluate its rules by sending it an explicit event (this is how the percepts tells the rule set to reevaluate the rules' conditions):



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.


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

Example: Controlling a rule set

Here we can see the TR rule set being started, later being told to reevaluate its rules and then still later stopped:

   1 myruleset run: mypercepts.
   2 // Wait for 10 seconds before telling the rule set to reevaluate its rules.
   3 root sleep: 10.
   4 myruleset event.
   5 // Wait for 20 seconds before stopping the rule set
   6 root sleep: 20.
   7 myruleset stop.

Example: Controlling multiple rule sets

Multiple rule sets can be given events

   1 mypercepts tell: myruleset1; tell: myruleset2.
   2 myruleset1 run: mypercepts.
   3 myruleset2 run: mypercepts.
   4 // Wait for 10 seconds before telling all the rule set to reevaluate their rules.
   5 root sleep: 10.
   6 mypercepts event.
   7 // This line will also cause the rule sets to reevaluate their rules
   8 percepts at: "myvar" put: "myvalue".


It can sometimes be difficult to see what is happening in a T-R 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

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. This is not very efficient in a multisking system so various options have been created to manage the rule sets efficently:

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.

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.




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

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



Percepts contain the global knowledge that the T-R rule set shares with all the rules. The rule set is informed whenever the percepts change and, 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 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 (internally, basic managed objects are treated differently from normal 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.



tell: aRuleSet

Tells the percepts that it must inform the given rule set whenever a value changes or an event is raised. This command may be used many times to inform many rule sets


Tells the percepts to inform the rule set(s), given by the tell command, that an event has occured

at: aName put: aValue

Store a value and raise an event for the rule set(s)


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.

   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 shown actions as blocks. In addition to blocks, actions may also be rule sets or other rules. All three types are described more fully here.

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