CLIPS compare 2 dates - clips

I'm trying to compare 2 dates to see if one person is older than 25 years old. I tried to bind a variable to the subtraction of the current date and his birthday, and then compare the variable to 25:
(deftemplate data
(multislot current_date (type INTEGER))
)
(deffacts today
(data (current_date 12 4 2018))
)
(deftemplate driver
(multislot name)
(multislot dateBorn)
)
(deffacts drivers
(driver (name Daniel Silva)(dateBorn 3 4 1985))
(driver (name Carlos Santos)(dateBorn 3 4 2000))
)
(defrule cantDrive
(driver(dateBorn $? ?age))
(data (current_date $? ?date))
(bind ?data (- ?age ?date))
(test(< ?data 25))
=>
(printout t "He is younger than 25" crlf)
)
The above code doesn’t run, and I don’t understand why. Is there any operation that is incorrect? Is there any way to compare two dates? For example, I was born in 26/06/1997 and if I need to be at least 25 to rent a car, how do I confirm that?
I can set the current date.

CLIPS (6.31 2/3/18)
CLIPS>
(deffunction current-date ()
(bind ?lt (local-time))
(format nil "%04d-%02d-%02d" (nth$ 1 ?lt) (nth$ 2 ?lt) (nth$ 3 ?lt)))
CLIPS>
(deffunction is-leap-year (?year)
(if (= (mod ?year 400) 0) then (return TRUE))
(if (= (mod ?year 100) 0) then (return FALSE))
(if (= (mod ?year 4) 0) then (return TRUE))
(return FALSE))
CLIPS>
(defglobal ?*days-before-month* = (create$ 0 31 59 90 120 151 181 212 243 273 304 334))
CLIPS> (defglobal ?*days-before-month-leap-year* = (create$ 0 31 60 91 121 152 182 213 244 274 305 335))
CLIPS>
(deffunction days-from-year-begin (?date)
(bind ?year (string-to-field (sub-string 1 4 ?date)))
(bind ?month (string-to-field (sub-string 6 7 ?date)))
(bind ?day (string-to-field (sub-string 9 10 ?date)))
(if (is-leap-year ?year)
then
(return (+ (nth$ ?month ?*days-before-month-leap-year*) ?day))
else
(return (+ (nth$ ?month ?*days-before-month*) ?day))))
CLIPS>
(deffunction days-until-year-end (?date)
(bind ?year (string-to-field (sub-string 1 4 ?date)))
(bind ?month (string-to-field (sub-string 6 7 ?date)))
(bind ?day (string-to-field (sub-string 9 10 ?date)))
(if (is-leap-year ?year)
then
(return (- 366 (+ (nth$ ?month ?*days-before-month-leap-year*) ?day)))
else
(return (- 365 (+ (nth$ ?month ?*days-before-month*) ?day)))))
CLIPS>
(deffunction date-days-diff (?date1 ?date2)
(bind ?year1 (string-to-field (sub-string 1 4 ?date1)))
(bind ?year2 (string-to-field (sub-string 1 4 ?date2)))
(if (= ?year1 ?year2)
then
(return (- (days-from-year-begin ?date1) (days-from-year-begin ?date2))))
(if (> ?year1 ?year2)
then
(bind ?negate FALSE)
else
(bind ?negate TRUE)
(bind ?temp ?date1)
(bind ?date1 ?date2)
(bind ?date2 ?temp)
(bind ?temp ?year1)
(bind ?year1 ?year2)
(bind ?year2 ?temp))
(bind ?day-count (+ (days-until-year-end ?date2) (days-from-year-begin ?date1)))
(loop-for-count (?year (+ ?year2 1) (- ?year1 1)) do
(if (is-leap-year ?year)
then (bind ?day-count (+ ?day-count 366))
else (bind ?day-count (+ ?day-count 365))))
(if ?negate
then
(return (- 0 ?day-count))
else
(return ?day-count)))
CLIPS>
(deffunction date-years-diff (?date1 ?date2)
(bind ?year1 (string-to-field (sub-string 1 4 ?date1)))
(bind ?year2 (string-to-field (sub-string 1 4 ?date2)))
(if (= ?year1 ?year2)
then
(return 0))
(if (> ?year1 ?year2)
then
(bind ?negate FALSE)
else
(bind ?negate TRUE)
(bind ?temp ?date1)
(bind ?date1 ?date2)
(bind ?date2 ?temp))
(bind ?year1 (string-to-field (sub-string 1 4 ?date1)))
(bind ?year2 (string-to-field (sub-string 1 4 ?date2)))
(bind ?month1 (string-to-field (sub-string 6 7 ?date1)))
(bind ?month2 (string-to-field (sub-string 6 7 ?date2)))
(bind ?day1 (string-to-field (sub-string 9 10 ?date1)))
(bind ?day2 (string-to-field (sub-string 9 10 ?date2)))
(bind ?years (- ?year1 ?year2))
(if (= ?month1 ?month2)
then
(if (< ?day1 ?day2)
then
(bind ?years (- ?years 1)))
else
(if (< ?month1 ?month2)
then
(bind ?years (- ?years 1))))
(if ?negate
then (return (- 0 ?years))
else (return ?years)))
CLIPS>
(deftemplate driver
(slot name)
(slot dateBorn))
CLIPS>
(deffacts drivers
(driver (name "Daniel Silva") (dateBorn "1985-03-04"))
(driver (name "Carlos Santos") (dateBorn "2000-03-04")))
CLIPS>
(defrule cantDrive
(driver (name ?name) (dateBorn ?born))
(test (< (date-years-diff (current-date) ?born) 25))
=>
(printout t ?name " is younger than 25" crlf))
CLIPS> (reset)
CLIPS> (run)
Carlos Santos is younger than 25
CLIPS>

