patternMinor
Small Haskell Hangman game
Viewed 0 times
hangmangamesmallhaskell
Problem
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.
``
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
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 IO ()
startplaying words = do index 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 [Char] -> Int
guessedwrong word guessed = length $ filter (notElem` map toUpper word) guessedprintState :: 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
Solution
Why do you have so much redundant data in
By eliminating all redundancy as well as replacing the explicit state variable by control flow, the
Admittedly, this does quite a bit of re-computation, especially on the "duds". Yet we can probably expect both words and game lengths to be small, therefore it is better to go with a more compact program.
State? That just means you have to put a lot of effort into keeping everything up-to-date - which doesn't just make your program longer, but is also prone to mistakes.By eliminating all redundancy as well as replacing the explicit state variable by control flow, the
playgame function can be simplified down to pretty much the following:count_duds :: String -> [Char] -> Int
count_duds word guessed = length $ filter (`notElem` map toUpper word) guessed
playgame :: String -> [Char] -> IO ()
playgame word guessed
| all (`elem` guessed) (map toUpper word) = putStrLn $ header "Won"
| count_duds word guessed + 1 >= length hangman = putStrLn $ header "Lost" ++ word
| otherwise = do putStrLn $ header "Pick"
l <- fmap toUpper getChar
let guessed' | not (isAlpha l) = guessed
| l `elem` guessed = guessed
| otherwise = l:guessed
playgame word guessed'
where header msg = formatState word guessed ++ "\n" ++ strings lang msgAdmittedly, this does quite a bit of re-computation, especially on the "duds". Yet we can probably expect both words and game lengths to be small, therefore it is better to go with a more compact program.
Code Snippets
count_duds :: String -> [Char] -> Int
count_duds word guessed = length $ filter (`notElem` map toUpper word) guessed
playgame :: String -> [Char] -> IO ()
playgame word guessed
| all (`elem` guessed) (map toUpper word) = putStrLn $ header "Won"
| count_duds word guessed + 1 >= length hangman = putStrLn $ header "Lost" ++ word
| otherwise = do putStrLn $ header "Pick"
l <- fmap toUpper getChar
let guessed' | not (isAlpha l) = guessed
| l `elem` guessed = guessed
| otherwise = l:guessed
playgame word guessed'
where header msg = formatState word guessed ++ "\n" ++ strings lang msgContext
StackExchange Code Review Q#18497, answer score: 3
Revisions (0)
No revisions yet.