I've been learning Haskell, and a while ago I created this Hangman game. I've been working on using a library for terminal output, so no more hardcoded escape codes, but in the meantime I'd like some comments on what I have until now.
Is this readable, correct Haskell code with no obvious performance problems?
The code depends on a file being present named after the language (EN or NL) containing linebreak-separated words that is easily created.
import System.Random (randomRIO)
import Data.Char (isAlpha, toUpper)
import Data.List (intersperse)
import System.IO (hSetBuffering, stdin, BufferMode (NoBuffering) )
lang = EN
main :: IO ()
main = do hSetBuffering stdin NoBuffering
f <- readFile $ show lang
startplaying $ lines f
startplaying :: [String] -> IO ()
startplaying words = do index <- randomRIO (0,length words - 1)
playgame (words !! index) []
putStrLn $ strings lang Another
ans <- getChar
case ans of
'n' -> return ()
_ -> startplaying words
playgame :: String -> [Char] -> IO ()
playgame word guessed
| complete = printState word guessed Won ""
| guessedwrong word guessed >= length hangman -1 = printState word guessed Lost word
| otherwise = do printState word guessed Pick ""
l <- fmap toUpper getChar
let guessed' | not (isAlpha l) = guessed
| l `elem` guessed = guessed
| otherwise = l : guessed
playgame word guessed'
where complete :: Bool
complete = all (`elem` guessed) (map toUpper word)
guessedwrong :: String -> [Char] -> Int
guessedwrong word guessed = length $ filter (`notElem` map toUpper word) guessed
printState :: String -> [Char] -> Message -> String -> IO ()
printState word guessed message string = putStrLn $ "\ESC[2J" ++
unlines [ hangman !! (guessedwrong word guessed)
, map (\x -> if (elem (toUpper x) guessed) then x else '_') word
, (strings lang Used) ++ intersperse ' ' guessed
, strings lang message ++ string
]
strings :: Language -> Message -> String
strings NL m = case m of
Another -> "Wil je nog een keer spelen? [Y/n]"
Won -> "Gefeliciteerd! Je hebt het woord geraden!"
Lost -> "Je bent dood. Het woord was "
Pick -> "Kies een letter"
Used -> "Gebruikte letters: "
strings EN m = case m of
Another -> "Play another game? [Y/n]"
Won -> "Congratulations! You got it!"
Lost -> "You're dead. The word was "
Pick -> "Pick a letter"
Used -> "Used letters: "
data Message = Another | Won | Lost | Pick | Used
data Language = NL
| EN
deriving (Show)
hangman = [ unlines [ ""
, ""
, ""
, ""
, ""
, ""
, ""
, ""
]
, unlines [ ""
, ""
, ""
, ""
, ""
, ""
, ""
, " ___"
]
, unlines [ ""
, "|"
, "|"
, "|"
, "|"
, "|"
, "|"
, "|___"
]
, unlines [ "_________"
, "|"
, "|"
, "|"
, "|"
, "|"
, "|"
, "|___"
]
, unlines [ "_________"
, "|/"
, "|"
, "|"
, "|"
, "|"
, "|"
, "|___"
]
, unlines [ "_________"
, "|/ |"
, "|"
, "|"
, "|"
, "|"
, "|"
, "|___"
]
, unlines [ "_________"
, "|/ |"
, "| (_)"
, "|"
, "|"
, "|"
, "|"
, "|___"
]
, unlines [ "_________"
, "|/ |"
, "| (_)"
, "| |"
, "| |"
, "|"
, "|"
, "|___"
]
, unlines [ "_________"
, "|/ |"
, "| (_)"
, "| /|"
, "| |"
, "|"
, "|"
, "|___"
]
, unlines [ "_________"
, "|/ |"
, "| (_)"
, "| /|\\"
, "| |"
, "|"
, "|"
, "|___"
]
, unlines [ "_________"
, "|/ |"
, "| (_)"
, "| /|\\"
, "| |"
, "| /"
, "|"
, "|___"
]
, unlines [ "_________"
, "|/ |"
, "| (_)"
, "| /|\\"
, "| |"
, "| / \\"
, "|"
, "|___"
]
]
startplaying words = do index <- randomRIO (0,length words)
should bestartplaying words = do index <- randomRIO (0,length words - 1)
so you don't go off the end of the list. \$\endgroup\$getChar
bug in ghc under windows (but is OK in WinHugs, and the fix causes a bug in WinHugs). This might not matter to you. See this stack overflow question \$\endgroup\$