home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 2 / crawlyvol2.bin / program / compiler / clips / clip_doc / clips9.man < prev    next >
Encoding:
Text File  |  1993-09-01  |  26.9 KB  |  728 lines

  1.          Chapter 9   Backwards And Forwards
  2.  
  3.  
  4.  
  5.  
  6. Up to this point, you've been learning the basic syntax of CLIPS.  In this 
  7. chapter you will learn more advanced techniques of overall program 
  8.  design.
  9.  
  10.  
  11. Forward And Backward Chaining
  12.  
  13.  
  14. There are two fundamental control strategies that a program can follow.  One 
  15. type is called forward chaining and the other is called backward 
  16.  chaining.  The control strategies should not be confused with controlling a 
  17. loop.  Instead, the control strategy of a program refers to the 
  18.  most basic design philosophy of the program.
  19.    The influence of these control strategies is so important that languages are 
  20. usually designed to optimize one or the other.  For example, 
  21.  CLIPS is designed as a forward chaining language while PROLOG is backward 
  22. chaining.  With appropriate programming, a forward chaining language 
  23.  can be used for backward chaining and vice versa.  So far the programs you've 
  24. been writing have been forward chaining.
  25.    Forward chaining reasons from known facts to the resulting conclusions.  In 
  26. contrast, backward chaining reasons from known conclusions to 
  27.  the facts which caused them.  The determination of which control strategy to 
  28. use depends on the application.
  29.    Consider the problem of writing an expert system program to diagnose 
  30. illness.  If a patient has certain symptoms, the program should try to 
  31.  determine the nature of the illness which caused the illness.  In this case, a 
  32. backward chaining strategy is desired.  In contrast, if a patient 
  33.  is known to have a certain disease, an expert system program could be written 
  34. for a prognosis of the disease.  That is, the program could predict 
  35.  the course of the disease and suggest a therapy.
  36.    Another consideration in the choice of forward or backward chaining 
  37. strategies is the extent of the search space.  The search space is the 
  38.  set of possible states a program must search to find an acceptable solution.  
  39. The term state means the collection of information which defines 
  40.  a configuration of the system.
  41.    For many problems, a brute-force approach by searching all possible states 
  42. is impractical because there are too many states.  Instead, the 
  43.  production rules contain knowledge about which paths to follow in the search 
  44. space that are likely to lead to the desired goal.
  45.    Sometimes it is necessary for the program to backtrack to a state that was 
  46. searched earlier to take an alternate path from that state because 
  47.  the first path chosen did not lead to an acceptable state.  An example is 
  48. searching through a maze and coming to a dead end.  You would then 
  49.  go back along your path and take a different path.
  50.    The knowledge encoded in rules may be heuristic in nature.  The term 
  51. heuristic means a method that may aid in the solution of a problem.  
  52.  A heuristic is not guaranteed to aid in the solution.  Only an algorithm is 
  53. guaranteed to determine a solution.  Very often the knowledge of 
  54.  experts is heuristic.  The person writing an expert system program, the 
  55. knowledge engineer, must interview the expert and implement this knowledge 
  56.  in an expert system program.
  57.    In a good forward chaining application, there are many possible final states 
  58. and few initial states.  Information is known about the initial 
  59.  states and the problem is to find an acceptable path from the initial state to 
  60. the final state.  Forward chaining reasons from given facts to 
  61.  the conclusions which follow from them.  The production rules in CLIPS are 
  62. written in a forward chaining syntax.  The known facts are the conditional 
  63.  elements which imply the conclusions which are the actions of the rule.
  64.    In a good backward chaining case, there are relatively few final states and 
  65. many initial states.  The goal in backward chaining is to reason 
  66.  backward from conclusions to the facts which caused them.
  67.  
  68.  
  69. A Forward Engine
  70.  
  71.  
  72. Let's first look at an example of an application written in the standard 
  73. forward chaining method of CLIPS and then see it written in a backward 
  74.  chaining format.
  75.    The following program simulates the action of a one-cylinder, four-stroke 
  76. gasoline engine.  Figure 9-1 illustrates the basic operation of 
  77.  the engine.  The engine works in the following way.
  78.  
  79.    1st Stroke-Intake Stroke: While the intake valve is open and the exhaust 
  80. valve is closed, the descending piston draws a fresh air and gasoline 
  81.  fuel mixture into the cylinder.
  82.  
  83.    2nd Stroke-Compression Stroke: The intake valve closes and the ascending 
  84. piston compresses the fuel mixture.
  85.  
  86.    Ignition: The compressed mixture is ignited by an electrical spark from the 
  87. spark plug.
  88.  
  89.    3rd Stroke-Power Stroke: The pressure of the burned gases from the mixture 
  90. forces the piston to descend.
  91.  
  92.    4th Stroke-Exhaust Stroke: The exhaust valve is opened and the ascending 
  93. piston expels the burned gases from the cylinder.
  94.  
  95.    As the piston ascends and descends, it rotates the crankshaft which supplies 
  96. mechanical power to the wheels.  The crankshaft is a rod going 
  97.  into the paper and symbolized by the black dot.
  98.    Enter the following program which simulates the operation of the engine.
  99.  
  100. ;***** forward engine program *****
  101. ;forward chaining model of a one-cylinder, four-stroke
  102. ;gasoline engine
  103.  
  104. (defrule make-initial-fact
  105.    (initial-fact)
  106. =>
  107.    (assert (engine intake-valve open
  108.                    exhaust-valve closed
  109.                    gas good
  110.                    air good 
  111.                    sparkplug nofire
  112.                    piston descending)))
  113.  
  114. (defrule intake-stroke
  115.    ?engine <- (engine intake-valve open
  116.                       exhaust-valve closed
  117.                       gas good
  118.                       air good
  119.                       sparkplug nofire
  120.                       piston descending)
  121. =>
  122.    (printout crlf "1st stroke" crlf)
  123.    (retract ?engine)
  124.    (assert (engine intake-valve closed
  125.                    exhaust-valve closed
  126.                    gas good
  127.                    air good
  128.                    sparkplug nofire
  129.                    piston ascending)))
  130.  
  131. (defrule compression-stroke
  132.    ?engine <- (engine intake-valve closed
  133.                       exhaust-valve closed
  134.                       gas good
  135.                       air good
  136.                       sparkplug nofire
  137.                       piston ascending)
  138. =>
  139.    (printout "2nd stroke" crlf)
  140.    (retract ?engine)
  141.    (assert (engine intake-valve closed
  142.      exhaust-valve closed
  143.    gas good
  144.    air good
  145.      sparkplug fire
  146.     piston ascending)))
  147.  
  148. (defrule ignition
  149.    ?engine <- (engine intake-valve closed
  150.                   exhaust-valve closed
  151.       gas good
  152.       air good
  153.        sparkplug fire
  154.       piston ascending)
  155. =>
  156.   (printout "ignition" crlf)
  157.   (retract ?engine)
  158.   (assert (engine intake-valve closed
  159.     exhaust-valve closed
  160.   gas burned
  161.   air burned
  162.    sparkplug nofire
  163.   piston descending)))
  164.  
  165. (defrule power-stroke
  166.    ?engine <- (engine intake-valve closed
  167.         exhaust-valve closed
  168.       gas burned
  169.       air burned
  170.       sparkplug nofire
  171.         piston descending)
  172. =>
  173.    (printout "3rd stroke" crlf)
  174.    (retract ?engine)
  175.    (assert (engine intake-valve closed
  176.     exhaust-valve open
  177.       gas burned
  178.    air burned
  179.       sparkplug nofire
  180.    piston ascending)))
  181.  
  182. (defrule exhaust-stroke
  183.    ?engine <- (engine intake-valve closed
  184.           exhaust-valve open
  185.       gas burned
  186.          air burned
  187.       sparkplug nofire
  188.       piston ascending)
  189. =>
  190.   (printout "4th stroke" crlf)
  191.   (retract ?engine)
  192.   (assert (engine intake-valve open
  193.       exhaust-valve closed
  194.   gas good
  195.   air good
  196.   sparkplug nofire
  197.   piston descending)))
  198.  
  199.    To run the engine, (reset) and issue a (run 21).  The partial output of the 
  200. program should look like the following for one cycle and the start 
  201.  of another.
  202.  
  203. 1st stroke
  204. 2nd stroke
  205. ignition
  206. 3rd stroke
  207. 4th stroke
  208.  
  209. 1st stroke
  210. 2nd stroke
  211.  
  212.  
  213. A Backward Example
  214.  
  215.  
  216. The basic algorithm of the backward chaining method can be stated recursively 
  217. as follows.
  218.    
  219. Solve the goal: If the goal has been solved
  220.                   then return
  221.                   else make an appropriate subgoal
  222.                        Solve the goal
  223.  
  224.    In backward chaining, the program tries to satisfy a goal.  If the goal 
  225. cannot be satisfied directly, the program creates one or more new 
  226.  goals, called subgoals.  The goal is said to be split up into simpler subgoals 
  227. for solution.  The goal which is split is called the parent goal 
  228.  and the subgoals are also called child goals.  The subgoals are designed so 
  229. that their solution should enable the parent goal to be satisfied. 
  230.   
  231.    A large complex problem may have many subgoals created.  Of course, not all 
  232. the subgoals will be active at once.  As appropriate subgoals 
  233.  are satisfied, the parent goal is satisfied.  When appropriate parent goals 
  234. are satisfied, their parent is satisfied.  If a subgoal cannot be 
  235.  solved or leads to an undesirable consequence, that information must be 
  236. relayed back to the parent goal so that some other action by the parent 
  237.  is taken.
  238.    For the example of the four-stroke engine, the initial goal is to produce an 
  239. engine cycle.  That is, the first goal is to have the engine 
  240.  complete a complete cycle of four strokes.  To complete a cycle means that the 
  241. exhaust-stroke must be done.  To complete the exhaust-stroke 
  242.  means that a power-stroke must be done.  To complete a power-stroke means that 
  243. the ignition must be done.  To complete ignition requires that 
  244.  a compression-stroke must be done.  To complete a compression-stroke requires 
  245. that an intake-stroke must be done.
  246.    As you can see by this analysis, the backward chaining method is a very 
  247. appropriate name.  Each goal generates a subgoal in a backward manner 
  248.  from the desired initial goal of an engine cycle to generating an intake goal. 
  249.  The goals act as if they linked by a chain.  In fact, the chain 
  250.  can be considered as the chain of logical inferences which specified the 
  251. creation of subgoals to satisfy the parent goal.
  252.    When each subgoal is created, the program checks to see if it has enough 
  253. information to solve the subgoal.  In the case of our engine, subgoals 
  254.  are created until there is a subgoal for an intake stroke.  The initial 
  255. conditions of the engine are such that it is set up for an intake stroke. 
  256.   That is, the make-initial-fact rule will set the intake-valve to open, the 
  257. exhaust-valve to closed, the air good, the gas good, the sparkplug 
  258.  to nofire and the piston to descending.  The intake-stroke rule will be 
  259. designed to fire if there is a goal whose parent is split-compression-stroke 
  260.  and whose name is intake-stroke and there is a engine element with the above 
  261. initial configuration.
  262.    The following program shows a backward chaining version of the engine.  The 
  263. four rules which split goals operate first.  Then the engine fires 
  264.  the four strokes.  Finally, the continue-cycle rule initial-facts another 
  265. engine cycle.  As before, you'll need to issue a (run 21) command 
  266.  to stop after 21 cycles.  Enter and run this program.  You should also issue 
  267. (watch facts) and (watch activations) commands to watch facts and 
  268.  activations.
  269.  
  270. ;***** backward engine program *****
  271. ;backward chaining model of a one-cylinder, four-stroke
  272. ;gasoline engine
  273.  
  274. (defrule make-initial-fact
  275.    (initial-fact)
  276. =>
  277.    (assert (engine intake-valve open
  278.                    exhaust-valve closed
  279.                    gas good
  280.                    air good 
  281.                    sparkplug nofire
  282.                    piston descending))
  283.    (assert (goal parent complete-cycle
  284.                  name exhaust-stroke
  285.                  action nil)))
  286.  
  287. (defrule split-exhaust-stroke
  288.    (goal parent complete-cycle
  289.          name exhaust-stroke
  290.          action nil)
  291. =>
  292.    (assert (goal parent split-exhaust-stroke
  293.                  name power-stroke
  294.                  action nil)))
  295. (defrule split-power-stroke
  296.    (goal parent split-exhaust-stroke
  297.          name power-stroke
  298.          action nil)
  299. =>
  300.    (assert (goal parent split-power-stroke
  301.                  name ignition
  302.                  action nil)))
  303.  
  304. (defrule split-ignition
  305.    (goal parent split-power-stroke
  306.          name ignition
  307.          action nil)
  308. =>
  309.    (assert (goal parent split-ignition
  310.                  name compression-stroke
  311.                  action nil)))
  312.  
  313. (defrule split-compression-stroke
  314.    (goal parent split-ignition
  315.          name compression-stroke
  316.          action nil)
  317. =>
  318.    (assert (goal parent split-compression-stroke
  319.                  name intake-stroke
  320.                  action nil)))
  321.  
  322. (defrule intake-stroke
  323.    ?goal <- (goal parent split-compression-stroke
  324.                   name intake-stroke
  325.                   action nil)
  326.    ?engine <- (engine intake-valve open
  327.                       exhaust-valve closed
  328.                       gas good
  329.                       air good
  330.                       sparkplug nofire
  331.                       piston descending)
  332. =>
  333.    (printout crlf "1st stroke" crlf)
  334.    (retract ?engine)
  335.    (assert (engine intake-valve closed
  336.                    exhaust-valve closed
  337.                    gas good
  338.                    air good
  339.                    sparkplug nofire
  340.                    piston ascending))
  341.    (retract ?goal)
  342.    (assert (goal parent split-compression-stroke
  343.                  name intake-stroke
  344.                  action delete)))
  345.  
  346. (defrule compression-stroke
  347.    ?goal <- (goal parent split-ignition
  348.                   name compression-stroke
  349.                   action nil)
  350.    ?engine <- (engine intake-valve closed
  351.                       exhaust-valve closed
  352.                       gas good
  353.                       air good
  354.                       sparkplug nofire
  355.                       piston ascending)
  356. =>
  357.    (printout "2nd stroke" crlf)
  358.    (retract ?engine)
  359.    (assert (engine intake-valve closed
  360.                    exhaust-valve closed
  361.    gas good
  362.    air good
  363.      sparkplug fire
  364.     piston ascending))
  365.    (retract ?goal)
  366.    (assert (goal parent split-ignition
  367.                  name compression-stroke
  368.                  action delete)))
  369.  
  370. (defrule ignition
  371.    ?goal <- (goal parent split-power-stroke
  372.                   name ignition
  373.                   action nil)
  374.    ?engine <- (engine intake-valve closed
  375.                   exhaust-valve closed
  376.       gas good
  377.       air good
  378.        sparkplug fire
  379.       piston ascending)
  380. =>
  381.    (printout "ignition" crlf)
  382.    (retract ?engine)
  383.    (assert (engine intake-valve closed
  384.                    exhaust-valve closed
  385.    gas burned
  386.    air burned
  387.     sparkplug nofire
  388.    piston descending))
  389.    (retract ?goal)
  390.    (assert (goal parent split-power-stroke
  391.                  name ignition
  392.                  action delete)))
  393.  
  394. (defrule power-stroke
  395.    ?goal <- (goal parent split-exhaust-stroke
  396.                   name power-stroke
  397.                   action nil)
  398.    ?engine <- (engine intake-valve closed
  399.                exhaust-valve closed
  400.       gas burned
  401.       air burned
  402.       sparkplug nofire
  403.         piston descending)
  404. =>
  405.    (printout "3rd stroke" crlf)
  406.    (retract ?engine)
  407.    (assert (engine intake-valve closed
  408.                    exhaust-valve open
  409.       gas burned
  410.    air burned
  411.       sparkplug nofire
  412.    piston ascending))
  413.    (retract ?goal)
  414.    (assert (goal parent split-exhaust-stroke
  415.                  name power-stroke
  416.                  action delete)))
  417.  
  418. (defrule exhaust-stroke
  419.    ?goal <- (goal parent complete-cycle
  420.                   name exhaust-stroke
  421.                   action nil)
  422.    ?engine <- (engine intake-valve closed
  423.           exhaust-valve open
  424.       gas burned
  425.                       air burned
  426.                       sparkplug nofire
  427.                       piston ascending)
  428. =>
  429.    (printout "4th stroke" crlf)
  430.    (retract ?engine)
  431.    (assert (engine intake-valve open
  432.         exhaust-valve closed
  433.    gas good
  434.    air good
  435.    sparkplug nofire
  436.    piston descending))
  437.    (retract ?goal)
  438.    (assert (goal parent complete-cycle
  439.                  name exhaust-stroke
  440.                  action delete)))
  441.  
  442. (defrule continue-cycle
  443.    (engine $?)
  444.    (not (goal $?))
  445. =>
  446.    (printout crlf "***** Engine cycle complete *****")
  447.    (assert (goal parent complete-cycle
  448.                  name exhaust-stroke
  449.                  action nil)))
  450.  
  451. (defrule remove-satisfied-goal
  452.    ?goal <- (goal $? action delete)
  453. =>
  454.   (retract ?goal))
  455.  
  456.  
  457.    Strictly speaking, not all of the patterns need be written so explicitly for 
  458. the (engine) in all the strokes.  They are written out to just 
  459.  help you understand what's happening in the engine.  For example, in the 
  460. exhaust-stroke, the conditional element for ?engine could be written 
  461.  more correctly with wildcards as
  462.  
  463.    ?engine <- (engine intake-valve closed
  464.              exhaust-valve open
  465.         gas ?
  466.                         air ?
  467.                         sparkplug ?
  468.                         piston ascending)
  469.  
  470. because the exhaust-stroke does not depend on the state of the gas, air, or 
  471. sparkplug.  In fact, the conditional element could even be written 
  472.  more briefly as
  473.  
  474.    ?engine <- (engine intake-valve closed
  475.             exhaust-valve open
  476.         $?
  477.                         piston ascending)
  478.  
  479.    Specifying the engine patterns more explicitly does help reduce the work 
  480. that CLIPS does in pattern matching.  When wildcards are used, CLIPS 
  481.  does a lot more work because it must try all possible matches.  Even with 
  482. wildcards, the "?" is less work for CLIPS than "$?" and so it's better 
  483.  to use "?".
  484.    Give a (run 21) command and observe the speed at which the program executes. 
  485.  You'll see the same kind of output as in the forward chaining 
  486.  version.  However, notice that the backward chaining version runs slower.  
  487. Also, there is less output for a given number of run cycles because 
  488.  CLIPS is doing more work compared to the forward chaining version.  These 
  489. factors slow down the execution speed of the backward version.
  490.  
  491.  
  492. A Good Explanation
  493.  
  494.  
  495. In this particular example of the four-stroke engine, the backward chaining is 
  496. not the best choice of a design strategy.  Since the strokes of 
  497.  the engine follow each other in a sequential manner, the simulation problem is 
  498. inherently forward chaining and should best be done by a forward 
  499.  chaining program.
  500.    However, the backward chaining method is useful in providing an explanation 
  501. facility.  The term explanation facility means that a program 
  502.  can explain its reasoning as it fires rules.  That is, the program can explain 
  503. why it is trying to satisfy certain goals.  An explanation facility 
  504.  is a very important feature in both program development and also in real runs 
  505. by users.
  506.    As the number of rules and facts grow, it becomes harder for a programmer to 
  507. just look at the code and understand how a program works.  In 
  508.  contrast to programs written in sequential languages like Pascal, rule-based 
  509. programs execute in a parallel manner since all the rules have 
  510.  simultaneous access to the working memory.  The solution to this problem is to 
  511. use the program to debug itself.  By designing with parent and 
  512.  subgoals in mind, the program can tell its reasons to perform certain actions.
  513.    Let's add an explanation facility to the Backward Engine Program so that you 
  514. can see how it works. Modify the make-initial-fact rule as follows 
  515.  so that it will ask the user if an explanation is desired.
  516.  
  517. (defrule make-initial-fact
  518.    (initial-fact)
  519. =>
  520.    (assert (engine intake-valve open
  521.                    exhaust-valve closed
  522.                    gas good
  523.    air good
  524.    sparkplug nofire
  525.    piston descending))
  526.    (printout "Explain goals (yes or no) ?" crlf) ;* add this *
  527.    (assert (explain =(read)))                    ;* add this *
  528.    (assert (goal parent complete-cycle
  529.                  name exhaust-stroke
  530.  action nil)))
  531.  
  532.    Now add the following rule to explain the creation of subgoals.  Notice the 
  533. salience fact so that this rule will fire before a competing split 
  534.  type rule in the agenda.  After you run the program with this salience, try 
  535. running it with no salience and note the difference in output.
  536.  
  537. (defrule explain-subgoal
  538.    (declare (salience 10))
  539.    (explain yes)
  540.    (goal parent ?parent
  541.          name ?name
  542.  action nil)
  543. =>
  544.    (printout "Must create subgoal :" ?name crlf
  545.              "in order to satisfy parent :" ?parent crlf))
  546.  
  547.    Also, add this rule to explain the satisfaction of subgoals as the stroke 
  548. rules fire.
  549.  
  550. (defrule explain-remove-satisfied-goal
  551.    (explain yes)
  552.    ?goal <- (goal parent ?parent
  553.                   name ?name
  554.                   action delete)
  555. =>
  556.    (retract ?goal)
  557.    (printout "Subgoal: " ?name " was satisfied" crlf
  558.              "Now satisfying parent : " ?parent crlf))
  559.  
  560.    Finally, modify the remove-satisfied-goal rule by adding the (explain no) 
  561. condition element as follows.
  562.  
  563. (defrule remove-satisfied-goal
  564.    (explain no)
  565.    ?goal <- (goal $? action delete)
  566. =>
  567.    (retract ?goal))
  568.  
  569.    Now do a (reset) and (run 22).  Answer "yes" to the explain question as 
  570. shown and you'll see the following output of one complete engine cycle 
  571.  and start of another.
  572.  
  573. Explain goals (yes or no) ?
  574. yes
  575. Must create subgoal :exhaust-stroke
  576. in order to satisfy parent :complete-cycle
  577. Must create subgoal :power-stroke
  578. in order to satisfy parent :split-exhaust-stroke
  579. Must create subgoal :ignition
  580. in order to satisfy parent :split-power-stroke
  581. Must create subgoal :compression-stroke
  582. in order to satisfy parent :split-ignition
  583. Must create subgoal :intake-stroke
  584. in order to satisfy parent :split-compression-stroke
  585.  
  586. 1st stroke
  587. Subgoal: intake-stroke was satisfied
  588. Now satisfying parent : split-compression-stroke
  589. 2nd stroke
  590. Subgoal: compression-stroke was satisfied
  591. Now satisfying parent : split-ignition
  592. ignition
  593. Subgoal: ignition was satisfied
  594. Now satisfying parent : split-power-stroke
  595. 3rd stroke
  596. Subgoal: power-stroke was satisfied
  597. Now satisfying parent : split-exhaust-stroke
  598. 4th stroke
  599. Subgoal: exhaust-stroke was satisfied
  600. Now satisfying parent : complete-cycle
  601. ***** Engine cycle complete *****
  602.  
  603. Must create subgoal :exhaust-stroke
  604. in order to satisfy parent :complete-cycle
  605.  
  606.  
  607. What's The Trouble
  608.  
  609.  
  610. In addition to providing an explanation of goals, a program can also be used 
  611. for diagnostic tasks by adding rules that look for problems.  For 
  612.  example, suppose the compression-stroke rule is changed to give a bad 
  613. sparkplug action.  In a system which had inputs from engine sensors, the 
  614.  bad sparkplug information could come directly from a sensor and be accepted 
  615. into the rule.  You can simulate the engine sensor data by changing 
  616.  the sparkplug action in the rule as follows to give a bad sparkplug.
  617.  
  618. (defrule compression-stroke
  619.    ?goal<-(goal parent split-ignition
  620.                 name compression-stroke
  621.                 action nil)
  622.    ?engine<-(engine intake-valve closed
  623.                     exhaust-valve closed
  624.                     gas good
  625.                     air good
  626.                     sparkplug nofire
  627.                     piston ascending)
  628. =>
  629.    (printout "2nd stroke" crlf)
  630.    (retract ?engine)
  631.    (assert (engine intake-valve closed
  632.                    exhaust-valve closed
  633.    gas good
  634.    air good
  635.      sparkplug bad ; changed from fire to bad
  636.     piston ascending))
  637.    (retract ?goal)
  638.    (assert (goal parent split-ignition
  639.                  name compression-stroke
  640.                  action delete)))
  641.  
  642.    Now when the compression-stroke rule fires, it produces a working memory 
  643. element with a bad sparkplug.  To detect a bad sparkplug, let's add 
  644.  a diagnostic rule to look for a bad sparkplug.
  645.  
  646. (defrule bad-sparkplug
  647.    (goal parent ? 
  648.          name ignition
  649.          action ?)
  650.    ?engine <- (engine intake-valve closed
  651.                       exhaust-valve closed
  652.       gas good
  653.       air good
  654.         sparkplug bad
  655.                piston ascending)
  656. =>
  657.    (printout "????? No ignition ?????" crlf
  658.              "Type fire for ignition" crlf)
  659.    (retract ?engine)
  660.    (assert (engine intake-valve closed
  661.                    exhaust-valve closed
  662.    gas good
  663.    air good
  664.      sparkplug =(read)
  665.     piston ascending)))
  666.  
  667.    If the sparkplug is bad, the ignition rule won't fire.  But that doesn't 
  668. tell us why the ignition didn't fire.  The rule above, bad-sparkplug, 
  669.  will look for a bad sparkplug and tell us the reason.  Additional rules could 
  670. be added to diagnose other conditions such as bad gas or bad air. 
  671.   As more diagnostic rules are added, the program acts more and more like an 
  672. expert.  In fact, expert system programs are usually designed to 
  673.  grow in an incremental fashion like this.
  674.    In a conventional sequential program, we usually have a good idea of the 
  675. algorithm to be programmed.  However, in an expert system, we may 
  676.  not have a good algorithm because our knowledge is incomplete or heuristic.  
  677. The goal in expert system programming is basically rapid prototyping. 
  678.   That is, we want to produce a prototype program quickly. This consideration 
  679. is very important if the source of knowledge is a human expert. 
  680.   A knowledge engineer may spend a lot of time interviewing the expert and 
  681. trying to extract the knowledge needed for the program.  The expert 
  682.  is the best judge of the performance of the program and it is important to 
  683. provide rapid feedback to the expert so that the system can be improved.
  684. .PA
  685.                              Problem
  686.  
  687.  
  688.    Write a program to play tic-tac-toe for these criteria.
  689.    (a) The program should never lose.  It will either win or        draw.
  690.    (a) Display the board on the screen with unfilled squares               
  691. numbered in the following pattern, and ask the user for           
  692.      the number of the square that the user wishes to move to.               
  693. The user always goes first with an X.
  694.  
  695.                           1  |  2  |  3
  696.                         ----------------
  697.                           4  |  5  |  6
  698.                         ----------------
  699.                           7  |  8  |  9
  700.  
  701.    (b) After each move, the board should be redrawn and updated.
  702.    (c) The program should print out messages for a draw or win.
  703.    (d) If the user inputs "why" after the computer moves, the
  704.        computer should explain why it moved to that square in
  705.        terms of its goals and which squares it plans to win on.
  706.  
  707.    Print out three games showing computer wins and three draws.
  708.    In one game of each type, ask a "why" question after moves.
  709.  
  710.    Hint: The board can be considered to have values associated    with each 
  711. square that add up to a magic square of 15.
  712.  
  713.                           8  |  3  |  4
  714.                         ----------------
  715.                           1  |  5  |  9
  716.                         ----------------
  717.                           6  |  7  |  2
  718.  
  719.    The goal is to move pieces to obtain a sum of 15 so that you can win while 
  720. preventing your opponent from getting 15.
  721.  
  722. Optional: (1) The program should give the user a initial-facting        choice 
  723. of X or O and choice of moving first. (2) An error messageis printed 
  724.  if the user specifies a number out of the range 1-9 or already taken. (3) The 
  725. program should be able to predict a draw.
  726.  
  727.  
  728.