@@ -5,80 +5,87 @@ import Data.Char (toLower)
5
5
import Data.Maybe (isJust )
6
6
import Data.List (intersperse )
7
7
import System.Exit (exitSuccess )
8
+ import System.IO
9
+
8
10
import System.Random (randomRIO )
9
11
10
- main :: IO ()
11
- main = do
12
- word <- randomWord'
13
- let puzzle = freshPuzzle (fmap toLower word)
14
- runGame puzzle
15
12
16
13
type WordList = [String ]
17
14
15
+
18
16
allWords :: IO WordList
19
17
allWords = do
20
18
dict <- readFile " data/dict.txt"
21
19
return (lines dict)
22
20
21
+
23
22
minWordLength :: Int
24
23
minWordLength = 5
25
24
25
+
26
26
maxWordLength :: Int
27
27
maxWordLength = 9
28
28
29
+
29
30
gameWords :: IO WordList
30
31
gameWords = do
31
32
aw <- allWords
32
- return $ filter gameLength aw
33
- where gameLength w =
34
- let l = length (w :: String )
35
- in l > minWordLength && l < maxWordLength
33
+ return (filter gameLength aw)
34
+ where
35
+ gameLength w =
36
+ let
37
+ l = length (w :: String )
38
+ in
39
+ l > minWordLength && l < maxWordLength
40
+
36
41
37
42
randomWord :: WordList -> IO String
38
43
randomWord wl = do
39
44
randomIndex <- randomRIO (0 , length wl - 1 )
40
45
return $ wl !! randomIndex
41
46
47
+
42
48
randomWord' :: IO String
43
49
randomWord' = gameWords >>= randomWord
44
50
51
+
45
52
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
53
+
49
54
50
55
instance Show Puzzle where
51
56
show (Puzzle _ discovered guessed) =
52
57
(intersperse ' ' $ fmap renderPuzzleChar discovered)
53
58
++ " Guessed so far: " ++ guessed
54
59
55
- renderPuzzleChar :: Maybe Char -> Char
56
- renderPuzzleChar = maybe ' _' id
57
60
58
61
freshPuzzle :: String -> Puzzle
59
- freshPuzzle word = Puzzle word discovered []
60
- where discovered = fmap ( const Nothing ) word
62
+ freshPuzzle w = Puzzle w ( map ( const Nothing ) w) []
63
+
61
64
62
65
charInWord :: Puzzle -> Char -> Bool
63
66
charInWord (Puzzle word _ _) c = elem c word
64
67
68
+
65
69
alreadyGuessed :: Puzzle -> Char -> Bool
66
70
alreadyGuessed (Puzzle _ _ guessed) c = elem c guessed
67
71
72
+
73
+ renderPuzzleChar :: Maybe Char -> Char
74
+ renderPuzzleChar Nothing = ' _'
75
+ renderPuzzleChar (Just c) = c
76
+
77
+
68
78
fillInCharacter :: Puzzle -> Char -> Puzzle
69
- fillInCharacter (Puzzle word fillInSoFar s) c =
79
+ fillInCharacter (Puzzle word filledInSoFar s) c =
70
80
Puzzle word newFilledInSoFar (c : s)
71
81
where
72
82
zipper guessed wordChar guessChar =
73
- if wordChar == guessed then Just wordChar else guessChar
83
+ if wordChar == guessed
84
+ then Just wordChar
85
+ else guessChar
74
86
75
- newFilledInSoFar =
76
- zipWith (zipper c) word fillInSoFar
87
+ newFilledInSoFar = zipWith (zipper c) word filledInSoFar
77
88
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
89
83
90
handleGuess :: Puzzle -> Char -> IO Puzzle
84
91
handleGuess puzzle guess = do
@@ -87,29 +94,34 @@ handleGuess puzzle guess = do
87
94
(_, True ) -> do
88
95
putStrLn " You already guessed that character, pick something else!"
89
96
return puzzle
90
-
91
97
(True , _) -> do
92
98
putStrLn " This character was in the word, filling in the word accordingly."
93
99
return (fillInCharacter puzzle guess)
94
-
95
100
(False , _) -> do
96
101
putStrLn " This character wasn't in the word, try again."
97
102
return (fillInCharacter puzzle guess)
98
103
104
+
99
105
gameOver :: Puzzle -> IO ()
100
106
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 ()
107
+ if (length guessed) > 7
108
+ then do
109
+ putStrLn " You lose!"
110
+ putStrLn $ " The word was: " ++ wordToGuess
111
+ exitSuccess
112
+ else
113
+ return ()
114
+
106
115
107
116
gameWin :: Puzzle -> IO ()
108
117
gameWin (Puzzle _ filledInSoFar _) =
109
- if all isJust filledInSoFar then
110
- do putStrLn " You win!"
111
- exitSuccess
112
- else return ()
118
+ if all isJust filledInSoFar
119
+ then do
120
+ putStrLn " You win!"
121
+ exitSuccess
122
+ else
123
+ return ()
124
+
113
125
114
126
runGame :: Puzzle -> IO ()
115
127
runGame puzzle = forever $ do
@@ -121,3 +133,11 @@ runGame puzzle = forever $ do
121
133
case guess of
122
134
[c] -> handleGuess puzzle c >>= runGame
123
135
_ -> putStrLn " Your guess must be a single character."
136
+
137
+
138
+ main :: IO ()
139
+ main = do
140
+ hSetBuffering stdout NoBuffering
141
+ word <- randomWord'
142
+ let puzzle = freshPuzzle (fmap toLower word)
143
+ runGame puzzle
0 commit comments