Number guessing game error and keeping count of guesses - haskell

I am doing a number guessing game in Haskell and I have 3 difficulties.
I made a menu where the user selects what difficulty but once I implemented it, it started giving me an error and I can't figure out what it is.
The error:
finalename.hs:5:10:
Parse error in pattern: putStrLn
Possibly caused by a missing 'do'?
Also how would I go about implementing a count where it counts how many times it takes for the user to guess?
import System.Random (randomRIO)
main :: IO ()
main = do
putStrLn "Do you want to play 1) Easy, 2) Medium or 3) Hard"
putStrLn "Select your difficulty by entering a number 1, 2 or 3"
choice <- readLn
| choice == 1 = "You selected easy" >> easy
| choice == 2 = "You selected medium" >> medium
| choice == 3 = "You selected hard" >> hard
| otherwiose = "You did not selected a valid choice" >> main
easy :: IO ()
easy = do
putStrLn "Guess a number between 1 and 13: "
rNumber <- randomRIO (1, 10) :: IO Int
loop rNumber
medium :: IO ()
medium = do
putStrLn "Guess a number between 1 and 25: "
rNumber <- randomRIO (1, 25) :: IO Int
loop rNumber
hard :: IO ()
hard = do
putStrLn "Guess a number between 1 and 50: "
rNumber <- randomRIO (1, 50) :: IO Int
loop rNumber
loop :: Int -> IO ()
loop rNumber = do
userInput <- readLn
case compare rNumber userInput of
EQ -> putStrLn "Correct!" >> main
LT -> putStrLn "Guess is too low, try again please" >> loop rNumber
GT -> putStrLn "Guess is too high, try again please" >> loop rNumber

You need to change your loop function to keep track of both the number to guess and the number of guesses:
loop :: Int -> Int -> IO ()
loop rNumber guesses = do
putStrLn $ "You have made " ++ show guesses ++ " guesses so far."
...
putStrLn "Too low, guess again"
loop rNumber (guesses+1)
...
putStrLn "To high, guess again"
loop rNumber (guesses+1)
Note how when you call loop recursively you change the guess count by passing in the old guess count + 1.

Related

End program after number of inputs

What my program does? Take input from user, it needs to be between 0-99.
Program ends when user guesses the number "guess" which is 45 or after taking 10 tries.
The problem is that I cant figure out the second part of this game.
guess = 45
game = do
putStrLn ("Give a number between 0 and 99")
a <- getLine
let x = read a
if x == guess then print ("You got it!") else game
You can make a helper function where you use a parameter with the number of guesses. Each time you make a recursive call where you decrement the number of guesses, if the variable is less than or equal to zero, we can stop the recursion:
guess :: Int
guess = 45
tries :: Int
tries = 10
main :: IO ()
main = game tries
game :: Int -> IO ()
game n
| n <= 0 = putStrLn "end of the game"
| otherwise = do
putStrLn "Give a number between 0 and 99"
a <- readLn
if a == guess then putStrLn "You got it!" else game (n-1)
Thank you Willem! Had to rearrange some things in the code but you gave me the idea. Thats how now it looks like:
guess = 45
tries = 10
main = zad5 tries
zad5 y = do
putStrLn ("Give a number between 0 and 99")
a <- getLine
let x = read a
if y==1 || x == guess then print ("end of the game") else zad5 (y-1)

Let block gives indentation error

