Keyboard input/Obtain a Y or N response

From Rosetta Code
Revision as of 02:14, 19 February 2013 by Markjreed (talk | contribs) (→‎{{header|Commodore BASIC}}: Add note about GETKEY.)
Task
Keyboard input/Obtain a Y or N response
You are encouraged to solve this task according to the task description, using any language you may know.

Obtain a valid Y or N response from the keyboard. The keyboard should be flushed, so that any outstanding keypresses are removed, preventing any existing Y or N keypress from being evaluated. The response should be obtained as soon as Y or N are pressed, and there should be no need to press an enter key.

Ada

<lang Ada> function Yes_Or_No (Prompt : String := "Your answer (Y/N): ") return Boolean is

     Answer : Character;
  begin
     Ada.Text_IO.Put (Prompt);
     loop
        Ada.Text_IO.Get_Immediate (Answer);
        case Answer is
           when 'Y'|'y' => return True;
           when 'N'|'n' => return False;
           when others  => null;
        end case;
     end loop;
  end Yes_Or_No;</lang>

AutoHotkey

<lang AutoHotkey>#NoEnv ; Recommended for increased performance Input, Response,,,y,n

Waits until they press y or n, storing all characters.

StringRight, Out, Response, 1

retrieves the ending character (which will be y or n)

Msgbox %out% If (Out = "y")

   Msgbox You pressed Y

If (Out = "n")

   Msgbox You pressed n

ExitApp </lang>

BASIC

Applesoft BASIC

<lang applesoftbasic>10 LET C = PEEK (49168): REM CLEAR KEYBOARD 20 PRINT "PRESS Y OR N TO CONTINUE" 30 GET K$ 40 IF K$ < > "Y" AND K$ < > "N" THEN 30 50 PRINT "THE RESPONSE WAS ";K$ </lang>

Locomotive Basic

<lang locobasic>10 CLEAR INPUT 20 PRINT "Press Y or N to continue" 30 a$=LOWER$(INKEY$) 40 IF a$="" THEN 30 50 IF a$="y" THEN PRINT "Yes":END 60 IF a$="n" THEN PRINT "No":END 70 PRINT "Try again" 80 GOTO 30</lang>

ZX Spectrum Basic

Note that this will also work in GW-BASIC and most QBasic-compatible BASICs if all instances of "GO TO" are changed to "GOTO".

<lang qbasic>10 IF INKEY$<>"" THEN GO TO 10: REM flush the keyboard buffer 20 PRINT "Press Y or N to continue" 30 LET k$ = INKEY$ 40 IF k$ <> "y" AND k$ <> "Y" AND k$ <> "n" AND k$ <> "N" THEN GO TO 30 50 PRINT "The response was "; k$</lang>

BBC BASIC

<lang bbcbasic> REPEAT UNTIL INKEY$(0) = ""

     PRINT "Press Y or N to continue"
     REPEAT
       key$ = GET$
     UNTIL key$="Y" OR key$="N"
     PRINT "The response was " key$</lang>

Commodore BASIC

<lang basic>10 PRINT "PRESS Y OR N TO CONTINUE:"; 20 POKE 198, 0: REM CLEAR KEY BUFFER 30 GET K$ 40 IF K$ <> "Y" AND K$ <> "N" THEN 30 50 PRINT K$</lang>

Note that 198 is the location of the keyboard buffer index on the VIC-20, C-64, and C-128. On the PET, the correct location is 158, while on the Plus/4 and C-16, it's 239.

The loop on lines 30 - 40 will cycle as fast as the interpreter can go, assigning K$ the empty string until the user presses a key. On versions of BASIC later than the 2.0 on the VIC and 64 (e.g. 3.5 on the C-16 and Plus/4, 7.0 on the C-128), GETKEY may be used in place of GET; it will wait for the user to press a key before continuing, so the polling is done in the BASIC interpreter's machine language code instead of at the BASIC level.

Batch File

batch has a choice command.. <lang dos>CHOICE /m "Press Y for yes or N for no." IF ERRORLEVEL 2 GOTO No IF ERRORLEVEL 1 GOTO Yes ECHO Cancel GOTO End

Yes

ECHO Your answer was YES. GOTO End

No

