I just finished a course of functional programming in uni and continued to study Haskell because I found it very interesting! I made a simple hangman game and would like to hear any of your thoughts and ideas that may arise looking at the code! What, for example, could make it more dogmatic in a functional programming sense?
import Control.Monad
import Data.List (elemIndices, sort)
pictures = 9
main :: IO ()
main = do
word <- getWord ""
clearScreen
hang word
hang :: String -> IO ()
hang word = hang' word [] [] pictures
hang' :: String -> [Char] -> [Char] -> Int -> IO ()
hang' word _ _ lives | lives == 0 = clearScreen >> putStrLn (renderHangman 0) >> putStrLn "You lost!" >> putStrLn ("The correct word was " ++ word ++ "\n")
hang' word rights _ lives | win rights word = clearScreen >> putStrLn (renderHangman lives) >> putStrLn "You won!" >> putStrLn ("The correct word was " ++ word ++ "\n")
hang' word rights wrongs lives = do
clearScreen
putStrLn $ renderHangman lives
putStrLn $ renderWord rights word
putStrLn $ renderLives lives
putStrLn $ renderWrongs wrongs
guess <- getGuess
if guess `elem` (rights ++ wrongs)
then hang' word rights wrongs lives
else
if correctGuess guess word
then hang' word (guess : rights) wrongs lives
else hang' word rights (guess : wrongs) (lives - 1)
win :: [Char] -> String -> Bool
win guesses = all (`elem` guesses)
clearScreen :: IO ()
clearScreen = replicateM_ 100 (putStrLn "")
correctGuess :: Char -> String -> Bool
correctGuess guess word = guess `elem` word
getGuess :: IO Char
getGuess = do
putStrLn "Guess a letter!"
getChar
getWord s = do
clearScreen
putStrLn "Give a secret word!"
putStr ['*' | _ <- s]
c <- getChar
case c of
'\n' -> return s
char -> getWord (s ++ [char])
renderWord :: [Char] -> String -> String
renderWord guesses = foldr hide ""
where
hide = \x xs -> if x `elem` guesses then x : xs else '_' : xs
renderWrongs :: [Char] -> String
renderWrongs [] = ""
renderWrongs wrongs = "Wrong guesses: " ++ sort wrongs
renderHangman :: Int -> String
renderHangman = unlines . hangmanpics
renderLives :: Int -> String
renderLives lives = show lives ++ " guesses left!"
hangmanpics :: Int -> [String]
hangmanpics 9 = [" ", " ", " ", " ", " ", " ", "========="]
hangmanpics 8 = [" ", " |", " |", " |", " |", " |", "========="]
hangmanpics 7 = [" +---+", " |", " |", " |", " |", " |", "========="]
hangmanpics 6 = [" +---+", " | |", " |", " |", " |", " |", "========="]
hangmanpics 5 = [" +---+", " | |", " O |", " |", " |", " |", " ========="]
hangmanpics 4 = [" +---+", " | |", " O |", " | |", " |", " |", " ========="]
hangmanpics 3 = [" +---+", " | |", " O |", " /| |", " |", " |", " ========="]
hangmanpics 2 = [" +---+", " | |", " O |", " /|\\ |", " |", " |", " ========="]
hangmanpics 1 = [" +---+", " | |", " O |", " /|\\ |", " / |", " |", " ========="]
hangmanpics 0 = [" +---+", " | |", " O |", " /|\\ |", " / \\ |", " |", " ========="]
hangmanpics _ = [" +---+", " | |", " O |", " /|\\ |", " / \\ |", " |", " ========="]
```