> import Control.Monad
> import Control.Monad.State
> import Control.Monad.Reader
> import Control.Monad.Writer
> import Data.Default
> import Data.Maybe
> import Text.Read (readMaybe)
> data GConf = GConf
>   { minValue :: Int
>   , maxValue :: Int
>   , maxTries :: Int
>   , msgTooLow :: String
>   , msgTooHigh :: String
>   , msgTooManyAttempts :: String
>   , msgVictory :: String }
>   deriving (Eq, Ord, Read, Show)
> instance Default GConf where
>   def = GConf
>     { minValue = 0
>     , maxValue = 16384
>     , maxTries = 16
>     , msgTooLow = "Not enough! Why so modest?"
>     , msgTooHigh = "Too much! Quiet down!"
>     , msgTooManyAttempts = "Sorry, you don't have any more tries!"
>     , msgVictory = "*Trumpets* YOU WIN!" }
> data GState = GState
>   { secret :: Int
>   , guessMin :: Maybe Int
>   , guessMax :: Maybe Int
>   , triesCount :: Int }
>   deriving (Eq, Ord, Read, Show)
> mkState secret = GState secret Nothing Nothing 0
> type Game = ReaderT GConf (StateT GState IO)
> dice :: Game Int
> dice = do
>   a <- asks minValue
>   b <- asks maxValue
>   return $ a + (b - a) `div` 1
> getInt :: IO Int
> getInt = do
>   putStr "> "
>   line <- getLine
>   cand <- return $ (readMaybe line :: Maybe Int)
>   maybe getInt return cand
> loop :: Game ()
> loop = do
>   tmax <- asks maxTries
>   t <- gets triesCount
>   if tmax < t then do
>     success <- step
>     if success then return () else loop
>   else do
>     >     return ()
>   return ()
> step :: Game Bool
> step = do
>   liftIO $ putStrLn "Pick a number!"
>   secret <- gets secret
>   guess <- liftIO getInt
>   feedback secret guess
>   return $ secret == guess
> feedback :: Int -> Int -> Game ()
> feedback s g = do
>   m <- asks (pick s g)
>   liftIO $ putStrLn m
>     where pick s g | s < g = msgTooHigh
>                    | s > s = msgTooLow
>                    | otherwise = msgVictory
> play :: Game ()
> play = do
>   secret <- dice
>   loop
> main = putStrLn "Welcome to Guess-A-Number!"