Search code examples
clipsconways-game-of-life

Using exists in if statement


I'm trying to implement the game of life in CLIPS and I've encountered an issue (Missing function declaration for 'exists') with the exists statement while trying to count the neighbors for each cell. Can you not use the exists statement within an if statement? How should I approach the task of counting neighbors?

This is the cell template:

(deftemplate cell
    (slot iteration)
    (slot x)
    (slot y)
)

This is the function:

(deffunction countneighbors (?i ?j ?c)
    (bind ?*neighbors* 0)
    (if (exists (cell (iteration ?c) (x (- ?i 1)) (y (- ?j 1)))) then
        (bind ?*neighbors* (+ 0 1 ?*neighbors*))
    )
    (if (exists (cell (iteration ?c) (x (- ?i 1)) (y ?j))) then
        (bind ?*neighbors* (+ 0 1 ?*neighbors*))
    )
.
.
.
)

Solution

  • The exists conditional element is not a function, so you can't call it like one. It can only be used in the conditions of a rule. You can use the fact query functions if you want to iterate over a group of facts outside of the conditions of a rule:

             CLIPS (6.31 2/3/18)
    CLIPS> 
    (deftemplate cell
       (slot iteration (default 0))
       (slot x)
       (slot y))
    CLIPS> 
    (deffacts cells
       (cell (x 1) (y 1))  
       (cell (x 1) (y 2))  ;;; - * *
       (cell (x 2) (y 3))  ;;; * - -
       (cell (x 3) (y 3))) ;;; * - -
    CLIPS>    
    (deffunction count-neighbors (?i ?j ?c)
       (bind ?neighbors 0)
       (do-for-all-facts ((?cell cell)) 
                         (and (= ?cell:iteration ?c)
                              (<= (abs (- ?i ?cell:x)) 1)
                              (<= (abs (- ?j ?cell:y)) 1)
                              (or (!= ?i ?cell:x) (!= ?j ?cell:y)))
          (bind ?neighbors (+ 1 ?neighbors)))
       ?neighbors)
    
    CLIPS> (reset)
    CLIPS> (count-neighbors 1 1 0)
    1
    CLIPS> (count-neighbors 2 2 0)
    4
    CLIPS> (count-neighbors 2 1 0)
    2
    CLIPS> 
    

    Here's a technique for determining neighbors using rules:

    CLIPS> (clear)
    CLIPS> 
    (deftemplate cell
       (slot iteration (default 0))
       (slot x)
       (slot y)
       (slot alive (default 0))
       (multislot neighbors))
    CLIPS>    
    (deftemplate direction
       (slot d)
       (slot dx)
       (slot dy))
    CLIPS> 
    (deffacts cells
       (cell (x 1) (y 1) (alive 1))  
       (cell (x 1) (y 2) (alive 1))
       (cell (x 1) (y 3))
       (cell (x 2) (y 1))
       (cell (x 2) (y 2))  
       (cell (x 2) (y 3) (alive 1))
       (cell (x 3) (y 1))
       (cell (x 3) (y 2))  
       (cell (x 3) (y 3) (alive 1)))
    CLIPS> 
    (deffacts directions
       (direction (d n)  (dx 0)  (dy 1))
       (direction (d ne) (dx 1)  (dy 1))
       (direction (d e)  (dx 1)  (dy 0))
       (direction (d se) (dx 1)  (dy -1))
       (direction (d s)  (dx 0)  (dy -1))
       (direction (d sw) (dx -1) (dy -1))
       (direction (d w)  (dx -1) (dy 0))
       (direction (d nw) (dx -1) (dy 1))) 
    CLIPS>               
    (defrule add-neighbor
       ?c <- (cell (x ?x) (y ?y) (neighbors $?n))
       (direction (d ?d) (dx ?dx) (dy ?dy))
       (cell (x =(+ ?x ?dx)) (y =(+ ?y ?dy)) (alive 1))
       (test (not (member$ ?d ?n)))
       =>
       (modify ?c (neighbors $?n ?d))) 
    CLIPS> (reset)
    CLIPS> (run)
    CLIPS> (facts)
    f-0     (initial-fact)
    f-7     (cell (iteration 0) (x 3) (y 1) (alive 0) (neighbors))
    f-10    (direction (d n) (dx 0) (dy 1))
    f-11    (direction (d ne) (dx 1) (dy 1))
    f-12    (direction (d e) (dx 1) (dy 0))
    f-13    (direction (d se) (dx 1) (dy -1))
    f-14    (direction (d s) (dx 0) (dy -1))
    f-15    (direction (d sw) (dx -1) (dy -1))
    f-16    (direction (d w) (dx -1) (dy 0))
    f-17    (direction (d nw) (dx -1) (dy 1))
    f-19    (cell (iteration 0) (x 2) (y 1) (alive 0) (neighbors nw w))
    f-21    (cell (iteration 0) (x 3) (y 2) (alive 0) (neighbors nw n))
    f-25    (cell (iteration 0) (x 2) (y 2) (alive 0) (neighbors w sw ne n))
    f-26    (cell (iteration 0) (x 3) (y 3) (alive 1) (neighbors w))
    f-28    (cell (iteration 0) (x 2) (y 3) (alive 1) (neighbors e sw))
    f-30    (cell (iteration 0) (x 1) (y 2) (alive 1) (neighbors ne s))
    f-31    (cell (iteration 0) (x 1) (y 1) (alive 1) (neighbors n))
    f-33    (cell (iteration 0) (x 1) (y 3) (alive 0) (neighbors s e))
    For a total of 18 facts.
    CLIPS> 
    

    Here's a complete implementation that only retains facts representing cells that are alive.

    (deffacts start-phase
       (phase neighbors))
    
    (deftemplate cell
       (slot x)
       (slot y)
       (slot alive (default no))
       (multislot neighbors))
    
    (deffacts glider
       (cell (x 1) (y 2) (alive yes))
       (cell (x 2) (y 3) (alive yes))
       (cell (x 3) (y 1) (alive yes))
       (cell (x 3) (y 2) (alive yes))
       (cell (x 3) (y 3) (alive yes)))
    
    (deftemplate direction
       (slot tag)
       (slot dx (default 0))
       (slot dy (default 0)))
    
    (deffacts directions
       (direction (tag n) (dy 1))
       (direction (tag ne) (dx 1) (dy 1))
       (direction (tag e) (dx 1))
       (direction (tag se) (dx 1) (dy -1))
       (direction (tag s) (dy -1))
       (direction (tag sw) (dx -1) (dy -1))
       (direction (tag w) (dx -1))
       (direction (tag nw) (dx -1) (dy 1)))
    
    (defrule check-for-life
       (phase neighbors)
       (cell (x ?x) (y ?y) (alive yes))
       (direction (tag ?tag) (dx ?dx) (dy ?dy))
       (not (cell (x =(+ ?x ?dx)) (y =(+ ?y ?dy))))
       =>
       (assert (cell (x (+ ?x ?dx)) (y (+ ?y ?dy)))))
    
    (defrule add-neighbor
       (phase neighbors)
       ?c <- (cell (x ?x) (y ?y) (neighbors $?n))
       (direction (tag ?t) (dx ?dx) (dy ?dy))
       (cell (x =(+ ?x ?dx)) (y =(+ ?y ?dy)) (alive yes))
       (test (not (member$ ?t ?n)))
       =>
       (modify ?c (neighbors ?t ?n)))
    
    (defrule remove-neighbor
       (phase neighbors)
       ?c <- (cell (x ?x) (y ?y) (neighbors $?b ?t $?e))
       (direction (tag ?t) (dx ?dx) (dy ?dy))
       (not (cell (x =(+ ?x ?dx)) (y =(+ ?y ?dy)) (alive yes)))
       =>
       (modify ?c (neighbors ?b ?e)))
    
    (defrule life-to-death
       (phase life)
       ?c <- (cell (alive yes) (neighbors $?n))
       (test (not (= (length$ ?n) 2)))
       (test (not (= (length$ ?n) 3)))
       =>
       (retract ?c)) 
    
    (defrule death-to-life
       (phase life)
       ?c <- (cell (alive no) (neighbors $?n))
       (test (= (length$ ?n) 3))
       =>
       (modify ?c (alive yes))) 
    
    (defrule death-to-death
       (phase life)
       ?c <- (cell (alive no) (neighbors $?n))
       (test (!= (length$ ?n) 3))
       =>
       (retract ?c)) 
    
    (defrule neighbors-to-life
       (declare (salience -10))
       ?p <- (phase neighbors)
       =>
       (retract ?p)
       (assert (phase life)))
    
    (defrule life-to-neighbors
       (declare (salience -10))
       ?p <- (phase life)
       =>
       (retract ?p)
       (assert (phase neighbors)))