I am working on a program that will tell me which moves to make in a dots-and-boxes game. I am trying to implement a defrule that will check to see if a box already has 2 of the possible 4 sides taken. If this is the case then I don't want to take one of the remaining two lines, as that will give the opponent a free point.
(defrule Player_Move_No_Box_1_1
(next_turn p)
(turn_num ?t_num)
(test(> ?t_num 3))
(line ?l1&~1)
(not(line 1))
=>
(if
(not(or(and(any-factp ((?l line)) (member$ (+ ?l1 3) ?l:implied))(any-factp ((?l line)) (member$ (+ ?l1 4) ?l:implied)))
(and(any-factp ((?l line)) (member$ (+ ?l1 3) ?l:implied))(any-factp ((?l line)) (member$ (+ ?l1 7) ?l:implied)))
(and(any-factp ((?l line)) (member$ (+ ?l1 4) ?l:implied))(any-factp ((?l line)) (member$ (+ ?l1 7) ?l:implied)))))
then
(printout t "Take line #1" crlf)
(assert(line 1))
(assert(next_turn c))))
I've been trying a lot of different things, but this is the last code I tried to use, but with no success. For this piece of code I'm looking at line 1 (clockwise starting from the top of a box the boxes are numbered: x, x+4, x+7, x+3). Is there a simpler way of making this check, or will this way work and I've just messed the code up somewhere?
I would suggest explicitly representing each possible line as a fact and denote in the fact whether the line has been taken. Also do the pattern matching in the conditions of the rules rather than the actions.
CLIPS>
(deftemplate line
(slot id)
(slot taken (default no)))
CLIPS>
(defrule Player_Move_Top_Line
?take <- (line (id ?l1) (taken no))
(line (id =(+ ?l1 3)) (taken ?t3))
(line (id =(+ ?l1 4)) (taken ?t4))
(line (id =(+ ?l1 7)) (taken ?t7))
(test (not (or (and (eq ?t3 yes) (eq ?t4 yes) (eq ?t7 no))
(and (eq ?t3 yes) (eq ?t4 no) (eq ?t7 yes))
(and (eq ?t3 no) (eq ?t4 yes) (eq ?t7 yes)))))
=>
(printout t "Take line #" ?l1 crlf)
(modify ?take (taken yes)))
CLIPS>
I've stripped out the turn information from your original rule just to make it easier to test it.
CLIPS> (assert (line (id 0)) (line (id 3)) (line (id 4)) (line (id 7)))
<Fact-4>
CLIPS> (agenda)
0 Player_Move_Top_Line: f-1,f-2,f-3,f-4
For a total of 1 activation.
CLIPS> (reset)
CLIPS> (assert (line (id 0)) (line (id 3)) (line (id 4)) (line (id 7) (taken yes)))
<Fact-4>
CLIPS> (agenda)
0 Player_Move_Top_Line: f-1,f-2,f-3,f-4
For a total of 1 activation.
CLIPS> (reset)
CLIPS> (assert (line (id 0)) (line (id 3)) (line (id 4) (taken yes)) (line (id 7)))
<Fact-4>
CLIPS> (agenda)
0 Player_Move_Top_Line: f-1,f-2,f-3,f-4
For a total of 1 activation.
CLIPS> (reset)
CLIPS> (assert (line (id 0)) (line (id 3) (taken yes)) (line (id 4)) (line (id 7)))
<Fact-4>
CLIPS> (agenda)
0 Player_Move_Top_Line: f-1,f-2,f-3,f-4
For a total of 1 activation.
CLIPS> (reset)
CLIPS> (assert (line (id 0)) (line (id 3) (taken yes)) (line (id 4) (taken yes)) (line (id 7)))
<Fact-4>
CLIPS> (agenda)
CLIPS> (reset)
CLIPS> (assert (line (id 0)) (line (id 3) (taken yes)) (line (id 4)) (line (id 7) (taken yes)))
<Fact-4>
CLIPS> (agenda)
CLIPS> (reset)
CLIPS> (assert (line (id 0)) (line (id 3)) (line (id 4) (taken yes)) (line (id 7) (taken yes)))
<Fact-4>
CLIPS> (agenda)
CLIPS> (reset)
CLIPS> (assert (line (id 0)) (line (id 3) (taken yes)) (line (id 4) (taken yes)) (line (id 7) (taken yes)))
<Fact-4>
CLIPS> (agenda)
0 Player_Move_Top_Line: f-1,f-2,f-3,f-4
For a total of 1 activation.
CLIPS>
Related
I have the following deftemplate
(deftemplate potential
(multislot values (type INTEGER))
)
and I want to remove instances of this deftemplate if an integer only occurs in it.
Example:
fact-1: potential 1 2 3
fact-2: potential 2 3 4
fact-3: potential 2 3 4 5
I want to retract fact-1 and fact-3 because they include 1 and 5 uniquely.
I'm trying to achieve it using a rule like the following:
1 (defrule remove_if_only_option
2 ?p<-(potential (values $? ?value $? ))
3 (not (exists (potential (values $? ?value $?) )))
4 =>
5 (retract ?p)
7 )
Obviously it doesn't work as line 3 can match the initial fact. Is there any way to make this run in such a way the rule doesn't consider ?p for the rest of it?
Thanks.
Neither of the patterns in your rule will be effected by the presence or absence of the initial-fact since they both match potential facts. The exists conditional element is superfluous in the second pattern, so your rule is equivalent to
(defrule remove_if_only_option
?p <- (potential (values $? ?value $?))
(not (potential (values $? ?value $?)))
=>
(retract ?p))
and since the condition x and not x is never true, this rule can never be satisfied.
You can tell if two facts of the same type are different by comparing their fact address, but since you can't bind a fact address within a not conditional element, you can't do that in this case. Alternatively, you can include a slot containing a unique value for each fact that can be used to tell if the facts are different:
CLIPS (6.31 6/12/19)
CLIPS>
(deftemplate potential
(slot id (default-dynamic (gensym*)))
(multislot values (type INTEGER)))
CLIPS>
(defrule remove_if_only_option
?p <- (potential (id ?id) (values $? ?value $?))
(not (potential (id ~?id) (values $? ?value $?)))
=>
(retract ?p))
CLIPS>
(assert (potential (values 1 2 3))
(potential (values 2 3 4))
(potential (values 2 3 4 5)))
<Fact-3>
CLIPS> (agenda)
0 remove_if_only_option: f-3,*
0 remove_if_only_option: f-1,*
For a total of 2 activations.
CLIPS>
Initially, this appears to work, but once it runs you can see there are issues:
CLIPS> (run 1)
CLIPS> (agenda)
0 remove_if_only_option: f-2,*
0 remove_if_only_option: f-1,*
For a total of 2 activations.
CLIPS>
Once f-3 is removed, the value 4 in f-2 now becomes unique and so this fact will now also be removed by this rule. The problem is that the common set of values is implicitly represented by the collection of potential facts, and once you start removing them you're altering the common set of values.
In order to do this, you'll need at least two steps and consequently at least two rules. One way to do it is to mark the facts that need to be deleted in one step and then delete them in another:
CLIPS> (clear)
CLIPS>
(deftemplate potential
(slot id (default-dynamic (gensym*)))
(multislot values (type INTEGER))
(slot delete (default no)))
CLIPS>
(defrule remove_if_only_option
(not (done))
?p <- (potential (id ?id) (values $? ?value $?) (delete no))
(not (potential (id ~?id) (values $? ?value $?)))
=>
(modify ?p (delete yes)))
CLIPS>
(defrule remove
(declare (salience -10))
?p <- (potential (delete yes))
=>
(assert (done))
(retract ?p))
CLIPS>
(assert (potential (values 1 2 3))
(potential (values 2 3 4))
(potential (values 2 3 4 5)))
<Fact-3>
CLIPS> (agenda)
0 remove_if_only_option: *,f-3,*
0 remove_if_only_option: *,f-1,*
For a total of 2 activations.
CLIPS> (run 1)
CLIPS> (agenda)
0 remove_if_only_option: *,f-1,*
-10 remove: f-4
For a total of 2 activations.
CLIPS> (facts)
f-0 (initial-fact)
f-1 (potential (id gen4) (values 1 2 3) (delete no))
f-2 (potential (id gen5) (values 2 3 4) (delete no))
f-4 (potential (id gen6) (values 2 3 4 5) (delete yes))
For a total of 4 facts.
CLIPS> (run 1)
CLIPS> (agenda)
-10 remove: f-5
-10 remove: f-4
For a total of 2 activations.
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-2 (potential (id gen5) (values 2 3 4) (delete no))
f-6 (done)
For a total of 3 facts.
CLIPS>
Another way to create a fact containing the unique values:
CLIPS> (clear)
CLIPS>
(deftemplate potential
(slot id (default-dynamic (gensym*)))
(multislot values (type INTEGER)))
CLIPS>
(defrule add-to-unique
(not (done))
?c <- (unique $?unique)
(potential (id ?id) (values $? ?value $?))
(not (potential (id ~?id) (values $? ?value $?)))
(test (not (member$ ?value ?unique)))
=>
(retract ?c)
(assert (unique $?unique ?value)))
CLIPS>
(defrule remove_if_only_option
(declare (salience -10))
(unique $?unique)
?p <- (potential (values $? ?value $?))
(test (member$ ?value ?unique))
=>
(assert (done))
(retract ?p))
CLIPS>
(assert (potential (values 1 2 3))
(potential (values 2 3 4))
(potential (values 2 3 4 5)))
<Fact-3>
CLIPS> (assert (unique))
<Fact-4>
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-2 (potential (id gen26) (values 2 3 4))
f-6 (unique 5 1)
f-7 (done)
For a total of 4 facts.
CLIPS>
I want to build an expert system in which in a case of emergency at a building with some floors (it needs to work for any amount of floors) the elevator should take the people into the ground.
The thing is, that the defrule to send the elevator at any floor never makes it in the agenda, so the system just does nothing. The correct action should be to fire the rule and then another rule that takes the people from the floor.
The code for the defrule is this:
(defrule move_to_floor "elevator moves to any floor "
?i <- (elevator is_at floor ?x has ?y adults and ?z minors)
(floor ?fl&~?x has ?n adult and ?m minor people)
(test (> (+ ?n ?m) 0))
=>
(retract ?i)
(assert (elevator is_at floor ?fl has ?y adults and ?z minors))
)
The facts as they have been initialized from the user in another defrule above are these:
f-0 (initial-fact)
f-1 (elevator is_at 0 has 0 adults and 0 minors)
f-3 (capacity 4)
f-4 (floors 3)
f-5 (initCanEnter 0) ;At 0 this prevents from entering the init_defrule again
f-6 (floor 3 has 2 adult and 1 minor people)
f-7 (floor 2 has 4 adult and 5 minor people)
f-8 (floor 1 has 1 adult and 2 minor people)
I can't seem to find the solution. Also, I'm using deffacts and not deftemplate as I have seen many people using on the internet.
You can use the matches command to see which patterns in a rule are matched.
CLIPS (6.31 2/3/18)
CLIPS>
(defrule move_to_floor "elevator moves to any floor "
?i <- (elevator is_at floor ?x has ?y adults and ?z minors)
(floor ?fl&~?x has ?n adult and ?m minor people)
(test (> (+ ?n ?m) 0))
=>
(retract ?i)
(assert (elevator is_at floor ?fl has ?y adults and ?z minors)))
CLIPS>
(deffacts initial
(elevator is_at 0 has 0 adults and 0 minors)
(capacity 4)
(floors 3)
(initCanEnter 0) ;At 0 this prevents from entering the init_defrule again
(floor 3 has 2 adult and 1 minor people)
(floor 2 has 4 adult and 5 minor people)
(floor 1 has 1 adult and 2 minor people))
CLIPS> (reset)
CLIPS> (matches move_to_floor)
Matches for Pattern 1
None
Matches for Pattern 2
f-5
f-6
f-7
Partial matches for CEs 1 - 2
None
Activations
None
(3 0 0)
CLIPS>
In this case, the first pattern is not matched. That's because your pattern expects is_at floor ?x but your fact contains is_at 0 (the symbol floor is missing in your fact). If you correct this issue, the rule will be placed on the agenda.
CLIPS>
(deffacts initial
(elevator is_at floor 0 has 0 adults and 0 minors)
(capacity 4)
(floors 3)
(initCanEnter 0) ;At 0 this prevents from entering the init_defrule again
(floor 3 has 2 adult and 1 minor people)
(floor 2 has 4 adult and 5 minor people)
(floor 1 has 1 adult and 2 minor people))
CLIPS> (reset)
CLIPS> (agenda)
0 move_to_floor: f-1,f-7
0 move_to_floor: f-1,f-6
0 move_to_floor: f-1,f-5
For a total of 3 activations.
CLIPS>
If you issue a (run) command at this point, the rules will endlessly fire in a loop moving from floor to floor, so that's something you'll need to address next.
If you use deftemplate facts rather than ordered facts, you'll get an error if you misspell slot names, so it's better to use these if you have a fact with multiple attributes.
CLIPS> (clear)
CLIPS>
(deftemplate elevator
(slot at_floor (type INTEGER))
(slot adults (type INTEGER))
(slot minors (type INTEGER)))
CLIPS>
(deftemplate floor
(slot # (type INTEGER))
(slot adults (type INTEGER))
(slot minors (type INTEGER)))
CLIPS>
(deffacts initial
(elevator (at_floor 0))
(capacity 4)
(floors 3)
(initCanEnter 0)
(floor (# 3) (adults 2) (minors 1))
(floor (# 2) (adults 4) (minors 5))
(floor (# 1) (adults 1) (minors 2)))
CLIPS>
(defrule move_to_floor
?i <- (elevator (at_floor ?x))
(floor (# ?fl&~?x) (adults ?n) (minors ?m))
(test (> (+ ?n ?m) 0))
=>
(modify ?i (at_floor ?fl)))
CLIPS> (reset)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (elevator (at_floor 0) (adults 0) (minors 0))
f-2 (capacity 4)
f-3 (floors 3)
f-4 (initCanEnter 0)
f-5 (floor (# 3) (adults 2) (minors 1))
f-6 (floor (# 2) (adults 4) (minors 5))
f-7 (floor (# 1) (adults 1) (minors 2))
For a total of 8 facts.
CLIPS> (agenda)
0 move_to_floor: f-1,f-7
0 move_to_floor: f-1,f-6
0 move_to_floor: f-1,f-5
For a total of 3 activations.
CLIPS>
I am trying to solve a problem, where I have to fill a 5x5 matrix with letters A, B, C, D, and E. Each letter cannot occur more than once in each row and in each column. With some initial letter positions given.
I created every position as separate facts eg. "M 1 1 X".
I am struggling how to loop a defrule in way to assert a fact with correct letter and check the conditions again.
(defrule solveA5
?a <-(M 5 ?c X)
(not (M ?x ?c A))
=>
(retract ?a)
(assert (M 5 ?c A))
)
Code above for example is only to check presence of A in every position of 5th row, but the problem is that conditions are checked at the beginning only and instead of asserting correct fact and checking again it asserts A in every position.
I've tried using deffunction to loop defrule.
(deffunction solve (?letter)
(loop-for-count (?x 1 5) do
(loop-for-count (?y 1 5) do
(build (str-cat"defrule costam
?a <-(M ?x ?y X)
(not (and(M ?x ?a ?letter) (M ?b ?y ?letter))
=>
(retract ?a)
(assert (M ?x ?y ?letter))")
)
)
)
)
Unfortunately running
(solve A)
returns "FALSE" and doesn't modify any facts.
To handle iteration within rules, you must assert the iteration information as facts to allow the rules to match and modify this information. In the placement, it's not essential to do this in any particular order, so you can just assert information containing the rows, columns, and letters to place and allow the rules fire arbitrarily:
CLIPS>
(deftemplate element
(slot row)
(slot column)
(slot value))
CLIPS>
(deftemplate print
(slot row)
(slot column)
(slot end-of-row))
CLIPS>
(deffacts initial
(rows 1 2 3 4 5)
(columns 1 2 3 4 5)
(letters A B C D E))
CLIPS>
(defrule place
(rows $? ?r1 $?)
(columns $? ?c1 $?)
(letters $? ?l $?)
(not (element (row ?r1) (column ?c1)))
(not (and (element (row ?r2)
(column ?c2)
(value ?l))
(test (or (= ?r1 ?r2) (= ?c1 ?c2)))))
=>
(assert (element (row ?r1) (column ?c1) (value ?l))))
CLIPS>
(defrule print-start
(declare (salience -10))
(rows ?r $?)
(columns ?c $?rest)
=>
(assert (print (row ?r)
(column ?c)
(end-of-row (= (length$ ?rest) 0)))))
CLIPS>
(defrule print-next-column
(declare (salience -10))
?f <- (print (column ?c))
(columns $? ?c ?nc $?rest)
=>
(modify ?f (column ?nc)
(end-of-row (= (length$ ?rest) 0))))
CLIPS>
(defrule print-next-row
(declare (salience -10))
?f <- (print (column ?c) (row ?r))
(columns $?first ?c)
(rows $? ?r ?nr $?)
=>
(if (= (length$ ?first) 0)
then
(bind ?eor TRUE)
(bind ?nc ?c)
else
(bind ?eor FALSE)
(bind ?nc (nth$ 1 ?first)))
(modify ?f (row ?nr)
(column ?nc)
(end-of-row ?eor)))
CLIPS>
(defrule print-placed
(print (row ?r) (column ?c) (end-of-row ?eor))
(element (row ?r) (column ?c) (value ?l))
=>
(if ?eor
then
(printout t ?l crlf)
else
(printout t ?l " ")))
CLIPS>
(defrule print-unplaced
(print (row ?r) (column ?c) (end-of-row ?eor))
(not (element (row ?r) (column ?c)))
=>
(if ?eor
then
(printout t "?" crlf)
else
(printout t "? ")))
CLIPS> (reset)
CLIPS> (run)
E D C B A
? C D A B
? B A D C
? A B C D
A ? ? ? E
CLIPS>
In this example, the print rules iterate over the rows and columns by storing the iteration information in facts. You can see how much more complicated this is than the place rule which assigns the elements in an arbitrary manner.
Whether you assign the values arbitrarily or in a specific order, it's possible to assign values that prevent a solution, so you must implement backtracking in order to guarantee finding the solution if one exists. In this example, the facts store information about the order of the value placements and the values that have been tried:
CLIPS> (clear)
CLIPS>
(deftemplate element
(slot row)
(slot column)
(slot value (default unset))
(multislot values)
(slot placement))
CLIPS>
(deffacts initial
(placement 0)
(rows 1 2 3 4 5)
(columns 1 2 3 4 5)
(letters A B C D E))
CLIPS>
(defrule prime
(placement ?p)
(rows $? ?r $?)
(columns $? ?c $?)
(letters $?l)
(not (element (placement ?p)))
(not (element (row ?r) (column ?c)))
=>
(assert (element (placement ?p) (values ?l) (row ?r) (column ?c))))
CLIPS>
(defrule place-good
?f1 <- (placement ?p)
?f2 <- (element (placement ?p)
(value unset)
(row ?r1)
(column ?c1)
(values ?v $?rest))
(not (and (element (row ?r2)
(column ?c2)
(value ?v))
(test (or (= ?r1 ?r2) (= ?c1 ?c2)))))
=>
(retract ?f1)
(assert (placement (+ ?p 1)))
(modify ?f2 (value ?v) (values ?rest)))
CLIPS>
(defrule place-bad
(placement ?p)
?f2 <- (element (placement ?p)
(value unset)
(row ?r1)
(column ?c1)
(values ?v $?rest))
(element (row ?r2)
(column ?c2)
(value ?v))
(test (or (= ?r1 ?r2) (= ?c1 ?c2)))
=>
(modify ?f2 (values ?rest)))
CLIPS>
(defrule backtrack
?f1 <- (placement ?p)
?f2 <- (element (placement ?p)
(value unset)
(values))
?f3 <- (element (placement =(- ?p 1))
(value ~unset))
=>
(retract ?f1)
(assert (placement (- ?p 1)))
(retract ?f2)
(modify ?f3 (value unset)))
CLIPS>
(defrule print
(declare (salience -10))
(rows $?rows)
(columns $?columns)
=>
(progn$ (?r ?rows)
(progn$ (?c ?columns)
(if (not (do-for-fact ((?f element))
(and (= ?r ?f:row) (= ?c ?f:column))
(printout t ?f:value " ")))
then
(printout t "? ")))
(printout t crlf)))
CLIPS> (reset)
CLIPS> (run)
B C D E A
A B C D E
C A E B D
D E A C B
E D B A C
CLIPS>
The print rules have been simplified into a single rule that iterates over the row and columns in the actions of the rule and uses the fact query functions to retrieve values that have been assigned.
The program also works if you preassign some of the values:
CLIPS> (reset)
CLIPS> (assert (element (row 1) (column 1) (value A)))
<Fact-5>
CLIPS> (assert (element (row 3) (column 3) (value C)))
<Fact-6>
CLIPS> (assert (element (row 5) (column 4) (value E)))
<Fact-7>
CLIPS> (run)
A C E D B
B A D C E
D E C B A
E D B A C
C B A E D
CLIPS>
If I have a bunch of facts like (example (fact 1)), (example (fact 2)), (example (fact 3)), and have another list of facts like (myfact (number 2)), how can I perform a printout on each item in the first list that is not in the second (based on the number in the fact/number slots)? I suspect I need do-for-all-facts, but I'm not sure exactly how. Here's my incomplete code:
(deffunction difference ()
(do-for-all-facts ((?f1 example)) TRUE
(find-all-facts ((?f2 myfact)) (eq 1 1))
(if (somehow check if ?f1:fact does not equal ANY of number slots in ?f2) then
(printout t "..." crlf))))
CLIPS> (clear)
CLIPS> (deftemplate example (slot fact))
CLIPS> (deftemplate myfact (slot number))
CLIPS>
(deffacts start
(example (fact 1))
(example (fact 2))
(example (fact 3))
(myfact (number 2))
(myfact (number 4)))
CLIPS>
(deffunction difference ()
(do-for-all-facts ((?f1 example))
(not (any-factp ((?f2 myfact)) (eq ?f1:fact ?f2:number)))
(printout t "difference " ?f1:fact crlf)))
CLIPS> (reset)
CLIPS> (difference)
difference 1
difference 3
CLIPS>
(defrule difference
(example (fact ?n))
(not (myfact (number ?n)))
=>
(printout t "difference " ?n crlf))
CLIPS> (run)
difference 3
difference 1
CLIPS>
I am using CLIPS for a project.
I am using this template A which has an attribute model and another template B which has an attribute model as well.
So what I want to achieve is based on the attribute model, return those facts of template A which has the same attribute model value as of facts from template B.
I tried using this format
(find-all-facts((?a template_A)(?b template_B))
(and
//condition to be met
)
)
it does give me the results, but it is giving me both the results for A and B which are duplicates.. How do I make it in a way it returns non duplicate values, either A or B?
CLIPS>
(deftemplate template_A
(slot model))
CLIPS>
(deftemplate template_B
(slot model))
CLIPS>
(deffacts start
(template_A (model 1))
(template_A (model 2))
(template_A (model 3))
(template_B (model 2))
(template_B (model 3))
(template_B (model 4)))
CLIPS>
(deffunction extract-every-nth-value (?values ?start ?increment)
(bind ?rv (create$))
(while (<= ?start (length$ ?values))
(bind ?rv (create$ ?rv (nth$ ?start ?values)))
(bind ?start (+ ?start ?increment)))
(return ?rv))
CLIPS> (reset)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (template_A (model 1))
f-2 (template_A (model 2))
f-3 (template_A (model 3))
f-4 (template_B (model 2))
f-5 (template_B (model 3))
f-6 (template_B (model 4))
For a total of 7 facts.
CLIPS>
(find-all-facts ((?a template_A)(?b template_B))
(eq ?a:model ?b:model))
(<Fact-2> <Fact-4> <Fact-3> <Fact-5>)
CLIPS>
(extract-every-nth-value
(find-all-facts ((?a template_A)(?b template_B))
(eq ?a:model ?b:model))
1 2)
(<Fact-2> <Fact-3>)
CLIPS>