-- hangman game -- Lecture 3B part 2, 2017 -- David Sands import System.Random(randomRIO) -- Constants guessLimit :: Int guessLimit = 6 -- number of guesses before you lose dict :: FilePath dict = "/usr/share/dict/words" -- mac OS X only dictLength :: Int dictLength = 235886 -- we could calculate this though -- The game state: the word to be guessed, -- and the guesses so far -------------------------------------- data GameState = G {word :: String, guess :: String} -------------------------------------- -- Non IO part. Would usually go in a seperate module showWord :: GameState -> String -- word "skeleton" showWord (G w gs) = [reveal c | c <- w] where reveal c | c `elem` gs = c | otherwise = '_' lives :: GameState -> Int lives (G w gs) = guessLimit - length badGuesses where badGuesses = [c | c <- gs, c `notElem` w] won,lost :: GameState -> Bool won state = showWord state == word state lost state = lives state <= 0 -------------------------------------------------------- -- IO part main :: IO() -- get a random word from the dictionary -- start the first round of the game -- with the word and empty list of guesses main = do w <- getRandom play (G w "") getRandom :: IO String getRandom = do ws <- readFile dict i <- randomRIO(0,dictLength -1) return (words ws !! i) -- Play a round of the game: -- if player had neither won nor lost -- print number of lives -- print word skeleton -- get a new guess from player -- start next round play :: GameState -> IO() play state@(G w gs) -- this is called an "as-pattern" | won state = putStrLn "Yay! You won!" | lost state = do putStrLn "Loser!" putStrLn ("The word was: " ++ w) | otherwise = do putStrLn ("Lives: " ++ show (lives state)) putStrLn (showWord state) putStr "Type some characters: " newg <- getLine play (G w (newg ++ gs)) -- Note: this solution allows you to cheat by typing the full alphabet. -- Suggested fix: check for lost before checking won. Simples.