Production Systems

A production system describes a set of condition-action rules to be applied in given situations. At the simplest level this could just be a set of if-then commands, but usually implies a more complicated arrangement such as that employed in Expert System shells such as CLIPS which do pattern-matching and unification of pre-conditions written in a predicate logic-like language over a database of facts. CLIPS is a forward-chaining production system, whereas Prolog is a form of backward-chaining production system (i.e., it tries to reason backwards from a goal to find facts that support it, rather than reasoning from known facts to find their consequences).

Here is a simple example of a production system for a game AI opponent [L1 ]:

 set health 100 ;# Current health
 set range  100 ;# Maximum weapons range
 set nearest 50 ;# Distance of nearest enemy
 set target ... ;# Position of target
 set base   ... ;# Position of base
 # etc ...
 
 plan Attack {
     when {$health < 40} do { goal Retreat }
     when {$nearest < $range} do { fire_at $target }
     otherwise { move_to $target }
 } 
 
 plan Retreat {
     when {$health > 60} do { goal Attack }
     when {![at $base]} do { move_to $base }
     otherwise { repair }
 }
 
 goal Attack

This uses a form of Reactive Planning based on Teleo-Reactive programs [L2 ]. The code that implements this simple production system is:

 proc goal {name} {
     global GOAL
     set GOAL $name
 }
 proc plan {goal body} {
     global GOAL ACTIONS ELSE
     set GOAL $goal
     set ACTIONS($GOAL) [list]
     set ELSE($GOAL) ""
     uplevel #0 $args
 }
 proc when {cond _do_ action} {
     global ACTIONS GOAL
     lappend ACTIONS($GOAL) $cond $action
 }
 proc otherwise {action} {
     global ELSE GOAL
     set ELSE($GOAL) $action
 }
 # Typically, "run" would be incorporated into your game main-loop
 proc run {} {
     global ACTIONS GOAL ELSE
     while 1 {
         set fired 0
         foreach {cond action} $ACTIONS($GOAL) {
             if {[uplevel #0 [list expr $cond]]} {
                  uplevel #0 $action
                  set fired 1
                  break; # Only first matching action fires
             }
         }
         if {!$fired} {
             uplevel #0 $ELSE($GOAL)
         }
     }
 }