; This CLIPS program solves the following simple puzzle: You have ; two buckets that hold 3 gallons and 5 gallons of water. You can ; fill a bucket, empty a bucket, pour from one bucket to another until ; the first is empty, an pour from one bucket to another until the ; second is full. The object is to end up with a specified number of ; gallons in one of the buckets. After loading this file, use the ; (reset) command followed by the (run) command. You will be asked ; how many gallons you want to end up with. If a solution exists, ; it will be printed. ; The get-goal rule gets things started by asking the user for the ; number of gallons and asserting a goal. It is triggered by the ; (initial-fact), which is asserted by the (reset) command. A fact ; of the form (visited A B) represents that the state where the ; buckets contain A and B gallons respectively has been generated. ; Start with empty buckets by asserting (visited 0 0). (defrule get-goal (initial-fact) => (printout t "How many gallons do you want? ") (assert (goal =(read))) ; Read answer and use it as a goal. (assert (visited 0 0)) ; ) ; The "done: rule will fire when a state is generated that achieves ; the goal, that is, one of the buckets contains the specified number ; of gallons. The salience is set to 1 so that this goal will jump ; ahead of other rules that are already on the agenda and fire ; immediately. The "print-path" fact is used to control printing of ; the solution. It will trigger printing of moves back from (?a,?b) ; to (0,0). Moves are printed by the "solution-printer" rule, and ; the end of the print sequence is handled by the "solution-printed" ; rule. (It would be harder to print the moves in forward order.) (defrule done (declare (salience 1)) (goal ?x) (or (visited ?a&:(= ?a ?x) ?b) (visited ?a ?b&:(= ?b ?x)) ) => (printout t crlf "Solution found. Moves in reverse order:" crlf crlf) (assert (print-path ?a ?b)) ; arrange for first move to be printed ) (defrule solution-printer (declare (salience 1)) ?prev <- (print-path ?a ?b) (predecessor ?a ?b ?c ?d) => (printout t " Moved from " ?c "," ?d " to " ?a "," ?b crlf) (retract ?prev) (assert (print-path ?c ?d)) ; arrange for next move to be printed ) (defrule solution-printed (declare (salience 1)) ?prev <- (print-path ?a ?b) (not (predecessor ?a ?b ? ?)) ; The state (?a,?b) has no predecessor. => (printout t crlf "And that was where we started." crlf crlf) (retract ?prev) (halt) ) ; The visit function is used in the rules that follows to represent ; the action of moving from one state, (?froma,?fromb), to another ; state, (?toa,?tob), by performing a legal action of the puzzle. (deffunction visit (?froma ?fromb ?toa ?tob) (assert (visited ?toa ?tob)) (assert (predecessor ?toa ?tob ?froma ?fromb)) (printout t "Try advance from " ?froma "," ?fromb " to " ?toa "," ?tob crlf) ) ; Each of the remaining rules represents one of the legal moves ; of the puzzle (defrule empty1 ; empty bucket number 1 (visited ?a ?b) (test (> ?a 0)) (not (visited 0 ?b)) => (visit ?a ?b 0 ?b) ) (defrule empty2 ; empty bucket number 2 (visited ?a ?b) (test (> ?b 0)) (not (visited ?a 0)) => (visit ?a ?b ?a 0) ) (defrule fill1 ; fill bucket number 1 (visited ?a ?b) (test (< ?a 3)) (not (visited 3 ?b)) => (visit ?a ?b 3 ?b) ) (defrule fill2 ; fill bucket number 2 (visited ?a ?b&:(< ?b 5)) (test (< ?b 5)) (not (visited ?a 5)) => (visit ?a ?b ?a 5) ) (defrule pour12-empty1 ; pour from bucket 1 to bucket 2 till 1 is empty (visited ?a ?b) (test (> ?a 0)) (test (<= (+ ?b ?a) 5)) (not (visited 0 =(+ ?b ?a))) => (visit ?a ?b 0 (+ ?a ?b)) ) (defrule pour21-empty2 ; pour from bucket 2 to bucket 1 till 2 is empty (visited ?a ?b) (test (> ?b 0)) (test (<= (+ ?b ?a) 3)) (not (visited =(+ ?b ?a) 0)) => (visit ?a ?b (+ ?a ?b) 0) ) (defrule pour12-fill2 ; pour from bucket 1 to bucket 2 till 1 is full (visited ?a ?b) (test (> ?a 0)) (test (> (+ ?b ?a) 5)) (not (visited =(- (+ ?a ?b) 5) 5)) => (visit ?a ?b (- (+ ?a ?b) 5) 5) ) (defrule pour21-fill1 ; pour from bucket 2 to bucket 1 till 2 is full (visited ?a ?b) (test (> ?b 0)) (test (> (+ ?b ?a) 3)) (not (visited 3 =(- (+ ?a ?b) 3))) => (visit ?a ?b 3 (- (+ ?a ?b) 3)) )