;;;=====================================================================================================
;;; Program Name: chatterbot4
;;; Description: the goal of the current version of this program is to extend the previous chatterbot,
;;; actualy to make it more flexible in the way it treats the inputs from the different users.
;;; Also this version will try controling repetions that are made by the user.
;;;
;;; Author: Gonzales Cenelia
;;; Date: 29 july 2009
;;;=====================================================================================================

(defparameter *knowledge-base* 
  '(((WHAT IS YOUR NAME)
      (MY NAME IS CHATTERBOT4.)
      (YOU CAN CALL ME CHATTERBOT4.)
      (WHY DO YOU WANT TO KNOW MY NAME?))

    ((HI)
      (HI THERE!)
      (HOW ARE YOU?)
      (HI!))
	
    ((HOW ARE YOU)
      (I'M DOING FINE!)
      (I'M DOING WELL AND YOU?)
      (WHY DO YOU WANT TO KNOW HOW AM I DOING?))

    ((WHO ARE YOU)
      (I'M AN A.I PROGRAM.)
      (I THINK THAT YOU KNOW WHO I'M.)
      (WHY ARE YOU ASKING?))

    ((ARE YOU INTELLIGENT)
      (YES OFCORSE.)
      (WHAT DO YOU THINK?)
      (ACTUALY I'M VERY INTELLIGENT!))

    ((ARE YOU REAL)
      (DOES THAT QUESTION REALLY MATERS TO YOU?)
      (WHAT DO YOU MEAN BY THAT?)
      (I'M AS REAL AS I CAN BE.))))


(defun chatterbot4()
  (let* ((input nil)
         (response nil)
         (prev-response nil)
         (prev-input nil))
    (loop
     (princ '>)
     (setf prev-input input)
     (setf input (read-line))
     (setf input (preprocess-input input))
     (setf response-list (find-match input *knowledge-base*))
     (cond ((equal input '(bye))
            (format t "~%IT WAS NICE TALKING TO YOU USER, SEE YOU NEXT TIME!~%")
            (return))
           ((and (> (length input) 0) (equal input prev-input))
            (format t "~%YOU ARE REPEATING YOURSELF.~%"))
           ((null response-list)
            (format t "~%I'M NOT SURE IF I UNDERSTAND WHAT YOU ARE TALKING ABOUT.~%"))
           (t (setf prev-response response)
              (setf response (random-elt response-list))
              (cond ((equal response prev-response)
                  (setf response-list (remove response response-list))
                (setf response (random-elt response-list))))
              (format t "~%~{~a ~}~%" response))))))

(defun find-match(input database)
  "searchs for the user's input inside the corresponding database"
  (if (null database)
      nil
    ;;there has been some improvements made in
    ;;here in order to make the matching process
    ;;a littlebit more flexible"
    (if (match input (first (first database)))
        (rest (first database))
      (find-match input (rest database)))))

(defun match(input keyword)
"test if the current keyword matches the user's input"
(cond ((null keyword) nil)
      ((null input) nil)
      ((equal input keyword) T)
      ((and (equal (first keyword) (first input)) (= (length keyword) 1)) T)
      ((equal (first keyword) (first input))
       (match (rest input) (rest keyword)))
      (t (match (rest input) keyword))))
      
(defun random-elt(L)
  "selects an element from a list randomly"
  (nth (random (length L)) L))

(defun preprocess-input(input)
  (convert-to-list (strip-puncs input)))

(defun is-punc(C)
  (find C ",?!.;"))

(defun strip-puncs(S)
  (substitute-if #\Space #'is-punc S))

(defun convert-to-list(S)
  (let* ((stream (make-string-input-stream S))
         (next-tok nil)
         (result nil))
    (loop 
     (setf next-tok (read stream nil 'eos))
     (if (equal next-tok 'eos)
         (return result)
       (setf result (append result (list next-tok)))))))