I'm trying to create an expert system that decides whether or not you can buy a house. I want to know how to word a rule that allows the person to buy a house if they're over a certain age. For Example, if you type in that you're over 40 years old the system would come back and tell you that you're not allowed to buy a home.
I have tried this code below but it doesn't work
(defrule age-over-forty
(student yes)
(income low)
(credit excellent)
(age 40>)
=>
(printout t "You can not buy a house" crlf))
EDIT: What I mean by "it doesn't work" ; When I run it, you type in a age, lets just say I typed in 46. It would add it to the facts but it is supposed to print out "You can not buy a house" so it doesn't satisfy the (age 40>) part of the code.
Use the predicate constraint (section 5.4.1.5 of the CLIPS 6.3 Basic Programming Guide) or alternatively the test conditional element to perform a numeric comparison.
CLIPS (6.31 6/12/19)
CLIPS>
(defrule age-over-forty
(student yes)
(income low)
(credit excellent)
(age ?age&:(> ?age 40))
=>
(printout t "You can not buy a house" crlf))
CLIPS>
(assert (student yes)
(income low)
(credit excellent)
(age 46))
<Fact-4>
CLIPS> (agenda)
0 age-over-forty: f-1,f-2,f-3,f-4
For a total of 1 activation.
CLIPS> (run)
You can not buy a house
CLIPS>
Related
I want to use a loop-for-count to loop many different variables to let user input and let CLIPS read the variables.
For example:
Question: "How many dependent you wish to add?"
Answer: 5.
Then the CLIPS should create the variables like:
name1
name2
name3
name4
name5
Here is my code:
(printout t "How many dependent you wish to add? (Must have atleast 1): ")
(bind ?DepNo (read))
(assert (DepNo ?DepNo))
(loop-for-count (?DepNo 1 ?DepNo) do
(printout t "Name: ")
(bind $?DepName (explode$ (readline)))
(assert (DepName $?DepName))
)
Create a multifield value to hold all of the dependent names:
CLIPS (6.31 6/12/19)
CLIPS>
(defrule get-dependents
=>
(printout t "How many dependent you wish to add? (Must have at least 1): ")
(bind ?DepNo (read))
(bind ?depNames (create$))
(loop-for-count ?DepNo
(printout t "Name: ")
(bind ?depNames (create$ ?depNames (readline))))
(assert (DepNames ?depNames)))
CLIPS>
(defrule print-names
(DepNames $?depNames)
=>
(printout t "Dependents are: ")
(foreach ?d ?depNames
(printout t " " ?d crlf)))
CLIPS> (reset)
CLIPS> (run)
How many dependent you wish to add? (Must have at least 1): 3
Name: Sally Jones
Name: Fred Jones
Name: David Jones
Dependents are:
Sally Jones
Fred Jones
David Jones
CLIPS> (facts)
f-0 (initial-fact)
f-1 (DepNames "Sally Jones" "Fred Jones" "David Jones")
For a total of 2 facts.
CLIPS>
Let's say I have some facts (I do not know how many there are) like this: lamp x is off. With a defrule I proggressively turn all lamps on so every fact will be: lamp x is on. How do I check every lamp that is on. I know that if there were three lamps I could write:
(defrule checkAllLamps
(lamp 1 is on)
(lamp 2 is on)
(lamp 3 is on)
=>
(printout t "All lamps are on now")
)
But for x lamps?
Thank you!
You can use fact-set query functions for that (chapter 12.9.12 of the Basic Programming Guide).
(deftemplate lamp
(slot id (type INTEGER))
(slot state (type SYMBOL)))
(defrule all-lamps-are-on
(lamp (state on))
(test (>= (length$ (find-all-facts ((?l lamp)) (eq ?l:state on))) 3))
=>
(printout t "All lamps are on" crlf))
Here's how you can check whether all of the lamps are on. The checkAllLamps rule treats the case where there are no lamps at all as all lamps being on, whereas the checkAllLampsAtLeastOne rule requires that there is at least one lamp that is on.
CLIPS (6.31 2/3/18)
CLIPS>
(defrule checkAllLamps
(not (lamp ? is off))
=>
(printout t "All lamps are on now" crlf))
CLIPS>
(defrule checkAllLampsAtLeastOne
(exists (lamp ? is on))
(not (lamp ? is off))
=>
(printout t "All lamps are on now" crlf))
CLIPS> (agenda)
0 checkAllLamps: *
For a total of 1 activation.
CLIPS> (assert (lamp 1 is on))
<Fact-1>
CLIPS> (agenda)
0 checkAllLampsAtLeastOne: *,*
0 checkAllLamps: *
For a total of 2 activations.
CLIPS> (assert (lamp 2 is off))
<Fact-2>
CLIPS> (agenda)
CLIPS> (retract 2)
CLIPS> (assert (lamp 2 is on))
<Fact-3>
CLIPS> (agenda)
0 checkAllLampsAtLeastOne: *,*
0 checkAllLamps: *
For a total of 2 activations.
CLIPS>
I'm trying to create an expert system which each time the user responds a question it creates a new fact (for example):
assert(like accion yes)
assert(like multi yes)
And once its done, it shows the name of every game that has those facts in their description:
(deffacts gaming (game Call_of_Duty multi accion)
(game BattleField multi strategy))
I tried using this rule, where I check if every "like-fact" is located inside an especific "game-fact", but it doesn't work:
(defrule conclusion
(like $?x yes)
(game ?y $?x2)
(test(member$ ?x ?x2))
=>
(printout t "You like the game: " ?y crlf))
Here's three different ways you can write the rule. Your original rule, conclusion-1, will print a message for each like that is matched by a game so you can get multiple prints for each game. Rule conclusion-2 prints a game if there is at least one like that matches the game. At most you will see a game printed once. Rule conclusion-3 will print a game only if it matches every like for that game.
CLIPS>
(deffacts gaming
(like accion yes)
(like multi yes)
(game Call_of_Duty multi accion)
(game BattleField multi strategy))
CLIPS>
(defrule conclusion-1
(like $?x yes)
(game ?y $?x2)
(test(member$ ?x ?x2))
=>
(printout t "1 You like the game: " ?y crlf))
CLIPS>
(defrule conclusion-2
(game ?y $?x2)
(exists (like $?x yes)
(test (member$ ?x ?x2)))
=>
(printout t "2 You like the game: " ?y crlf))
CLIPS>
(defrule conclusion-3
(game ?y $?x2)
(forall (like $?x yes)
(test (member$ ?x ?x2)))
=>
(printout t "3 You like the game: " ?y crlf))
CLIPS> (reset)
CLIPS> (run)
1 You like the game: BattleField
2 You like the game: BattleField
1 You like the game: Call_of_Duty
1 You like the game: Call_of_Duty
2 You like the game: Call_of_Duty
3 You like the game: Call_of_Duty
CLIPS>
Is it possible to cause CLIPS to re-evaluate the value of a global variable in a defrule? I have this:
(defrule encourage "Do we have a GPA higher than 3.7?"
(test (> (gpa) 3.7))
=>
(printout t "Keep up the excellent work!" crlf))
gpa is function that calculates and returns a number based on two global variables (grade points and number of credits). I read somewhere that changes to global variables do not invoke pattern matching. How do I go about forcing this? I want to print that string every time I do (run) as long as the GPA is higher than 3.7.
Don't attempt to use global variables or function calls in this manner. First, global variables are specifically designed to not trigger pattern matching. Second, it would take a bit of magic for CLIPS to know when a function call needs to be reevaluated as there are any number of changes which could cause a function to return a different value, not just changes to globals. If you want a particular piece of information to trigger pattern matching, then stick it in a fact or instance. It will make your code easier to understand if you parameterize the function calls and bind the values to be used as arguments in the conditions of the rule.
CLIPS> (clear)
CLIPS>
(deffunction gpa (?grade-points ?number-of-credits)
(/ ?grade-points ?number-of-credits))
CLIPS>
(defrule encourage "Do we have a GPA higher than 3.7?"
(grade-points ?gp)
(number-of-credits ?noc)
(test (> (gpa ?gp ?noc) 3.7))
=>
(printout t "Keep up the excellent work!" crlf))
CLIPS> (assert (grade-points 35) (number-of-credits 10))
<Fact-2>
CLIPS> (agenda)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (grade-points 35)
f-2 (number-of-credits 10)
For a total of 3 facts.
CLIPS> (retract 1)
CLIPS> (assert (grade-points 38))
<Fact-3>
CLIPS> (agenda)
0 encourage: f-3,f-2
For a total of 1 activation.
CLIPS>
Alternately, you can use the fact query functions to iterate over a group of facts to dynamically compute the gpa based on facts rather than globals. Each time you modify one of these facts (add or remove), you can also assert a fact indicating the gpa needs to be rechecked to trigger the encourage rule.
CLIPS> (clear)
CLIPS>
(deftemplate grade
(slot class)
(slot grade-points)
(slot credits))
CLIPS>
(deffunction gpa ()
(bind ?grade-points 0)
(bind ?credits 0)
(do-for-all-facts ((?g grade)) TRUE
(bind ?grade-points (+ ?grade-points ?g:grade-points))
(bind ?credits (+ ?credits ?g:credits)))
(if (= ?credits 0)
then 0
else (/ ?grade-points ?credits)))
CLIPS>
(defrule encourage
?f <- (check-gpa)
=>
(retract ?f)
(if (> (gpa) 3.7)
then
(printout t "Keep up the excellent work!" crlf)))
CLIPS> (gpa)
0
CLIPS> (assert (check-gpa))
<Fact-1>
CLIPS> (run)
CLIPS> (assert (grade (class Algebra) (grade-points 12) (credits 3)))
<Fact-2>
CLIPS> (gpa)
4.0
CLIPS> (assert (check-gpa))
<Fact-3>
CLIPS> (run)
Keep up the excellent work!
CLIPS> (assert (grade (class History) (grade-points 6) (credits 2)))
<Fact-4>
CLIPS> (gpa)
3.6
CLIPS> (assert (check-gpa))
<Fact-5>
CLIPS> (run)
CLIPS> (assert (grade (class Science) (grade-points 12) (credits 3)))
<Fact-6>
CLIPS> (gpa)
3.75
CLIPS> (assert (check-gpa))
<Fact-7>
CLIPS> (run)
Keep up the excellent work!
CLIPS>
i am writing an expert system on cheese.
when reset all the facts about various cheese is loaded into the system and by asking question such as texture smell etc this will retract certain facts from the system.
My Question how do you keep track of the amount of rules in the system. I created a count but i was wondering if there was a way to see the amount of facts currently in the system when running a rule.
Any help would be appriciated
You can make a call to get-defrule-list or get-fact-list to determine the number of rules/facts present in the system:
CLIPS> (assert (a) (b) (c) (d))
<Fact-4>
CLIPS> (defrule x =>)
CLIPS> (defrule y =>)
CLIPS> (defrule z =>)
CLIPS> (length$ (get-defrule-list *))
3
CLIPS> (length$ (get-fact-list *))
5
CLIPS> (facts)
f-0 (initial-fact)
f-1 (a)
f-2 (b)
f-3 (c)
f-4 (d)
For a total of 5 facts.
CLIPS> (rules)
x
y
z
For a total of 3 defrules.
CLIPS>