Given the case study of a wall-following behavior, the module design, its implementation, an interface with the environment, and an empty skeleton, the application phase is surprisingly straightforward!
The application process is iterative. We implement a prototype that we believe will work and try it in the game. If it fails, the problem is identified (that is, visually or with debugging tools) and fixed for the next iteration.
First, a symbol needs to indicate whether the AI is currently following the wall (see Table 12.1). This is defined as false initially, but it's true by default because we're optimistic; the animat should follow the wall most of the time!
We also need two symbols for sensing obstacles: one ahead and one on the side. Symbols corresponding to sensors need no default values because they are updated continuously; default values are also intended to simplify the actions, not the sensors. Then, we need three actions symbols: one to turn toward the wall, one to turn away from it, and, of course, we need the ability to move forward. In fact, that's done by default!
Defining the rules for the system is just a matter of expressing the case study as a set of rules. Consider the rules of Listing 12.3.
Until the animat has found a wall, it keeps looking for one by setting the symbol for following to false, using the default forward move. If there is a front wall, the animat turns away and overrides the default forward move to slow down. If there is no side wall, the animat stops and turns toward the previous location of the wall. Implicitly, there is a fourth rule that orders a move forward in all other cases (done by default).
Listing 12.3 The Rules Applied on the Working Memory to Define the Wall-Following Behavior (The latter rules can assume that the conditions of the previous rules are false.)
IF NOT following AND NOT frontWall AND NOT sidewall THEN following = false IF frontWall THEN turnAway = true AND moveForwards = false IF NOT sideWall THEN turnTowards = true AND moveForwards = false
From the description, we realize the importance of default symbols. The rulebase would be much more complex without them; a) there would be a fourth rule, b) each rule body to set the unused effectors to false, and c) all but the first rule would have to set the following symbol to true.
The priorities in the rules enable us to process the "looking for wall" behavior first, so the "following-wall" behavior (rules 2 and 3) can assume the wall has been found. The multiple actions in the rule body prevent rule duplication.
Sensors and Effectors
Both the sensors and effectors are very similar to the ones used for obstacle avoidance. They are coded as member methods in the Brain class, and passed to the RBS as function pointers—but with a pointer to the class. This implies that we can legally access member variables from the Brain in the callback.
The sensors are placed at 0 degrees (in front) and 45 degrees (to the side), with the same length only of a few steps. A value of true is returned if an obstacle is detected within that range, and false otherwise.
The effectors were originally implemented as direct calls to the Move() and Turn() interface functions. However, this produces very jerky movement, so instead we can use a smoothed version of the RBS output as actions (a moving average is ideal). This was particularly important for the movement, less so for turning.