Related

Which function for adding one to every element of a list is faster?

I'm wondering which of the following function is less computational complex.
The background: I have a trigger signal and that signal triggers a rule that invokes my function which adds a 1 to every element of a certain list. Because that trigger signal is send quite frequently I'm interested which of the following functions I should prefer.
Function 1:
;?lon = list of numbers
(deffunction add-one-to-list-of-numbers (?lon)
(progn$ (?field ?lon)
(bind ?lon (replace$ ?lon ?field-index ?field-index (+ ?field 1)))
)
(return ?lon)
)
Function 2:
;?lon = list of numbers
;?cnt = counter
(deffunction add-one-to-list-of-numbers-alt (?lon)
(loop-for-count (?cnt (length ?lon))
(bind ?lon (replace$ ?lon ?cnt ?cnt (+(nth$ ?cnt ?lon) 1)))
)
(return ?lon)
)
I'd suggest testing empirically:
CLIPS (6.31 2/3/18)
CLIPS>
(deffunction add-one-to-list-of-numbers-1 (?lon)
(progn$ (?field ?lon)
(bind ?lon (replace$ ?lon ?field-index ?field-index (+ ?field 1))))
(return ?lon))
CLIPS>
(deffunction add-one-to-list-of-numbers-2 (?lon)
(loop-for-count (?cnt (length$ ?lon))
(bind ?lon (replace$ ?lon ?cnt ?cnt (+(nth$ ?cnt ?lon) 1))))
(return ?lon))
CLIPS>
(deffunction add-one-to-list-of-numbers-3 (?lon)
(bind ?rv (create$))
(progn$ (?field ?lon)
(bind ?rv (create$ ?rv (+ ?field 1))))
?rv)
CLIPS>
(timer (bind ?numbers (create$ 1 2 3 4 5 6 7 8 9))
(loop-for-count 1000000 (add-one-to-list-of-numbers-1 ?numbers)))
7.51635100000021
CLIPS> (release-mem)
13499
CLIPS>
(timer (bind ?numbers (create$ 1 2 3 4 5 6 7 8 9))
(loop-for-count 1000000 (add-one-to-list-of-numbers-2 ?numbers)))
9.28229099999953
CLIPS> (release-mem)
3771
CLIPS>
(timer (bind ?numbers (create$ 1 2 3 4 5 6 7 8 9))
(loop-for-count 1000000 (add-one-to-list-of-numbers-3 ?numbers)))
6.42367899999954
CLIPS>

cannot update the object in CLIPS, getting compile time error in clips

(bind ?existing_total_count (nth$ 2 (send ?INSTANCE ?get-INTS)))
(send (nth$ 2 (send ?INSTANCE put-INTS)) (+ ?total_count ?existing_total_count))
first line compiles fine, but second line throwing error
Function send expected argument #2 to be of type symbol
I cant findout what the issue is. I am trying to update the second entry in slot INTS.
CLIPS>
(defclass A
(is-a USER)
(multislot INTS))
CLIPS> (make-instance [a] of A (INTS 1 2 3))
[a]
CLIPS> (send [a] print)
[a] of A
(INTS 1 2 3)
CLIPS> (bind ?INSTANCE [a])
[a]
CLIPS> (bind ?existing_total_count (nth$ 2 (send ?INSTANCE get-INTS)))
2
CLIPS> (bind ?total_count 3)
3
CLIPS> (slot-replace$ ?INSTANCE INTS 2 2 (+ ?total_count ?existing_total_count))
(1 5 3)
CLIPS> (send [a] print)
[a] of A
(INTS 1 5 3)
CLIPS> (bind ?total_count 5)
5
CLIPS> (send ?INSTANCE put-INTS (replace$ (send ?INSTANCE get-INTS) 2 2 (+ ?total_count ?existing_total_count)))
(1 7 3)
CLIPS> (send [a] print)
[a] of A
(INTS 1 7 3)
CLIPS>

CLIPS defrule checking if multiple sides of a box are taken

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>

Incrementing a global variable within a user answered defrule

I'm trying to increment a defglobal variable (symcount) by 1 if the user defines that they have pain by using the (read) function
(defrule QPain
(initial-fact)
=>
(printout t "Are You In Pain? " crlf)
(bind (ans Answer) (read))
)
(defrule AnsInc
(Answ Answer = "y")
=>
(bind ?*symcount* (+ ?*symcount* 1)))
the increment must only happen of the user presses "y"
otherwise the increment must not happen.
CLIPS> (defglobal ?*symcount* = 0)
CLIPS>
(defrule QPain
=>
(printout t "Are You In Pain? ")
(bind ?answer (read))
(if (eq ?answer y)
then
(bind ?*symcount* (+ ?*symcount* 1))))
CLIPS> (reset)
CLIPS> (run)
Are You In Pain? y
CLIPS> ?*symcount*
1
CLIPS> (reset)
CLIPS> (run)
Are You In Pain? n
CLIPS> ?*symcount*
0
CLIPS>

Clips - print a list of numbers in pyramid

I am trying to print my 1234 list like:
1
12
123
1234
Here is my code:
(deffacts lists
(list 1 2 3 4)
)
(defrule print
(list $?x ? $?)
=>
(printout t ?x )
)
I'm not sure exactly how I should continue...
CLIPS>
(deffacts lists
(list 1 2 3 4))
CLIPS>
(deffunction pyramid-print (?list)
(loop-for-count (?i (length$ ?list))
(printout t (implode$ (subseq$ ?list 1 ?i)) crlf)))
CLIPS>
(defrule print
(list $?x)
=>
(pyramid-print ?x))
CLIPS> (reset)
CLIPS> (run)
1
1 2
1 2 3
1 2 3 4
CLIPS>

Resources