[Haskell-beginners] Keyboard input

legajid legajid at free.fr
Mon Dec 7 16:52:42 EST 2009


Hello,
ok for the responses.
However, about the strange behaviour of getChar, i forgot to say i'm 
running on Windows XP with GHci 6.10.4
It seems that getChar buffers all characters, including newline; so, 
typing A + enter is equivalent to typing 2 characters successively.
To successfully solve the problem, i did the following :

guessLetter :: IO Char
guessLetter = do
    putStrLn "Guess a letter (9 to end): "
    cl <- getLine
    let c= extr cl
    -- c <- getChar
    -- putChar '\b'
    return (toUpper c)

extr :: String -> Char
extr (c:cs)=c


Notice that, on my system, putChar has no visible effect.

Seeking for info, i found this mail : 
http://old.nabble.com/How-to-getCh-on-MS-Windows-command-line--td20414545.html
that talks about a similar issue on Windows

Didier.



Daniel Fischer a écrit :
> Am Samstag 05 Dezember 2009 23:20:22 schrieb legajid:
>   
>> Hello,
>> in order to get familiar with keyboard data entry, i'm writing a program
>> for the well-known Hangman game.
>> Each entry must change several lists (letters found and remaining letters).
>> I have three main difficulties with data entry :
>>
>> First When i enter a letter (A) , i get a good response (displaying old
>> word and letters, message "guess ok") and an unattended one that says
>> "guess false", thus displaying the result from my entry.
>> I don't understand why the process seems to execute twice, the fisrt one
>> being correct, the second not.
>>     
>
> I don't get that behaviour, for me, after each guess, it displays only one message.
>
>   
>> Second difficulty : the  "procedure"  for data entry is written twice
>> ("guess a letter ..." prompt). I think it would be a good idea to write
>> it once as a "function" that would display the prompt then get a
>> character from the keyboard.
>>     
>
> Also, the entered letter appears before the word, which is not nice.
> You can either
>
> import System.IO
>
> and at the beginning of hangman
>
>  hSetEcho stdin False
>
> and at the end
>
> do putStrLn "Done"
>    hSetEcho stdin True
>
> or, without futzing with the echo setting:
>
> import Data.Char (toUpper)
> import Data.List (delete)
>
> guessLetter :: IO Char
> guessLetter = do
>     putStrLn "Guess a letter (9 to end): "
>     c <- getChar
>     putChar '\b'
>     return (toUpper c)
>
>   
>> Third difficulty : in both cases (guess ok or false), i have to ask for
>> a new entry; to avoid writing this twice again, i write it before the
>> recursive calls of process_guess. Writing a function would perhaps allow
>> to call it directly as a parameter of process_guess ?
>>     
>
> It would be cleaner to not have the guess as a parameter for the game loop.
>
>   
>> Being new to haskell,  recursive functions now seem clear to me but i
>> feel that IOs make writing a program more difficult. And i've not yet
>> tried data base access...
>>
>> Please, say me if the way i wrote my program is a good or bad approach.
>>
>> Below an example of a run :
>>     
>
> Forgot that, didn't you?
>
>   
>> Here's my program.
>> Could you help me.
>> Thanks,
>> Didier
>>
>> solution="HANGMAN"
>> word="H-----N"
>> letters=['A'..'Z']
>>     
>
> It would be good to have a list of letters to be guessed,
>
> wletters = "AGMN"
>
> and check whether the guess appears there (-> Guess OK)
> or not (-> Guess false :()
> and remove correctly guessed letters from this, so when there are no more letters to be 
> guessed you can detect it and
>
> putStrLn "Congratulations"
>
> to end the game.
>
> The list of letters as allowed guesses is unnecessary, just check whether
>
> 'A' <= g && g <= 'Z'
>
> where g is toUpper (entered letter), or don't care whether what is entered is a letter or 
> something else.
>
>   
>> hangman = do
>>   -- enter a letter
>>   putStrLn "Guess a letter (9 to end):"
>>   guess <- getChar
>>   -- process
>>   process_guess guess letters word
>>     
>
> hangman = hangloop wletters word
>
>   
>> process_guess pg pletters pword =do
>>     
>
> hangloop "" pword = do
>     putStrLn pword
>     putStrLn "Congratulations, you've solved it!"
> hangloop pletters pword = do
>     -- best to display the word first
>     putStrLn pword
>     g <- guessLetter
>     if g == '9'
>       then putStrLn "Bye"
>       else do
>             -- check whether the guess is good
>             if g `notElem` pletters
>               then do
>                 putStrLn "Wrong guess"
>                 hangloop pletters pword
>               else do
>                 putStrLn "Good guess"
>                 let nletters = delete g pletters
>                     nword = newword pword g solution
>                 hangloop nletters nword
>
>   
>>   let pguess = toUpper pg
>>   putStrLn pword
>>   putStrLn pletters
>>   if pguess == '9'
>>     then do putStrLn "Done"
>>     else do
>>       if pguess `elem` pletters
>>         then do putStrLn "Guess OK \n"
>>         else do putStrLn "Guess false \n"
>>
>>       -- Enter a new letter before going on, in both cases
>>       putStrLn "Guess a letter (9 to end):"
>>       guess <- getChar
>>
>>       if pguess `elem` pletters
>>         -- guess ok -> remove guess from available letters then add
>> guess to word
>>         then do process_guess guess (newletters pletters pguess)
>> (newword pword pguess solution)
>>
>>         -- guess false -> remove guess from available letters, leave
>> word unchanged
>>         else do process_guess guess (newletters pletters pguess) pword
>>
>>
>> newletters l g =
>>   filter (/= g) l
>>     
>
> if every letter appears only once in l, Data.List.delete g l is perhaps better.
>
>   
>> newword [] _ _ = []
>> newword (w:ws) g (s:ss) =
>>   if w == '-'
>>     then
>>       if s == g
>>         then g : newword ws g ss
>>         else w : newword ws g ss
>>     else
>>       w : newword ws g ss
>>     
>
> better pattern-match:
>
> newword "" _ _ = ""
> newword ('-':ws) g (s:ss)
>     | g == s    = g:newword ws g ss
> newword (w:ws) g (s:ss) = w:newword ws g ss
>
> or, for the nonempty case:
>
> newword (w:ws) g (s:ss)
>     | w == '-' && s == g  = g:newword ws g ss
>     | otherwise = w:newword ws g ss
>
>   
>>     
>
> -- these are in Data.Char
>
>   
>> toUpper c
>>
>>   | isLower c = toEnum (fromEnum c - fromEnum 'a' + fromEnum 'A')
>>   | otherwise = c
>>
>> isLower c = c >= 'a' && c <= 'z'
>>     
>
>
>   



More information about the Beginners mailing list