[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