Animals

Animals builds a decision-tree that guesses an animal. When it fails to guess yours, it asks for a question that might divide the search space.

I coded Animals in icon which was a challenge to use its sense of expression success or failure to control flow.

Of course the behavior of the program depends on the quality of questions offered by users. I programmed in the first few questions to divide the space:

node ("Does it swim",
    node ("a trout"),
    node ("Can it fly",
        node ("an eagle"),
        node ("a cat")))

My brother's girlfriend Teresa thought she would surely stump the program when she chose pterodactyl. She knew she'd lost when it asked, is it hard to spell?

The program stored the tree of choices as nodes, either branches that asked about a characteristic, or leaves that asked about a specific animal. The subtrees of a branch either had or didn't have the specific characteristic.

Nodes were written recursively to a flat-file database. When reading, I had only to distinguish leaves from branches. When I read a branch, I read (recursively) two more nodes, one for "yes" and one for "no".

Try recursing through this sample data:

Does it swim
Is it warm blooded
Is it really big
a whale
a seal
Is it very small
an amoeba
Does it have legs
a frog
a trout
Can it fly
Is it furry
Does it have bones
a bat
Will it sting
a bee
a moth
Is it endangered
an eagle
Would it live in the city
Will it sing
a parrot
a pigeon
a hawk
Has it four legs
Is it really big
Does it have spots
a giraffe
an elephant
Is it a domestic animal
Do you have to walk it
a dog
a cat
Does it eat plants
Is it magical
an unicorn
a bear
Will it eat people
a tiger
Does it live on a farm
a pig
Is it striped
a skunk
a fox
It has more than four
a spider
Does it stand erect
a man
Does it leave a trail
a slug
a snake

I read the database with the following icon code. The read_node procedure reads one line, establishes it as the match subject, then, based on matching, either creates a leaf node with that subject or creates a branch node with the subject and two more nodes.

procedure read_database ()
    if file := open (filename, "r") then {
        root := read_node ()
        close (file)
        return \root
    }
end

procedure read_node ()
    return read (file) ? {
        (match ("a " | "an ") & node (&subject)) |
        node (&subject, read_node (), read_node ())
    }
end