ECHO Your answer was NO.

End </lang>

C

For POSIX compliant systems (in theory that includes WinNT family). <lang C>#include <stdio.h>

  1. include <stdio.h>
  2. include <termios.h>
  3. include <unistd.h>
  4. include <fcntl.h>

void set_mode(int want_key) { static struct termios old, new; if (!want_key) { tcsetattr(STDIN_FILENO, TCSANOW, &old); return; }

tcgetattr(STDIN_FILENO, &old); new = old; new.c_lflag &= ~(ICANON); tcsetattr(STDIN_FILENO, TCSANOW, &new); }

int get_key(int no_timeout) { int c = 0; struct timeval tv; fd_set fs; tv.tv_usec = tv.tv_sec = 0;

FD_ZERO(&fs); FD_SET(STDIN_FILENO, &fs);

select(STDIN_FILENO + 1, &fs, 0, 0, no_timeout ? 0 : &tv); if (FD_ISSET(STDIN_FILENO, &fs)) { c = getchar(); set_mode(0); } return c; }

int main() { int c; while(1) { set_mode(1); while (get_key(0)); /* clear buffer */ printf("Prompt again [Y/N]? "); fflush(stdout);

c = get_key(1); if (c == 'Y' || c == 'y') { printf("\n"); continue; }

if (c == 'N' || c == 'n') { printf("\nDone\n"); break; }

printf("\nYes or no?\n"); }

return 0; }</lang>

C#

<lang c sharp>using System;

namespace Y_or_N {

   class Program
   {
       static void Main()
       {
           bool response = GetYorN();
       }
       static bool GetYorN()
       {
           ConsoleKey response; // Creates a variable to hold the user's response.
           do
           {
               while (Console.KeyAvailable) // Flushes the input queue.
                   Console.ReadKey();
               Console.Write("Y or N? "); // Asks the user to answer with 'Y' or 'N'.
               response = Console.ReadKey().Key; // Gets the user's response.
               Console.WriteLine(); // Breaks the line.
           } while (response != ConsoleKey.Y && response != ConsoleKey.N); // If the user did not respond with a 'Y' or an 'N', repeat the loop.
            /* 
             * Return true if the user responded with 'Y', otherwise false.
             * 
             * We know the response was either 'Y' or 'N', so we can assume 
             * the response is 'N' if it is not 'Y'.
             */
           return response == ConsoleKey.Y;
       }
   }

}</lang>

Common Lisp

This example is incorrect. Please fix the code and remove this message.

Details: The standard library function y-or-n-p requires the user to press Enter, in violation of the requirements.

<lang lisp>(defun rosetta-y-or-n ()

 (clear-input *query-io*)
 (y-or-n-p))</lang>

D

<lang d>import std.stdio: stdout, write, writefln;

extern (C) nothrow {

   void _STI_conio();
   void _STD_conio();
   int kbhit();
   int getch();

}

void main() {

   _STI_conio();
   write("Enter Y or N: ");
   stdout.flush();
   int c;
   do {
       while(!kbhit()) {}
       c = getch();
       // Visual feedback for each keypress.
       write(cast(char)c);
       stdout.flush();
   } while(c != 'Y' && c != 'y' && c != 'N' && c != 'n');
   writefln("\nResponse: %c", cast(char)c);
   _STD_conio();

}</lang>

Output:
Enter Y or N: abcN
Response: N

Euphoria

<lang Euphoria>integer key

puts(1,"Your answer? (Y/N)\n") while get_key()!=-1 do end while

while 1 do

   key = get_key()
   if key!=-1 and (key = 'Y' or key = 'y' or key = 'N' or key = 'n') then
       exit
   end if

end while

printf(1,"Your response was %s\n",key)</lang>

EGL

Works with: EDT
Works with: RBD

<lang EGL>handler YesOrNoHandler type RUIhandler{initialUI =[ui], onConstructionFunction = start}

   ui Div { };
   const KEY_N int = 78;
   const KEY_Y int = 89;
   function start()
   	document.onKeyDown = d_onKeyDown;
   end
   
   function d_onKeyDown(e Event in)

case (e.ch)

 	    when (KEY_N)

