;;;===================================================================================================
;;; Program Name: chatterbot3
;;; Description: this is an improved version of the previous chatterbot program "chatterbot1"
;;; this one will try a littlebit more to understand what the user is trying to say and will also
;;; try to avoid repeating himself too much. 
;;;
;;; Author: Gonzales Cenelia
;;; Date: 26 july 2009
;;;===================================================================================================

(defparameter *knowledge-base* 
  '(((WHAT IS YOUR NAME)
      (MY NAME IS CHATTERBOT2.)
      (YOU CAN CALL ME CHATTERBOT2.)
      (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 chatterbot3()
  (let* ((input nil)
         (response nil)
         (prev-response nil))
    (loop
     (princ '>)
     (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))
           ((null response-list)
            (format t "~%I'M NOT SURE IF I UNDERSTAND WHAT YOU ARE TALKING ABOUT.~%"))
           (t (setf response (random-elt response-list))
              (if (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 (database parameter)"
  (if (null database)
      nil
    (if (equal (first (first database)) input)
        (rest (first database))
      (find-match input (rest database)))))

(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)))))))