Animal Guessing Game is a toy problem in Artificial Intelligence and is one of the best introductory projects for programmers who want to learn AI. I am taking Artificial Intelligence course for my fifth semester and this is my first AI project which I wrote in Kotlin.

Animal Guessing Game as the name suggests the guesses the animal that the player is thinking of. Here is how the game goes:

Description

First, the Animal Guessing Game agent asks the player to think of an animal. Then the agent will ask a series of Yes/No questions to narrow down its guess. When the agent runs out of questions to ask, it makes a guess and asks if it got is correct.

If the agent got the answer correct, it wins. Otherwise, the agent asks the player what it was thinking of and also asks what question should the agent had asked to make the correct guess. In other words, the agent learns from the player so that it can make a better guess next time.

Demo Snapshot

Here is a snapshot of a run in the game.

Kotlin Code

A snapshot of a run in Animal Guessing Game

Understanding The Problem

Here, it is very important to understand the problem. This is a simple binary problem i.e. ask the player a Yes/No question; if the answer is ‘Yes’ go to this question; if the answer is ‘No’ go to that question. Hence, it is best implemented in a Binary Search Tree which I have used.

Implementation

Binary Search Tree

Create a simple Binary search tree [Wiki] with Nodes containing data (A Yes/No question or an animal name) and two children nodes. The binary search tree consists of a function to insert a child node which the agent uses to store new information.

class Node ( /* Node data structure for binary search tree */
        var data: String, 
        var leftChild: Node? = null, 
        var rightChild: Node? = null
)

class BinarySearchTree {

    var rootNode = Node("") /* Initialize root node */
    var currentNode = Node("") /* A pointer variable to the current node*/

    /* Initialize the Binary Search Tree with initial data */
    fun initialize(data: String, yesAnswer: String, noAnswer: String) {
        rootNode = Node(data, Node(noAnswer), Node(yesAnswer)) /* Create new node and set it as root node */
    }

    /* Function to insert children node for left and right node of current node */
    fun insertChildren(currentNode: Node, data: String, userAnswer: String) {
        currentNode.leftChild = Node(currentNode.data) /* Shift the current answer to a new left node */
        currentNode.rightChild = Node(userAnswer) /* Store the user answer */
        currentNode.data = data /* Replace answer with new hint given by user */
    }
}

Animal Guessing Game Agent

Next, create the Animal Guessing Game Agent. It will contain the following functions:

  • A function to start the game. Here, we will initialize the game with a question and two answers (one for a ‘Yes’ answer and the other for a ‘No’ answer). In the above run, the initial question is ‘Can it fly?’ and the answer is Pigeon for ‘Yes’ and Elephant for ‘No’.
  • A function to play the game. This function whenever called points to the topmost node and traverses down. It contains a loop that iterates until a node is found null. Then it exits the loop and makes a guess.
  • A function to learn new information. This is what makes the agent intelligent. It asks the player the name of the animal that the player was thinking of and also a hint related to that animal. Then, the agent inserts the new information into its binary search tree.
class AnimalGuessingAgent {

    var BST = BinarySearchTree() /* Initialze binary search tree */
    var counter: Int = 0 /* Initialize counter to calculate number of steps */

    fun start() {
        println ("Think of an animal. I will try to guess which animal you are thinking of.")
        println ("I will ask some questions. Please, answer with a YES or NO.")

        BST.initialize("Can it fly?", "Pigeon", "Elephant")
        play()
    }

    private fun play() {

        BST.currentNode = BST.rootNode /* Each time the user plays the game, the pointer points to the initial node */
        counter = 0 /* Set counter to zero for new game */

        while (BST.currentNode.leftChild != null || BST.currentNode.rightChild != null){ /*  Traverse until a child node is null. */
            askQuestion()
            var userResponse: String = readLine()!!
            when (userResponse.toUpperCase()) {
                "YES" -> BST.currentNode = BST.currentNode.rightChild!! /* Traverse right */
                "NO" -> BST.currentNode = BST.currentNode.leftChild!! /* Traverse left */
                else -> println("My creator only designed me to understand a YES or NO. 🙁 ")
            }
            counter++ /* Increment counter */
        }
            guess() /* Make a guess */
    }

    private fun learn() {

        println ("Ok. I give up. What is it?")
        var userAnswer: String = readLine()!!

        println ("What question should have I asked?")
        var newHint: String = readLine()!!

        BST.insertChild(BST.currentNode, newHint, userAnswer) /* Insert newly learned information */
        println("Ok. Got it.")
        replay()
    }
}

You can find the full executable code here.