ui.innerText = "N pressed."; when (KEY_Y) ui.innerText = "Y pressed."; end

e.preventDefault();

   end

end</lang>

Forth

<lang Forth>: flush ( -- ) \ discard pending input

 begin key? while key drop repeat ;
y-or-n ( c-addr u -- f )
 flush begin
   cr 2dup type key bl or                  \ note 1.
   dup [char] y = swap [char] n = over or  \ note 2.
   if nip nip exit then
 drop again ;

\ Note 1. KEY BL OR returns a lowercase letter in the case that an \ uppercase letter was entered, an unchanged lowercase letter in the \ case that a lowercase letter was entered, and garbage otherwise. BL \ returns the ASCII code for a space, 32, which is incidentally the \ "bit of difference" between ASCII uppercase and lowercase letters.

\ Note 2. this line has the stack effect ( x -- f1 f2 ), where F1 is \ true only if x='y', and F2 is true only if x='y' OR if x='n'.

\ I think these expressions aren't too clever, but they _are_ rather \ optimized for the task at hand. This might be more conventional:

y-or-n ( c-addr u -- f )
 flush begin
   cr 2dup type key case
     [char] y of 2drop true  exit endof
     [char] Y of 2drop true  exit endof
     [char] n of 2drop false exit endof
     [char] N of 2drop false exit endof
 endcase again ;</lang>

GW-BASIC

<lang qbasic>10 IF INKEY$<>"" THEN GOTO 10: REM flush the keyboard buffer 20 PRINT "Press Y or N to continue" 30 LET k$ = INKEY$ 40 IF k$ <> "y" AND k$ <> "Y" AND k$ <> "n" AND k$ <> "N" THEN GOTO 30 50 PRINT "The response was "; k$</lang>

Haskell

This may not be very idiomatic:

<lang haskell>import System.IO

hFlushInput :: Handle -> IO () hFlushInput hdl = do

 r <- hReady hdl
 if r then do
   c <- hGetChar hdl
   hFlushInput hdl
 else
   return ()

yorn :: IO Char yorn = do

 c <- getChar
 if c == 'Y' || c == 'N' then return c
 else if c == 'y' then return 'Y'
 else if c == 'n' then return 'N'
 else yorn

main :: IO () main = do

 hSetBuffering stdout NoBuffering
 putStr "Press Y or N to continue: "
 hSetBuffering stdin NoBuffering
 hSetEcho stdin False
 hFlushInput stdin
 answer <- yorn
 putStrLn [answer]</lang>

Icon and Unicon

This solution works in both Icon and Unicon. It also accepts y or n. <lang unicon>procedure main()

   write("Response was ",getResponse("OK? (Y or N): "))

end

procedure getResponse(prompt)

   while kbhit() do getch()   # flush input
   writes(prompt)
   repeat if map(answer := getch()) == ("y"|"n") then break
   return answer

end</lang>

Inform 7

Keyboard input goes through a virtual machine that's only required to provide blocking input operations, so flushing the buffer isn't possible.

Inform 7 has a built-in function to ask the user for yes-or-no input, but it requires them to press enter afterward: <lang inform7>Qwantz is a room.

When play begins: say "A wizard has turned you into a whale. Is this awesome (Y/N)? "; if the player consents, say "Awesome!"; end the story.</lang>

To read a single key without waiting for enter, we can redefine the function by including a snippet of Inform 6 code: <lang inform7>To decide whether player consents: (- (YesOrNoKey()) -).

Include (- [ YesOrNoKey ch;

   do { ch = VM_KeyChar(); } until (ch == 'y' or 'Y' or 'n' or 'N');
   return ch == 'y' or 'Y';

]; -).</lang>

Liberty BASIC

<lang lb> nomainwin open "Y/N" for graphics_nsb_nf as #1

  1. 1 "trapclose Quit"
  2. 1 "down;setfocus;when characterInput KeyCheck"
  3. 1 "place 10 50;\Press Y or N"

Inkey$="" wait

sub KeyCheck hndle$,k$

   k$=upper$(k$)
   #hndle$ "cls;place 10 50"
   select case k$
       case "Y"
       #hndle$ "\ Yes"
       case "N"
       #hndle$ "\No"
       case else
       #hndle$ "\Incorrect input. Press Y or N"
       end select
   end sub

