|
COSC 4P41 Additional Assignment
|
|
Instructor:
Michael
Winter, Office
J323, Office Hours: Tue & Fri, 09:00am - 11:00am, email: mwinter@brocku.ca
In this assignment you will implement a version of the well-known Hangman game in Haskell.
The module MyRandom
This module implements our version of a random number generator. The implementation used by this module is based on this Wikipedia entry. While using the principle of information hiding, the module should export the following entities:
- A new data type RandGen of random number generators. The implementation of a random number generator consists of a value seed of type Long and a function of type Long -> Double that produces from a given Long value a Double from the interval [0.0..1.0).
- getRandGen :: IO RandGen. This function is supposed to generate and return a random number generator embedded in the IO monad. Use the nanosSinceEpoch (module COSC4P41) for the seed and the following function:
\x -> let a = x * 15485863 in fromIntegral (a*a `mod` 2038074743) / 2038074743
- nextRandom :: RandGen -> (Double,RandGen). This function creates a random value (first component of the result) by applying the function of the random number generator to its seed. The new random number generator (second component of the result) is obtained from the original number generator by increasing the seed by 1.
- randomInRange :: Enum a => (a,a) -> RandGen -> (Int,RandGen). This function creates a random value (first component of the result) in the range given by the pair of values (first parameter) from the enumeratesd data type a. The new generator (second component of the result) is the same as in the previous function.
Remark: The module System.Random cannot be used to implement the module above.
The Main Program
This module runs the Hangman game. Implement the following:
- Use the following type synomyms:
- type HiddenWord = String
- type GuessesLeft = Int
- type GuessedChars = [Char]
- type UserMove = Char
- A new data type State for the current state of the game. A state has three components:
- The hidden word (of type HiddenWord). This is the word the player has to guess.
- The number of guesses left (of type GuessesLeft). This is the number of wrong guesses that are left. Each time the player guesses a wrong letter, the number is reduced by 1. Once this number is 0, the player has lost the game.
- The letters that have already be chosen by the player (of type GuessedChars). These are the letters the player has guessed so far. Each time the player guesses another letter, it will be added to this list (independently whether the letter occurs in the hidden word or not).
Make State an instance of Show so that the state of the game can be displayed in each iteration of the game as, for example, shown in the Lines 3-9 of the example run below.
- gameStep :: State -> UserMove -> Either String (String,State). This function takes a State and a UserMove and progresses the game one step. There are two potential outcomes of a such a move.
- If the current move ends the game, i.e., if the player has completely guessed the hidden word or the player runs out of guesses, then the function returns a Left element with a String message indicating this outcome (see the example run).
- Otherwise, the function returns a Right element with the new State
and a message indicating the outcome of the current move (see the example run).
- iterateGame :: String -> State -> IO Bool. This function should do th following:
- It first prints the message (first parameter).
- Then it prints the State.
- Then it prompts the player to enter a new letter, and a line is read from the input and trimmed. If the input is the string "quit" or "Quit", then the function prints "Good bye." and terminates with True. Otherwise, it checks whether the input is exactly one character. If not, the function is called recursively with the message "Illegal input: " plus the current input and the current State. If the input is just one character, the function calls gameStep. If this call returns a Left element, the message is printed and the function returns False. If the call returns a Right element, iterateGame is called recursively with the message and the new State.
- runGame :: [String] -> RandGen -> IO (). This function takes the list of potential hidden words (first parameter) and a random number generator (of type RandGen) as inputs. It selects one of the hidden words randomly using the random number generator and calls the function iterateGame with the initial message (see example run below) and the initial State based on the selected word, 5 guesses left, and the empty list of guessed letters. If the result of the call is True, then the function terminates. Otherwise, it calls itself recursively with the list of hidden words and the new random number generator for another game.
- main :: IO (). This function first loads the list of potential hidden words from the file "HiddenWords.txt", converts it to a list of Strings, gets a random number generator, and the calls runGame. The file span style="font-family: Courier New;">"HiddenWords.txt" can be downloaded here.
The module COSC4P41
This module can be downloaded here and defines to following entities to be used for this assignment:
- The data type Long as a type synonym for Int64.
- nanosSinceEpoch :: IO Long that returns the current time in nanoseconds embedded in the IO monad.
- The function split :: Eq a => a -> [a] -> [[a]] that splits a list of a elements into a list of lists at the symbol given by the first parameter. Examples:
>>> split 0 [12,42,0,8,34,2,-1,0,15,0,21,34]
[[12,42],[8,34,2,-1],[15],[21,34]]
>>> split ',' "and,to,here,there"
["and","to","here","there"]
- The function trim :: String -> String that remove spaces from either side of a string. Example:
>>> trim " Hello "
"Hello"
- The function subSet :: Eq a => [a] -> [a] -> Bool that returns true iff the first list seen as a set is a subset of the second list seen as a set. Examples:
>>> subSet [1,2] [2,3,4,1]
True
>>> subSet [1,2] [2..10]
False
Example run
*** Hangman Game ***
Type "quit" or "Quit" to quit.
_ _ _ _ _ _ _ _ _ _
You have 5 moves left.
You have guessed the letters "" so far.
Guess a letter: e
This letter is not in the word.
_ _ _ _ _ _ _ _ _ _
You have 4 moves left.
You have guessed the letters "e" so far.
Guess a letter: a
Good guess.
_ _ _ _ _ _ a _ _ _
You have 4 moves left.
You have guessed the letters "ae" so far.
Guess a letter: r
Good guess.
_ r _ _ _ _ a _ _ _
You have 4 moves left.
You have guessed the letters "rae" so far.
Guess a letter: w
Good guess.
w r _ _ _ w a _ _ _
You have 4 moves left.
You have guessed the letters "wrae" so far.
Guess a letter: yy
Illegal input: yy
w r _ _ _ w a _ _ _
You have 4 moves left.
You have guessed the letters "wrae" so far.
Guess a letter: i
Good guess.
w r i _ _ w a _ _ _
You have 4 moves left.
You have guessed the letters "iwrae" so far.
Guess a letter: s
Good guess.
w r i s _ w a _ _ _
You have 4 moves left.
You have guessed the letters "siwrae" so far.
Guess a letter: t
Good guess.
w r i s t w a t _ _
You have 4 moves left.
You have guessed the letters "tsiwrae" so far.
Guess a letter: c
Good guess.
w r i s t w a t c _
You have 4 moves left.
You have guessed the letters "ctsiwrae" so far.
Guess a letter: h
Congratulations!!! You won.
*** Hangman Game ***
Type "quit" or "Quit" to quit.
_ _ _ _ _ _
You have 5 moves left.
You have guessed the letters "" so far.
Guess a letter: quit
Good bye.
COSC Home Page
COSC 4P41 Home Page
© M. Winter, 2022