> 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!"