Keyboard input/Obtain a Y or N response
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.
You are encouraged to solve this task according to the task description, using any language you may know.
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
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>
C
For POSIX compliant systems (in theory that includes WinNT family). <lang C>#include <stdio.h>
- include <stdio.h>
- include <termios.h>
- include <unistd.h>
- 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>
Common Lisp
<lang lisp>(defun rosetta-y-or-n ()
(clear-input *query-io*) (y-or-n-p))</lang>
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>
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>
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>
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>
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>
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
REXX (in general) requires the user to press ENTER when
entering text.
Some REXX interpretors have a keyboard read subroutine so that the
program can read keyboard keys as they are pressed.
<lang rexx>
do queued() /*flush the stack if any lines queued. */ pull end
prompt='Press Y or N for some reason.' /*prompt message*/ ok='Y N' /*acceptable answers (will be uppercase)*/
do forever say /*write a blank line for visual fidelity.*/ say prompt /*prompt the user for an input. */ pull ans /*get the answer(s), also, uppercase it. */ ans=strip(ans) /*get rid of leading/trailing blanks. */ if ans= then iterate /*if blank, try again.*/ if wordpos(ans,ok)\==0 then leave /*if ans is OK, leave.*/ end
/*as this point, ANS holds a Y or N. */
</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>
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>