Consider the following (very dull) game: - Player A thinks of a number between 1 and 100. - Player B is allowed 5 attempts to guess the number. Player A will respond to each guess as "too big", "too small" or "correct".
I wanted to simulate this in Haskell, which is trivial, of course. To make things interesting, though, I wanted to write the code in such a way that Player B can't "cheat". That means two things: - The Player B code isn't allowed to see the correct value of the secret number. The Player A code gives the Player B code a function with which to check its guesses. - The Player B code isn't allowed to call that function more than five times. Somehow, the Player A code has to keep count of how many times the function is called.
This is very easy to achieve in an OO language with private mutable variables.
In Haskell, I coded it using an IORef to keep count of the number of calls. That's fine, I think my solution is correct. But my question is:
"Can this be done in Haskell without IORef or similar? Is there a purely functional solution that I've missed?"
Here is my Haskell code:
import Data.IORef (newIORef, readIORef, writeIORef)
import System.Random (randomRIO)
lowest = 1
highest = 100
maxtries = 5
playerA :: IO (Int -> IO (Maybe Ordering))
playerA = do
secret <- randomRIO (lowest, highest)
print ("Secret", secret)
tries <- newIORef maxtries
return $ \ guess -> do
t <- readIORef tries
if t == 0 then
return Nothing
else do
writeIORef tries $ t - 1
return $ Just $ guess `compare` secret
playerB :: (Int -> IO (Maybe Ordering)) -> Int -> Int -> IO ()
playerB guessfunc lowbound highbound = do
let guess = (lowbound + highbound) `div` 2
response <- guessfunc guess
print (lowbound, highbound, guess, response)
case response of
Just GT -> playerB guessfunc lowbound (guess - 1)
Just LT -> playerB guessfunc (guess + 1) highbound
Just EQ -> putStrLn "Player B wins"
Nothing -> putStrLn "Player B loses"
main :: IO ()
main = do
guessfunc <- playerA
playerB guessfunc lowest highest