Me and my friends play a variant of Morra when drinking. The rules are as follows:
The players sit in a circle and hold one of their hands in front of them.
One player guesses a number. All the players (including this one) then hold out between 0 and 5 fingers on their hand.
If there are as many fingers held out as the player guessed, he is "saved", leaves the circle and doesn't have to play anymore. Next turn another player tries to guess (otherwise the game just goes on with the player staying in the circle).
The last player to remain in the circle has to drink.
I'm just learning about monad transformers, so I thought it would be nice to implement this simple game.
module Morra where
import Control.Monad.Trans.State
import Control.Monad.Trans
import Control.Monad
import Data.List
import Data.Char
main :: IO ()
main = do
putStrLn "Welcome to Morra. The last to remain has to drink."
putStr "Please input the names of players: "
psraw <- getLine
putStrLn ""
([p], n) <- execStateT (play 0) (words psraw, 0)
putStrLn $ p ++ " has lost, after " ++ show n ++ " rounds. (S)he has to take a shot."
play :: Int -> StateT ([String], Int) IO ()
play p = do
(ps, n) <- get
let currp = ps !! p
if length ps == 1
then put (ps, n)
else do
liftIO $ putStr $ "It's now " ++ currp ++ "'s turn. How many fingers will be help up? "
a <- liftIO safeGetLine
putNewLine
liftIO $ putStrLn "[INPUT PHASE] Each player now chooses, how many fingers does (s)he hold up."
fs <- liftIO $ getFingers (length ps)
liftIO $ putStr "[END OF INPUT PHASE]"
putNewLine
if sum fs == a
then do
liftIO $ putStrLn $ currp ++ " has guessed right, he's out."
putNewLine
let newps = delete currp ps
put (newps, n + 1)
play ((p + 1) `mod` length newps)
else do
liftIO $ putStrLn $ currp ++ " hasn't guessed right, the game goes on!"
putNewLine
put (ps, n + 1)
play ((p + 1) `mod` length ps)
where
putNewLine = liftIO $ putStrLn ""
getFingers :: Int -> IO [Int]
getFingers n = replicateM n helper
where
helper = do
putStr "Input the number of fingers you hold up: "
fs <- safeGetLine
putStrLn "Pass the computer to the next player now. [ENTER]"
getLine
return fs
safeGetLine :: IO Int
safeGetLine = do
x <- getLine
if all isNumber x
then return $ read x
else do
putStrLn "Please input a number"
safeGetLine
Firstly, you can see the strings I print are pretty long, longer than the 80 chars limit. What is the common approach to tackle these and keep all the lines below 80 chars?
And secondly, please point out anything that could be made more elegant, whatever that might be (even changing the underlaying StateT). I'm also open to new ideas and challenges.