qulog/examples/towers/towerBuilder.qlg
contains a very simple tower builder program - a direct translation of the example given by Nilsson in [3].
We give this simple example for three reasons: firstly to pay homage to Nilsson and his elegant invention of Teleor-Reactive Programming; secondly to give an example of a recursive TeleoR procedure; and thirdly as a precursor to the introduction of multiple tasks, sharing and alternating the use of one or more physical resources.
The tower builder problem introduced by Nilsson consists of a table containing a collection of numbered blocks and a robot arm that can pick up blocks and build a tower of numbered blocks in a given order. The environment might cover needed blocks, shuffle (partly built) towers or even help build the tower or uncover needed blocks. There is a simplifying assumption that there is enough room on the table for every block to be placed directly on the table in the area that can be reached by the robot arm.
The relatively high level robotic actions and percepts that we can assume are:
The top-level TeleoR procedure does not directly invoke any robotic actions. It calls subsidiary TeleoR procedures, and itself. It is recursive.
The unpile procedure clears blocks above a block of interest.
The basic move procedures are given below.
Note that makeTower
is a recursive procedure and that unpile
and move_to_table
are mutually recursive.
To reinforce the differences between TeleoR procedures and procedures of traditional procedural code we focus on two aspects of this program.
First we consider recursion. In traditional programming a recursive call is made and eventually the child call will return and the parent will continue and eventually return itself. For TeleoR procedures the parent will pick a rule that calls the child TeleoR procedure. This is similar to traditional procedure calls. The important difference is that the child procedure never returns. Each procedure is continually monitoring the state and eventually the parent will pick a different rule and the child procedure will no longer be active.
The second procedure we consider is unpile
. On superficial reading you might think that the second rule will move blocks to the table. This is only partially true. Consider the case where Block
is covered by exactly one block OnBlock
. In this case the third rule of the procedure call move_to_table(OnBlock)
will fire causing the arm to pick up the block. As soon as the block is picked up the first rule of unpile
fires and so it does not complete the move to the table. For this reason it might have been better to rename unpile
to something like remove_blocks_above
.
You will notice that both move_to_table
and move_to_block
have a rule to deal will holding an unwanted block. This is partially to deal with this problem but also because the environment might rearrange the blocks in a partial tower and the block being held intended for that partial tower will now need to be put on the table.