;;;====================================================== ;;; Farmer's Dilemma Problem ;;; ;;; Another classic AI problem (cannibals and the ;;; missionary) in agricultural terms. The point is ;;; to get the farmer, the fox the cabbage and the ;;; goat across a stream. ;;; ;;; But the boat only holds 2 items. If left ;;; alone with the goat, the fox will eat it. If ;;; left alone with the cabbage, the goat will eat ;;; it. ;;; ;;; This example uses COOL classes and ;;; message-handlers to solve the problem. ;;; ;;; CLIPS Version 6.4 Example ;;; ;;; To execute, merely load and enter (solve-dilemma). ;;;====================================================== ;;;************** ;;;* DEFCLASSES * ;;;************** (defclass status (is-a USER) (role concrete) (slot farmer (create-accessor write) (default shore-1)) (slot fox (create-accessor write) (default shore-1)) (slot goat (create-accessor write) (default shore-1)) (slot cabbage (create-accessor write) (default shore-1)) (slot parent (create-accessor write) (default no-parent)) (slot search-depth (create-accessor write) (default 1)) (slot last-move (create-accessor write) (default no-move))) ;;;**************** ;;;* DEFFUNCTIONS * ;;;**************** (deffunction contradiction (?f ?x ?g ?c ?d) (if (or (and (eq ?x ?g) (neq ?f ?x)) (and (eq ?g ?c) (neq ?f ?g))) then TRUE else (any-instancep ((?s status)) (and (eq ?s:farmer ?f) (eq ?s:fox ?x) (eq ?s:goat ?g) (eq ?s:cabbage ?c) (< ?s:search-depth ?d))))) (deffunction opposite-shore (?value) (if (eq ?value shore-1) then shore-2 else shore-1)) (deffunction solve-dilemma () (do-for-all-instances ((?a status)) TRUE (send ?a delete)) (make-instance start of status) (send [start] generate-moves)) ;;;************** ;;;* DEFRULES * ;;;************** (defrule start-it => (solve-dilemma)) ;;;*********************** ;;;* DEFMESSAGE-HANDLERS * ;;;*********************** (defmessage-handler status move-farmer () (if (not (contradiction (opposite-shore ?self:farmer) ?self:fox ?self:goat ?self:cabbage ?self:search-depth)) then (bind ?x (make-instance (gensym) of status (farmer (opposite-shore ?self:farmer)) (fox ?self:fox) (goat ?self:goat) (cabbage ?self:cabbage) (last-move farmer) (parent ?self) (search-depth (+ ?self:search-depth 1)))) (if (not (send ?x solution?)) then (send ?x generate-moves)))) (defmessage-handler status move-goat () (if (and (eq ?self:farmer ?self:goat) (not (contradiction (opposite-shore ?self:farmer) ?self:fox (opposite-shore ?self:goat) ?self:cabbage ?self:search-depth))) then (bind ?x (make-instance (gensym) of status (farmer (opposite-shore ?self:farmer)) (fox ?self:fox) (goat (opposite-shore ?self:farmer)) (cabbage ?self:cabbage) (last-move goat) (parent ?self) (search-depth (+ ?self:search-depth 1)))) (if (not (send ?x solution?)) then (send ?x generate-moves)))) (defmessage-handler status move-fox () (if (and (eq ?self:farmer ?self:fox) (not (contradiction (opposite-shore ?self:farmer) (opposite-shore ?self:fox) ?self:goat ?self:cabbage ?self:search-depth))) then (bind ?x (make-instance (gensym) of status (farmer (opposite-shore ?self:farmer)) (fox (opposite-shore ?self:farmer)) (goat ?self:goat) (cabbage ?self:cabbage) (last-move fox) (parent ?self) (search-depth (+ ?self:search-depth 1)))) (if (not (send ?x solution?)) then (send ?x generate-moves)))) (defmessage-handler status move-cabbage () (if (and (eq ?self:farmer ?self:cabbage) (not (contradiction (opposite-shore ?self:farmer) ?self:fox ?self:goat (opposite-shore ?self:cabbage) ?self:search-depth))) then (bind ?x (make-instance (gensym) of status (farmer (opposite-shore ?self:farmer)) (fox ?self:fox) (goat ?self:goat) (cabbage (opposite-shore ?self:farmer)) (last-move cabbage) (parent ?self) (search-depth (+ ?self:search-depth 1)))) (if (not (send ?x solution?)) then (send ?x generate-moves)))) (defmessage-handler status generate-moves () (send ?self move-farmer) (send ?self move-fox) (send ?self move-goat) (send ?self move-cabbage)) (defmessage-handler status print-solution () (if (neq ?self:parent no-parent) then (send ?self:parent print-solution) (bind ?move-dest (dynamic-get ?self:last-move)) (if (eq ?self:last-move farmer) then (println "Farmer moves alone to " ?move-dest ".") else (println "Farmer moves with " ?self:last-move " to " ?move-dest ".")))) (defmessage-handler status solution? () (if (and (eq ?self:farmer shore-2) (eq ?self:fox shore-2) (eq ?self:goat shore-2) (eq ?self:cabbage shore-2)) then (println crlf "Solution found:" crlf) (send ?self print-solution) (println) TRUE else FALSE))