sub Quit hndle$

   close #hndle$
   end
   end sub
</lang>

<lang logo>to yorn

 type [Press Y or N to continue: ]
 local "clear
 make "clear readchars 0 ; clear input buffer
 local "yorn
 do.until [make "yorn readchar] [or equal? :yorn "Y equal? :yorn "N]
 print :yorn
 output :yorn

end</lang>

OpenEdge/Progress

<lang progress>DEF VAR lanswer AS LOGICAL INITIAL ?.

DO WHILE lanswer = ?:

  READKEY.
  IF CHR( LASTKEY ) = "n" OR CHR( LASTKEY ) = "y" THEN
     lanswer = CHR( LASTKEY ) = "y".

END.

MESSAGE lanswer VIEW-AS ALERT-BOX.</lang>

Pascal

Works with: Free_Pascal
Library: CRT

<lang pascal>Program ObtainYN;

uses

 crt;

var

 key: char;

begin

 write('Your answer? (Y/N): ');
 repeat
   key := readkey;
 until (key in ['Y', 'y', 'N', 'n']);
 writeln;
 writeln ('Your answer was: ', key);

end.</lang> Output:

% ./ObtainYN
Your answer? (Y/N): 
Your answer was: y

Perl

<lang perl>use Term::ReadKey;

ReadMode 4; # change to raw input mode

my $key = ;

while($key !~ /(Y|N)/i) {

   1 while defined ReadKey -1; # discard any previous input
   print "Type Y/N: ";
   $key = ReadKey 0; # read a single character
   print "$key\n";

}

ReadMode 0; # reset the terminal to normal mode

print "\nYou typed: $key\n"; </lang>

Perl 6

<lang perl6>my $TTY = open("/dev/tty");

sub prompt-char($prompt) {

   ENTER shell "stty raw -echo min 1 time 1";
   LEAVE shell "stty sane";
   print $prompt;
   $TTY.getc;

}

say so prompt-char("Y or N? ") ~~ /:i y/;</lang>

PicoLisp

<lang PicoLisp>(de yesno ()

  (loop
     (NIL (uppc (key)))
     (T (= "Y" @) T)
     (T (= "N" @)) ) )</lang>

PureBasic

Inkey() returns the character string of the key which is being pressed at the time. <lang PureBasic>PrintN("Press Y or N to continue")

Repeat

 ; Get the key being pressed, or a empty string.
 Key$=UCase(Inkey())
 ;
 ; To Reduce the problems with an active loop
 ; a Delay(1) will release the CPU for the rest
 ; of this quanta if no key where pressed.
 Delay(1)

Until Key$="Y" Or Key$="N" PrintN("The response was "+Key$)</lang>

Python

<lang python>#!/usr/bin/env python

try:

   from msvcrt import getch

except ImportError:

   def getch():
       import sys, tty, termios
       fd = sys.stdin.fileno()
       old_settings = termios.tcgetattr(fd)
       try:
           tty.setraw(sys.stdin.fileno())
           ch = sys.stdin.read(1)
       finally:
           termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
       return ch

print "Press Y or N to continue" while True:

   char = getch()
   if char.lower() in ("y", "n"):
       print char
       break</lang>
   

Retro

<lang Retro>

 : y|n ( -c )
   "\nPress Y or N..." puts
   0 [ drop getc dup [ 'Y <> ] [ 'N <> ] bi and ] while cr ;

</lang>

REXX

version for all classic REXXes

This version works with all classic REXXes.

REXX (in general) requires the user to press the   ENTER   key after entering text.
This is because the original (IBM) REXX was designed and written for a system when all I/O to a user's terminal screen was
in block mode and required the user to press one of the following before any data was sent to the computer:

  • the   ENTER   key
  • a   PF     (program function key)
  • a   PA     (program assist key)
  • the   ATTN     (attention) key
  • possibly some other special key(s)


Note that the above keys may have different names on terminals that emulate an IBM 3270 type terminal.
Some REXX interpreters have a keyboard read subroutine (bif) so that the program can
read keyboard keys as they are pressed   (see version 2 below). <lang rexx>/*REXX program to test for a Y or N key when entered after a prompt.*/

 do queued();   pull;   end          /*flush stack if anything queued. */

prompt = 'Please enter Y or N for verification:' /*PROMPT msg.*/

                                     /* [↓] keep prompting til answered*/
 do until pos(ans,'NY')\==0 & length(space(ans,0))==1     /*··· Y | N ?*/
 say;      say prompt                /*show blank line, show the prompt*/
 pull ans                            /*get the answer(s) & uppercase it*/
 end   /*until*/
                                     /*stick a fork in it,  we're done.*/</lang>

version for PC/REXX and Personal REXX

This version of a REXX program works with PC/REXX and Personal REXX. <lang rexx>/*REXX program to test for a Y or N key when pressed. */ prompt='Press Y or N for some reason.' /*the PROMPT message.*/

 do until pos(ans,'NY')\==0   /*keep prompting until user answers Y | N*/
 say;   say prompt            /*show blank line, then show the prompt. */
 ans=inkey('wait'); upper ans /*get the answer(s), also, uppercase it. */
 end   /*until*/
                              /*stick a fork in it,  we're all done.   */</lang>

Ruby

<lang Ruby> def yesno

 begin
   system("stty raw -echo")
   str = STDIN.getc
 ensure
   system("stty -raw echo")
 end
 if str == "Y"
   return true
 elsif str == "N"
   return false
 else
   raise "Invalid character."
 end

end </lang>

Run BASIC

<lang runbasic>[loop] cls ' Clear screen html "Click Y or N" ' no other options

     button #y, "Y", [Y]                '   they either click [Y]
     button #n, "N", [N]                '   or they click [N]

html "
";msg$ ' print message showing what they entered wait [Y] msg$ = "You entered [Y]es": goto [loop] [N] msg$ = "You entered [N]o" : goto [loop] </lang>

Seed7

<lang seed7>$ include "seed7_05.s7i";

 include "keybd.s7i";

const func boolean: yesOrNo (in string: prompt) is func

 result
   var boolean: yes is FALSE;
 local
   var char: answer is ' ';
 begin
   while keypressed(KEYBOARD) do
     ignore(getc(KEYBOARD));
   end while;
   write(prompt);
   repeat
     answer := lower(getc(KEYBOARD));
   until answer in {'y', 'n'};
   yes := answer = 'y';
 end func;

const proc: main is func

 begin
   writeln(yesOrNo("Press Y or N to continue "));
 end func;</lang>

Tcl

Using the console (expects U*Xish stty)

<lang tcl>proc yesno Template:Message "Press Y or N to continue" {

   fconfigure stdin -blocking 0
   exec stty raw
   read stdin ; # flush
   puts -nonewline "${message}: "
   flush stdout
   while {![eof stdin]} {
       set c [string tolower [read stdin 1]]
       if {$c eq "y" || $c eq "n"} break
   }
   puts [string toupper $c]
   exec stty -raw
   fconfigure stdin -blocking 1
   return [expr {$c eq "y"}]

}

set yn [yesno "Do you like programming (Y/N)"]</lang>

Without a console (answer in the global variable yn; this should work in any GUI for which there is a TCL):

<lang tcl> proc yesno {message} {

 toplevel .msg 
 pack [label .msg.l -text "$message\n (type Y/N)?"]
 set ::yn ""
 bind .msg <Key-y> {set ::yn "Y"}
 bind .msg <Key-n> {set ::yn "N"}
 vwait ::yn
 destroy .msg

}

yesno "Do you like programming?"

</lang>

Vedit macro language

<lang vedit>Key_Purge() // flush keyboard buffer do {

   #1 = Get_Key("Are you sure? (Y/N): ")       // prompt for a key
   #1 &= 0xdf                                  // to upper case

} while (#1 != 'Y' && #1 != 'N') </lang>

XPL0

<lang XPL0>include c:\cxpl\codes; \intrinsic 'code' declarations loop [OpenI(1); \flush any pending keystroke

       case ChIn(1) of         \get keystroke
        ^Y,^y: Text(0, "yes");
        ^N,^n: Text(0, "no");
        $1B:   quit            \Esc key terminates program
       other ChOut(0, 7\bel\);
       CrLf(0);
       ]</lang>