; 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 (path A B ...) represents that the state where the ; buckets contain A and B gallons respectively has been generated. ; The remaining items in the (path) fact constitute the path that ; was found from state 0,0 to state A,B. For example, (path 3 2 0 5 0 0) ; indicates that 3,2 was achieved by moving from 0,0 to 0,5 and then ; to 3,2. Start with empty buckets by asserting (path 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 (path 0 0)) ; Start from state 0,0 ) ; The print-path function will print out a path of states. The parameter ; is a multifield value representing the path in a form such as ; (3 2 0 5 0 0). It represents a list of states visited in reverse order, ; so the first two elements of the list represent the final state. (deffunction print-path (?path) (bind ?a (nth$ 1 ?path)) ; Contents of 3-gallon bucket in final state. (bind ?b (nth$ 2 ?path)) ; Contents of 5-gallon bucket in final state. (if (> (length$ ?path) 2) then ; There are other states in the path. Call print-path recursively ; to print them out before printing the final state. (print-path (rest$ (rest$ ?path))) (printout t " Move to (" ?a "," ?b ")" crlf) else ; There are no other states in the path. (?a,?b) is the starting ; state. This is the base case -- only (?a,?b) has to be printed. ; (In fact, in this program at least, (?a,?b) is always (0,0). (printout t " Start from (" ?a "," ?b ")" crlf) ) ) ; 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" function is used to print ; the solution. The (halt) command stops execution, since the goal ; has been achieved. (defrule done (declare (salience 1)) (goal ?x) (or (path ?a&:(= ?a ?x) ?b $?rest) (path ?a ?b&:(= ?b ?x) $?rest) ) => (printout t crlf "Solution found::" crlf crlf) (print-path (create$ ?a ?b ?rest)) (printout t crlf crlf) (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. ; it asserts a new "path" fact containing the path of states that ; led to (?toa,?tob), and it prints an informational message. (deffunction visit (?froma ?fromb ?toa ?tob ?rest) (assert (path ?toa ?tob ?froma ?fromb ?rest)) (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 (path ?a ?b $?rest) (test (> ?a 0)) (not (path 0 ?b $?)) => (visit ?a ?b 0 ?b ?rest) ) (defrule empty2 ; empty bucket number 2 (path ?a ?b $?rest) (test (> ?b 0)) (not (path ?a 0 $?)) => (visit ?a ?b ?a 0 ?rest) ) (defrule fill1 ; fill bucket number 1 (path ?a ?b $?rest) (test (< ?a 3)) (not (path 3 ?b $?)) => (visit ?a ?b 3 ?b ?rest) ) (defrule fill2 ; fill bucket number 2 (path ?a ?b&:(< ?b 5) $?rest) (test (< ?b 5)) (not (path ?a 5 $?)) => (visit ?a ?b ?a 5 ?rest) ) (defrule pour12-empty1 ; pour from bucket 1 to bucket 2 till 1 is empty (path ?a ?b $?rest) (test (> ?a 0)) (test (<= (+ ?b ?a) 5)) (not (path 0 =(+ ?b ?a) $?)) => (visit ?a ?b 0 (+ ?a ?b) ?rest) ) (defrule pour21-empty2 ; pour from bucket 2 to bucket 1 till 2 is empty (path ?a ?b $?rest) (test (> ?b 0)) (test (<= (+ ?b ?a) 3)) (not (path =(+ ?b ?a) 0 $?)) => (visit ?a ?b (+ ?a ?b) 0 ?rest) ) (defrule pour12-fill2 ; pour from bucket 1 to bucket 2 till 1 is full (path ?a ?b $?rest) (test (> ?a 0)) (test (> (+ ?b ?a) 5)) (not (path =(- (+ ?a ?b) 5) 5 $?)) => (visit ?a ?b (- (+ ?a ?b) 5) 5 ?rest) ) (defrule pour21-fill1 ; pour from bucket 2 to bucket 1 till 2 is full (path ?a ?b $?rest) (test (> ?b 0)) (test (> (+ ?b ?a) 3)) (not (path 3 =(- (+ ?a ?b) 3) $?)) => (visit ?a ?b 3 (- (+ ?a ?b) 3) ?rest) )