I know what an indentation error is, but I have no idea why I'm getting this error here, while every is aligned, trying to solve it for 2 hours.
Account.hs:40:25: error:
parse error (possibly incorrect indentation or mismatched brackets)
|
40 | let amount = readLn :: IO Int
| ^
Failed, 0 modules loaded.
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
I also tried this version but this also gives incorrect indentation or mismatched brackets:
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
The problem is here:
-- |<---- "column 0" of this 'do' block
then do putStrLn $ "Enter your name "
-- | still good; a 'let' statement:
let name = getLine
-- |<---- "column 0" of this 'let' block
putStrLn $ "Enter the initial amount"
-- | Huh, there's no '=' in ^this^ declaration?
let amount = readLn :: IO Int
-- ^^^ Why is there a 'let' within another let binding?
-- I still haven't seen a '='. Better throw a parse error.
Basically, putStrLn $ "Enter the initial amount" is aligned with name = ... in the preceding line, so the compiler reads it as a declaration (part of the same let block).
To fix your indentation errors, it should be:
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
But then you'll run into type errors:
read g is wrong: read takes a String, but g :: IO String
g == 1 is wrong: 1 is an Int, but g :: IO String
show value is wrong: show returns a String, but you're using it as an IO action
You haven't shown the declaration of Account, but you're likely going to have issues with name and amount, too
You probably want something like:
main = do
putStrLn $ "Press one to create a new account"
g <- getLine
let enteredValue = read g :: Int
if enteredValue == 1
then do putStrLn $ "Enter your name "
name <- getLine
putStrLn $ "Enter the initial amount"
amount <- readLn :: IO Int
let value = Account (name,1,amount) Saving
putStrLn (show value)
else do putStrLn $ "Nothing"
Basically, use v <- expr to go from expr :: IO Something to v :: Something.
Other notes:
g <- getLine; let enteredValue = read g :: Int better written as enteredValue <- readLn :: IO Int
putStrLn (show value) can be shortened to print value
you don't need do for a single expression (nor $ for a single operand): ... else putStrLn "Nothing"
There is more wrong to your code than just the Indentation Errors - so my first suggestion would be reading a bit of learn you a haskell for great good.
Next there are two assignment operators in haskell - one binds the result of an action … <- … and the other one is a local definition/declaration of a pure computation let … = ….
Moreover you can improve your reading a value by taking account of the possible false input, that someone could give you (intentionally and unintentionally) by replacing read with readMaybe, where the latter returns a Maybe something, for example readMaybe "1" = Just 1 :: Maybe Int or readMaybe "foo" = Nothing :: Maybe Int.
Regarding your indentation it is best that you compare one solution to your program with yours own:
import Text.Read (readMaybe)
data Type = Saving | Checking
deriving (Show)
data Account = Account (String,Int,Int) Type
deriving (Show)
main :: IO ()
main = do
putStrLn "Press one to create a new account"
g <- getLine
let enteredValue = readMaybe g :: Maybe Int
here the result of getLine and entered value have the same scope so they have the same indentation - we only change the scope after the next if where the then-block - and the else-block do not share the 'declarations' of each branch, so you couldn't use name in the else-block, but enteredValue can be used in both.
if enteredValue == Just 1
then do putStrLn "Enter your name "
name <- getLine
putStrLn "Enter the initial amount"
amount' <- fmap readMaybe getLine
here again name and amount' share the same scope, and pattern matching on amount' creates a new scope where amount is visible and the match on Nothing where you cannot use this variable.
case amount' of
Just amount -> print $ Account (name,1,amount) Saving
Nothing -> putStrLn "Nothing"
else putStrLn "Nothing"
let is for binding values, which is done in the form let x = y+z, where x is the name (aka "identifier") being bound, and y+z is the expression to which it is being bound.
In your example, I see three bindings: name, amount, and value. The rest are not value bindings, but actions.
In the do notation, actions do not need a let. You just write them one after another. So:
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
let value = Account (name,1,amount) Saving
show value
But wait! This is not all!
getLine is not actually an expression of type String, as you seem to be hoping here. Rather, getLine is an action. In order to get it to "run" and "produce" a String value, you need to use the <- construct instead of let:
name <- getLine
Similarly with readLn:
amount <- readLn :: IO Int
Finally, show value is not actually an action that would print the value to the screen. show is a function that takes a value and return a String. It doesn't "do" anything (i.e. doesn't produce any outside effects), so you can't use it in place of an action in the do notation. If you wanted an action that would print a value to the screen, that would be print:
print value
Gathering everything together:
name <- getLine
putStrLn $ "Enter the initial amount"
amount <- readLn :: IO Int
let value = Account (name,1,amount) Saving
print value
And after fixing all of that, you'll have similar difficulties with the first part of your program, where you have let g = getLine instead of g <- getLine.

Clean up if else control flow in haskell

