Animals Program in LISP
The Animals program in LISP already used in many programming education sites because it’s quite fun and easy to implement, and shows how a computer can interactively create a database with the request from the user. It can be written quite easily in Lisp by taking advantage of list handling.
Description of Animals Program in LISP
The description of Animal program in lisp is as follows; The Animals game tries to predict the animal you are dreaming of. To start the game you simply type:
CL-USER > (play-animals)
The program then asks a series of questions, such as:
to which you answer Yes or No as needed. Gradually it makes a guess of the animal:
If it was true the game will be ended. However, No was clicked program will ask:
You will be prompt to add a question to the tree:
Finally the program needs to know the correct answer for the new animal:
As a result of this doing new animal will be in the database, and in the next plays the program will already know about it.
Designing the Animal Program in LISP
We will represent the database as a tree of questions and animals. The animal program in lisp starts with one question and two animals:
We’ll represent the tree as a Lisp list, of the form:
(question yes-answer no-answer)
So for the tree shown above the list will be:
("Is it living in the sea?" "a dolphin" "a horse")
We will store the database tree in a variable called *tree*, so we’ll start by defining:
(defparameter animals-tree '("Is it living in the sea?" "a dolphin" "a horse"))
After it has passed training with 2 next animals whole tree probably will look like below:
Below located Lisp representation, needed to make whole structure clearer:
("Does it live in the sea?" "a dolphin" ("Does it have 4 legs?" "a horse" ("Can it fly?" "a raven" "a human")))
We’re going to build the animals program in LISP out of four different functions: make-assumption, learn-animal, animals, and ask-question. We could have combined some of these functions but the advantage of breaking it down into small and separate building-blocks needed for independent testing.
Learning a new animal
First let’s define the procedure to learn a new animal:
(defun learn-animal (animal new-animal) ;;first menu here (let ((question (capi:prompt-for-string ;; first answer (format nil "yes/no between ~a and ~a" new-animal animal)))) ;; second menu here (if ;;look again (capi:prompt-for-confirmation ;;our second answer here (format nil "the answer be for ~a?" new-animal)) (list question animal new-animal)) ;;final (list question new-animal animal)))
This takes two parameters: a new animal and an already existing one. It prompts for a question, and the correct answer for the new animal, and returns a list of the question and answers in the correct order. For example:
(learn-animal "a cat" "a dog")
will prompt:
Yes/no between a dog and a cat
and
What would the answer be for a dog?
and return the appropriate list; for example
("Does it bark?" "a dog" "a cat")
Making an assumption
The following procedure, make-assumption, is needed when we’ve got to an animal on the tree. It takes our animal as its parameter, and predicts that as the answer. If it’s right it simply states the animal. If it’s wrong, it calls learn-animal to make a list which have a new question and the 2 answers.
(defun make-assumption (animal) ;;start of it (if (capi:prompt-for-confirmation (format nil "It's ~a?" animal)) ;;displaying message (let ((message (capi:display-message "Hurray!"))) animal) ;;final (learn-animal animal (capi:prompt-for-string "About what are you thinking?"))))
We have changed text a bit to differ it from what’s on images, but do not worry, you can create your own versions of this.
Asking a question
ask-question is called at the moment when we’re at a question point on our tree. It prompts with the question and depending on whether the answer is yes or no it calls animals on the left branch or right branch of the tree respectively.
(defun ask-question (tree) ;;start of it (if (capi:prompt-for-confirmation (first tree)) ;;middle stage of tree (list (first tree) ;;building (animals (second tree)) (third tree)) ;;final stage of all questions (list (first tree) ;;building again with a bit different order (second tree) (animals (third tree)))))
It returns the complete tree, modified to add a new animal if one was added.
The main procedure – animals
Finally here’s the main procedure, animals. It is called with the current position on the tree as the parameter, and simply calls ask-question and if parameter appears a list, stating that more questions need to be asked, or make-guess needed if it’s an animal, stating that we’re at the bottom of our branch.
(defun animals (tree) (if (listp tree) (ask-question tree) (make-assumption tree)))
To save the modified tree we need to save it back as *tree*. Here’s a routine play-animals that does that for us:
(defun play-animals () (setf animals-tree (animals animals-tree)))
Saving the tree
Note that the value of *tree* should be saved to disk if you want to preserve the results of training your database with new animals. Here’s a changed variant of play-animals that does that, saving the data in a file called tree at the top level of your hard disk. Note that this uses functions that we don’t explain in these tutorials so you’ll have to take them on trust:
(defun play-animals () (when (probe-file "tree") ;;it's all starts here (with-open-file (stream "tree" :direction :input) (setq animals-tree (read stream)))) ;;then we add what needed more below (setq animals-tree (animals animals-tree)) ;;and a bit more (with-open-file ;;very important string here (stream "tree" ;;take a look in lisp help :direction :output :if-exists :supersede) ;;final output (write animals-tree :stream stream)))