Skip to content

Commit 47440ac

Browse files
committed
Complete Step 3 of the Hangman game
- Making a puzzle
1 parent b1e96de commit 47440ac

File tree

1 file changed

+83
-1
lines changed

1 file changed

+83
-1
lines changed

ch13/hangman/src/Main.hs

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import System.Random (randomRIO)
99

1010
main :: IO ()
1111
main = do
12-
putStrLn "hello world"
12+
word <- randomWord'
13+
let puzzle = freshPuzzle (fmap toLower word)
14+
runGame puzzle
1315

1416
type WordList = [String]
1517

@@ -39,3 +41,83 @@ randomWord wl = do
3941

4042
randomWord' :: IO String
4143
randomWord' = gameWords >>= randomWord
44+
45+
data Puzzle = Puzzle String [Maybe Char] [Char]
46+
-- the word we're trying to guess
47+
-- the characters we've filled in so far
48+
-- the letters we've guessed so far
49+
50+
instance Show Puzzle where
51+
show (Puzzle _ discovered guessed) =
52+
(intersperse ' ' $ fmap renderPuzzleChar discovered)
53+
++ " Guessed so far: " ++ guessed
54+
55+
renderPuzzleChar :: Maybe Char -> Char
56+
renderPuzzleChar = maybe '_' id
57+
58+
freshPuzzle :: String -> Puzzle
59+
freshPuzzle word = Puzzle word discovered []
60+
where discovered = fmap (const Nothing) word
61+
62+
charInWord :: Puzzle -> Char -> Bool
63+
charInWord (Puzzle word _ _) c = elem c word
64+
65+
alreadyGuessed :: Puzzle -> Char -> Bool
66+
alreadyGuessed (Puzzle _ _ guessed) c = elem c guessed
67+
68+
fillInCharacter :: Puzzle -> Char -> Puzzle
69+
fillInCharacter (Puzzle word fillInSoFar s) c =
70+
Puzzle word newFilledInSoFar (c : s)
71+
where
72+
zipper guessed wordChar guessChar =
73+
if wordChar == guessed then Just wordChar else guessChar
74+
75+
newFilledInSoFar =
76+
zipWith (zipper c) word fillInSoFar
77+
78+
-- For e.g. let c = 'h', word = "his", filledInSoFar = [Nothing, Just 'i', Nothing]
79+
-- then zipper 'h' 'h' Nothing = Just 'h'
80+
-- zipper 'h' 'i' Just 'i' = Just 'i'
81+
-- zipper 'h' 's' Nothing = Nothing
82+
83+
handleGuess :: Puzzle -> Char -> IO Puzzle
84+
handleGuess puzzle guess = do
85+
putStrLn $ "Your guess was: " ++ [guess]
86+
case (charInWord puzzle guess, alreadyGuessed puzzle guess) of
87+
(_, True) -> do
88+
putStrLn "You already guessed that character, pick something else!"
89+
return puzzle
90+
91+
(True, _) -> do
92+
putStrLn "This character was in the word, filling in the word accordingly."
93+
return (fillInCharacter puzzle guess)
94+
95+
(False, _) -> do
96+
putStrLn "This character wasn't in the word, try again."
97+
return (fillInCharacter puzzle guess)
98+
99+
gameOver :: Puzzle -> IO ()
100+
gameOver (Puzzle wordToGuess _ guessed) =
101+
if (length guessed) > 7 then
102+
do putStrLn "You lose!"
103+
putStrLn $ "The word was: " ++ wordToGuess
104+
exitSuccess
105+
else return ()
106+
107+
gameWin :: Puzzle -> IO ()
108+
gameWin (Puzzle _ filledInSoFar _) =
109+
if all isJust filledInSoFar then
110+
do putStrLn "You win!"
111+
exitSuccess
112+
else return ()
113+
114+
runGame :: Puzzle -> IO ()
115+
runGame puzzle = forever $ do
116+
gameOver puzzle
117+
gameWin puzzle
118+
putStrLn $ "Current puzzle is: " ++ show puzzle
119+
putStr "Guess a letter: "
120+
guess <- getLine
121+
case guess of
122+
[c] -> handleGuess puzzle c >>= runGame
123+
_ -> putStrLn "Your guess must be a single character."

0 commit comments

Comments
 (0)