I have the following simple number guess program
import System.Random
turn :: Int -> Int -> Int -> IO ()
turn number attempt attempts =
do
if attempts == 0
then putStrLn "You lose"
else if attempt==number
then putStrLn "You got it!"
else if attempt==0
then guess number attempt attempts
else if attempt < number
then do
putStrLn "The number is greater"
guess number attempt attempts
else
do
putStrLn "The number is lesser"
guess number attempt attempts
guess :: Int -> Int -> Int -> IO ()
guess number attempt attempts =
do
putStr "Try and guess number "
g <- getLine
let number' = read g :: Int
let check = (number'==number)
let attempts' = if check then attempts else attempts - 1
turn number number' attempts'
numberGuess :: IO ()
numberGuess = do
let attempts = 5
number <- randomRIO (0, 10) :: IO Int
turn number 0 attempts
How can I clean up the ugly if else or what techniques are available in haskell?
Your outermost do in turn doesn't actually do anything. So you should nix it. Next, use pattern matching and guards to begin to clean things up.
turn _number _attempt 0 = putStrLn "You lose"
turn number attempt attempts
| attempt == number = putStrLn "You got it!"
| attempt == 0 = guess number attempt attempts
| attempt < number = do
putStrLn "The number is greater"
guess number attempt attempts
| otherwise = do
putStrLn "The number is lesser"
guess number attempt attempts
The final step to clean up turn will be factoring out the pattern "Do something, then call guess". I'll let you try that on your own. I used a pattern match for the first case to demonstrate that technique, which is very often the right one. In this particular case you might be better off just using guards.
For guess, the easy bit is just to combine some expressions.
guess :: Int -> Int -> Int -> IO ()
guess number attempt attempts = do
putStr "Try and guess number "
g <- getLine
let attempts' = if read g == number
then attempts
else attempts - 1
turn number number' attempts'
Note, however, that read generally shouldn't be used to process user input, as it will crash your program on bad input. Import Text.Read and use readMaybe, perhaps.

Haskell IO reads an integer n from the user then adds up n integers

I'm trying to write a program that reads an integer n from the user, then reads n integers (on separate lines), and finally display the sum of the n numbers read.
Here is my code so far:
addNumbers :: IO ()
addNumbers = do
putStrLn "Enter a number:"
num <- getInt
addNumbers2 num
addNumbers2 :: Int -> IO ()
addNumbers2 num = do
putStrLn "Enter a number:"
n <- getInt
if num == 1 then
print n
else do
print (n + addNumbers2 (num - 1))
At the moment it doesn't compile, the error says:
Couldn't match expected type `Int' with actual type `IO ()'
In the return type of a call of `addNumbers2'
In the second argument of `(+)', namely `addNumbers2 (num - 1)'
In the first argument of `print', namely
`(n + addNumbers2 (num - 1))'
IO is really confusing me, I'm trying to get an output of:
Enter a number:
3
Enter a number:
2
Enter a number:
1
Enter a number:
5
Sum is: 8
You treated addNumbers as if it were an ordinary function, but it's an IO operation, so we can only get numbers out of it inside do and with answer <- addNumbers2, but also at the moment it doesn't return anything, it just prints it.
I've refactored a little:
addNumbers :: IO ()
addNumbers = do
putStrLn "Enter how many numbers:" -- clearer
num <- getInt
sum <- addNumbers2 num -- use new version to return sum
print sum -- print them here
and now addNumbers2 actually adds them and returns them:
addNumbers2 :: Int -> IO Int
addNumbers2 num = do
putStrLn "Enter a number:"
n <- getInt
if num == 1 then
return n -- pass the number back
else do
therest <- addNumbers2 (num - 1) -- get the rest of them
return (n + therest) -- add them up
That works:
addNumbers
Enter how many numbers:
3
Enter a number:
1
Enter a number:
2
Enter a number:
3
6
A better way
sequence :: Monad m => [m a] -> m [a] takes a list of actions and runs them all, returning the list of results. If we just make a list full of getInts, [getInt| _<-[1..num]] or more consisely, replicate num getInt we could do numbers <- sequence (replicate num getInt). There's a shorthand for that in Control.Monad, called replicateM :: Monad m => Int -> m a -> m [a]
This would be better done like this though:
import Control.Monad
addNumbers' = do
putStrLn "Enter how many numbers:"
num <- getInt
numbers <- replicateM num (putStrLn "Enter a number" >> getInt)
print (sum numbers)
which gives
Enter how many numbers:
3
Enter a number
10
Enter a number
20
Enter a number
30
60
You can (and should) use combinators available
addNumbers2 n = do
n_numbers <- replicateM n (putStrLn "Number, please: " >> getInt)
let result = sum n_numbers
return result
The crucial insight is to combine the IO actions
putStrLn "string" :: IO ()
getInt :: IO Int
to
(putStrLn "Number?" >> getInt) :: IO Int
So we have a IO action that asks for input and reads it.
Now, we can use
replicateM :: Int -> IO a -> IO [a]
and since we pass an IO action that returns Int, we get a list of Int back.
numbers <- replicateM n (putStrLn "Number?" >> getInt)
runs the given IO action n times and collects their results.
All that is left is summing up the numbers and returning them in the IO Monad.
Or, if you just want to print the sum, you can also
replicateM n (putStrLn "Number?" >> getInt) >>= putStrLn . show . sum
The following pipes-based solution has one tiny advantage over the accepted solution, which is that it will not stack overflow on a large number of lines:
import Pipes
import qualified Pipes.Prelude as Pipes
main = do
numLines <- readLn
total <- Pipes.sum (Pipes.replicateM numLines readLn)
print total
Example use:
$ ./example
3<Enter>
10<Enter>
20<Enter>
30<Enter>
60

Help In Solving Problem Using Guards - UPDATED With CODE

I am using guards inside a function but not immediately after the function signature. The guards are under a do statement inside the body of the function. I get this error:
parse error on input `|'
I thought maybe the error comes from the indentation but i have tried many indentation but still i get the errors. Am asking is it because the guards are not immedaitely after the function signature that is why am getting the errors?
thanks
UPDATE 1
CODE:
The user is suppose to guess a number , and the number will be compared with the random number if they are the same. If it is not correct then the user will guess till the "guess" variable in the function is zero. in every interation that value(guess) is decreased by one.
for instance : puzz 12 5. the user can guess for five times, the random number will be picked between 1 and 12. that is how the function is suppose to do, but it is not working.
puzz :: Int -> Int -> IO ()
puzz boundary guess = do
putStr "Guess"
-- putStr -- I have to print (1 .. guess) here in each iteration
putStr ":"
x <- randomRIO (1, boundary :: Int)
n <- getLine
let
nTo = read n::Int
in print x
| guess == 0 = putStr "You couldn't guess right,the correct answer is" ++ x
| nTo > x = putStr "lower"
| nTo < x = putStr "higer"
| nTo == x = putStr "Congrat, You guess right."
| otherwise raad boundary (guess - 1)
the ouput must be like this:
Main> puzz 50 6
Guess a number betwee 1 en 50.
Guess 1: 49
lower
Guess 2: 25
lower
Guess 3: 12
higher
Guess 4: 18
higher
Guess 5: 21
higher
Guess 6: 23
lower
You couldn't guess correct, the answer was: 22.
thanks for your help
You’re using guards incorrectly. From the report:
Top level patterns in case expressions and the set of top level patterns in function or pattern bindings may have zero or more associated guards.
So they’re only for cases and function bindings. If you just want to concisely introduce a series of true-false tests, while inside a do-notation, perhaps the case () of () trick would work:
main = do
putStrLn "hello world"
n <- getLine
let nTo = read n :: Int
case ()
of () | cond -> putStrLn "foo"
| cond' -> putStrLn "bar"
| otherwise -> putStrLn "baz"
It should be noted that there are several things that are a bit off with your code, in addition to using guards wrong. By default output is buffered in haskell so if you want Guess to be on the same line as input you have to either say that stdOut should not be buffered (hSetBuffering stdOut NoBuffering), or you have to flush output with hFlush. It's not necessary to write boundary :: Int, the compiler knows it is an Int. Here is a bit more complete example, I'm sure it could be done better but atleast it works:
import Control.Monad(liftM,unless)
import System.IO(hFlush,stdout)
import System.Random(randomRIO)
guessMyNumber upper guesses = do
putStrLn $ "Guess a number between 1 and " ++ show upper ++ "!"
randomRIO (1, upper) >>= puzz guesses
puzz 0 number = do
putStrLn $ "Sorry, no more guesses, the number was "
++ show number ++ "."
puzz guesses number = do
putStr "Guess:" >> hFlush stdout
guess <- liftM read getLine
printMessage guess number guesses
printMessage guess number guesses
| number > guess = putStrLn "higer" >> puzz (guesses-1) number
| number < guess = putStrLn "lower" >> puzz (guesses-1) number
| number == guess = putStrLn "Congratulations! You guessed right!"

Resources