[Haskell-cafe] Re: [Haskell] Animal guessing game - critique my code

David Roundy droundy at darcs.net
Sat Oct 28 22:45:34 EDT 2006

I haven't made anything more elegant, but have a few improvements that I
found made it more fun to play (and yes, I'm rather zoned out after
spending most of the day solving electromagnetism problems).  It's nice
(and quite easy) to save the tree, so that the game is more exciting every
time you play.  With a bit of work and integration with darcs, you could
probably interface with a public server, so the guesser can be
collaboratively improved by people who have too much time on their hands
or are too tired to do anything productive.

Here's the improved code:

module Main where

import Data.Char ( isUpper )

data GuessTree = Answer String
               | GuessTreeQuestion {guessTreeQuestion :: String,
                                    guessTreeYes ::GuessTree}
                 deriving (Show, Read)

askQuestion :: String -> IO Bool
askQuestion str = do
  putStrLn str
  response <- getLine
  return $ isYes response
  where isYes ('y':xs) = True
        isYes _ = False

a :: String -> String
a s@(c:_) | isUpper c = s
          | c `elem` "aeiou" = "an " ++ s
          | otherwise = "a " ++ s
a "" = "nonexistent"

runTree :: GuessTree -> IO GuessTree
runTree (Answer name) = do
  response <- askQuestion $ "Is it " ++ a name ++ "?"
  if response
    then do putStrLn "Ha!"
            return $ Answer name
    else do putStrLn "What is it?"
            animal <- getLine
            putStrLn $ "Enter a question to help distinguish between " ++
                      a name ++ " and " ++ a animal
            question <- getLine
            a <- askQuestion $ "Is the answer yes for " ++ animal ++ "?"
            (if a then id else flip) (\n y ->
                                      return $ GuessTreeQuestion {guessTreeQuestion = question,
                                                                  guessTreeNo = n,
                                                                  guessTreeYes = y}) (Answer animal)
                        (Answer name)
runTree GuessTreeQuestion {guessTreeQuestion = ques,
                           guessTreeYes = yesTree,
                           guessTreeNo = noTree} = do
  response <- askQuestion $ ques
  if response
    then do a <- runTree noTree
            return $ GuessTreeQuestion {guessTreeQuestion = ques,
                                        guessTreeNo = a,
                                        guessTreeYes = yesTree}
    else do a <- runTree yesTree
            return $ GuessTreeQuestion
                       {guessTreeQuestion = ques,
                        guessTreeNo = noTree,
                        guessTreeYes = a}

run :: GuessTree -> IO GuessTree
run tree = do
  putStrLn "Think of an animal and press enter."
  a <- runTree tree
  writeFile "animaldata" $ show a
  r <- askQuestion "Play again?"
  if r then run a else return a

read_animal :: IO GuessTree
read_animal = (read `fmap` readFile "animaldata") `catch`
              \_ -> return $ Answer "bear"

main = read_animal >>= run

As you can see, I also modified it so that it'll be a bit smarter about not
saying things like "a David Roundy" or "a elephant".
David Roundy
Dept. of Physics
Oregon State University

More information about the Haskell-Cafe mailing list