Created
December 27, 2016 20:55
-
-
Save ygrenzinger/1961032c0ef4c4f88ae8e3be3400cce2 to your computer and use it in GitHub Desktop.
Hangman Haskell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main where | |
import Control.Monad (forever) | |
import Data.Char (toLower) | |
import Data.Maybe (isJust, fromMaybe) | |
import Data.List (intersperse) | |
import System.Exit (exitSuccess) | |
import System.Random (randomRIO) | |
newtype WordList = WordList [String] deriving (Eq, Show) | |
minWordLength :: Int | |
minWordLength = 4 | |
maxWordLength :: Int | |
maxWordLength = 7 | |
allWords :: IO WordList | |
allWords = do | |
dict <- readFile "data/dict.txt" | |
return $ WordList (lines dict) | |
gameWords :: IO WordList | |
gameWords = do | |
(WordList aw) <- allWords | |
return $ WordList (filter gameLength aw) | |
where gameLength w = let l = length (w :: String) | |
in l > minWordLength && l < maxWordLength | |
randomWord :: WordList -> IO String | |
randomWord (WordList wl) = do | |
randomIndex <- randomRIO (0, length wl - 1) | |
return $ wl !! randomIndex | |
randomWord' :: IO String | |
randomWord' = gameWords >>= randomWord | |
data Puzzle = Puzzle String [Maybe Char] [Char] | |
instance Show Puzzle where | |
show (Puzzle _ discovered guessed) = | |
intersperse ' ' $ fmap renderPuzzleChar discovered ++ " Guessed so far: " ++ guessed | |
freshPuzzle :: String -> Puzzle | |
freshPuzzle s = Puzzle s (map (const Nothing) s) [] | |
charInWord :: Puzzle -> Char -> Bool | |
charInWord (Puzzle word _ _) c = c `elem` word | |
alreadyGuessed :: Puzzle -> Char -> Bool | |
alreadyGuessed (Puzzle _ _ alreadyGuessed) c = c `elem` alreadyGuessed | |
renderPuzzleChar :: Maybe Char -> Char | |
renderPuzzleChar = fromMaybe '_' | |
fillInCharacter :: Puzzle -> Char -> Puzzle | |
fillInCharacter (Puzzle word filledInSoFar s) c = | |
Puzzle word newFilledInSoFar (c : s) | |
where zipper guessed wordChar guessChar = | |
if wordChar == guessed then Just wordChar else guessChar | |
newFilledInSoFar = zipWith (zipper c) word filledInSoFar | |
handleGuess :: Puzzle -> Char -> IO Puzzle | |
handleGuess puzzle guess = do | |
putStrLn $ "Your guess was: " ++ [guess] | |
case (charInWord puzzle guess, alreadyGuessed puzzle guess) of | |
(_, True) -> do | |
putStrLn "You already guessed that character, pick something else!" | |
return puzzle | |
(True, _) -> do | |
putStrLn "This character was in the word, filling in the word accordingly" | |
return (fillInCharacter puzzle guess) | |
(False, _) -> do | |
putStrLn "This character wasn't in the word, try again." | |
return (fillInCharacter puzzle guess) | |
gameOver :: Puzzle -> IO () | |
gameOver (Puzzle wordToGuess _ guessed) = | |
if length guessed > 7 then | |
do putStrLn "You lose!" | |
putStrLn $ "The word was: " ++ wordToGuess | |
exitSuccess | |
else return () | |
gameWin :: Puzzle -> IO () | |
gameWin (Puzzle _ filledInSoFar _) = | |
if all isJust filledInSoFar | |
then do putStrLn "You win!" | |
exitSuccess | |
else return () | |
runGame :: Puzzle -> IO () | |
runGame puzzle = forever $ do | |
gameOver puzzle | |
gameWin puzzle | |
putStrLn $ "Current puzzle is: " ++ show puzzle | |
putStr "Guess a letter: " | |
guess <- getLine | |
case guess of | |
[c] -> handleGuess puzzle c >>= runGame | |
_ -> putStrLn "Your guess must be a single character" | |
main :: IO () | |
main = do | |
word <- randomWord' | |
let puzzle = freshPuzzle (fmap toLower word) | |
runGame puzzle |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment