I am writing a snake game in C using the ncurses library, where the screen updates itself every second. As those who have played the game will know, if the user enters various keys or holds down a key for long, there should be no 'buffered' key presses which get stored. In other words, if I hold down w (the up key) and stdin receives a sequence of 20 ws, and subsequently enter a d (the right key), I expect the snake to immediately move to the right, and ignore the buffered ws.
I am trying to achieve this using the ncurses function getch(), but for some reason I am achieving the undesired effect which I have just described; namely that any long key presses are being stored and processed first, before considering the last key pressed, as this MWE will illustrate:
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
int main(){
char c = 'a';
initscr();
cbreak();
noecho();
for(;;){
fflush(stdin);
timeout(500);
c = getch();
sleep(1);
printw("%c", c);
}
return 0;
}
Is there any way I can modify this code so that getch() ignores any buffered text? The fflush() right before doesn't seem to be helping.
First the answer to your immediate question: Even when you read from stdin one character at a time, you normally don't want to miss one. If getch() would just return whatever character was entered last, input routines using getch() would become very unreliable.
Even in a snake game, you DO want to get all the characters entered. Imagine the snake moves to the right and the player wants to do a "u-turn" by hitting down and then immediately left. If your game would only pick up the last character, the snake would go directly to the left and thus kill itself. Quite a frustrating game experience ;)
The solution to your problem is simple: make your game loop poll for input much more often than the snake is moved and maintain a queue of requested direction changes. I've done that in my curses-based snake game.
Here is the relevant code from the game loop:
typedef enum
{
NONE,
LEFT,
DOWN,
RIGHT,
UP
} Dir;
// [...]
ticker_start(10);
while (1)
{
screen_refresh(screen);
ticker_wait();
key = getch();
if (key == 'q' || key == 'Q')
{
// quit game
}
switch (key)
{
case KEY_LEFT:
snake_setDir(snake, LEFT);
break;
case KEY_DOWN:
snake_setDir(snake, DOWN);
break;
case KEY_UP:
snake_setDir(snake, UP);
break;
case KEY_RIGHT:
snake_setDir(snake, RIGHT);
break;
case ' ':
// pause game
break;
}
if (!--nextStep)
{
step = snake_step(snake); // move the snake
// code to check for food, killing, ...
}
if (!--nextFood)
{
// add a new food item
}
}
ticker_stop();
And here is how the snake implements and uses the queue:
struct snake
{
// queue of requested directions:
Dir dir[4];
// more properties
};
void
snake_setDir(Snake *self, Dir dir)
{
int i;
Dir p;
p = self->dir[0];
for (i = 1; i < 4; ++i)
{
if (self->dir[i] == NONE)
{
if (dir != p) self->dir[i] = dir;
break;
}
p = self->dir[i];
}
}
static void dequeueDir(Snake *self)
{
int i;
if (self->dir[1] != NONE)
{
for (i=0; i<3; ++i) self->dir[i] = self->dir[i+1];
self->dir[3] = NONE;
}
}
Step
snake_step(Snake *self)
{
dequeueDir(self);
// [...]
switch (self->dir[0])
{
case LEFT:
--newHead->x;
break;
case DOWN:
++newHead->y;
break;
case RIGHT:
++newHead->x;
break;
case UP:
--newHead->y;
break;
default:
break;
}
// [...]
}
Good luck with your game!
I've done some more research and found out that:
The flushinp() routine throws away any typeahead that has been typed by the user and has not yet been read by the program.
This solved my problem.
Source: https://linux.die.net/man/3/flushinp
the following proposed code shows how to ignore all but the last key entered.
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
int main( void )
{
char c = ' ';
char lastKey;
initscr();
cbreak();
noecho();
for(;;)
{
timeout(500);
while( (c = getch()) != EOF )
{
lastKey = c;
}
sleep(1);
if( EOF == c )
{
printw("%c", lastKey);
}
}
return 0;
}
Related
I'm making a multithreaded console game where I have to move an actor and shoot the enemies. Moving and shooting are two different threads and they both must listen to the keyboard input.
I've tried listening to the input in the while loop in main function and passing it by address to both threads, where an infinite loop is processing this character but this way the actor just jumps into the corners, my guess is that these loops are processing the character very fast and the character stays the same even after one key pressed, so the thread moves an actor every time the loop positively checks the character
If I put getchar() in both threads, only one of them will be functional.
int main() {
...
do {
cur_c = getchar();
if(!created){
pthread_create(&funcs[0], NULL, shot, (void *) &cur_c);
pthread create(&funcs[1], NULL, actor, (void *) &cur_c);
created = true;
}
if(...)
break;
} while (1);
...
}
}
void *actor(void *ch)
{
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
int *cur_char = ch;
while(1){
switch(*cur_char){
case 'w':
goUp();
break;
case 'a':
goLeft();
break;
...
}
}
pthread_exit(NULL);
}
void *shot(void *ch)
{
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
int *cur_char = ch;
while(1){
if(*cur_char == ' ') {
Position pos = ...;
fire(&pos);
}
}
pthread_exit(NULL);
}
I expect both of these functions to listen for an input but this way:
if I press ' ' the actor will fire once or if I press 'w' will move up once and not until I press any other key.
My question is whether it is possible to make getchar() in separate threads listen to an input simultaneously? If so, how is it possible?
I'm creating a menu driven program in C where I have to ask for user input, then use only the first character of the input in my program. In addition to the code below, I've also tried #define MAX_CHAR 1 and using it instead of EOF in the while loop, among other things.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void print_menu();
void uppercase(char *string);
int main()
{
char i = 0, c = 0;
char selection[0];
print_menu();
while((c = getchar()) != EOF)
{
selection[i++] = c;
selection[i] = '\0'; //insert NULL at end of string
uppercase(selection);
switch(selection[i])
{
case 'c':
uppercase(selection);
printf("%s\n", selection );
print_menu();
break;
case 'X':
printf("The program is exiting, Schuss!\n" );
exit(0);
default:
printf("\nYou entered: %s\n", selection);
print_menu();
break;
}
}
return 0;
}
void print_menu() //gives code for menu
{
printf("Select a menu item and hit enter: \n" );
printf("C) Convert the string to uppercase\n");
printf("M) Display this menu\n");
printf("X) Exit the program\n");
}
void uppercase(char *string)
{
int c = 0;
while (string[c] != '\0')
{
if (string[c] >= 'a' && string[c] <= 'z')
{
string[c] = string[c] - 32;
}
c++;
}
}
If I type Yes! when running the program, I expect the output to be
You entered: Yand then the menu prints.
Currently the output is
You entered: Y
Select a menu item and hit enter:
C) Convert the string to uppercase
M) Display this menu
X) Exit the program
You entered: YE
Select a menu item and hit enter:
C) Convert the string to uppercase
M) Display this menu
X) Exit the program
You entered: S
Select a menu item and hit enter:
C) Convert the string to uppercase
M) Display this menu
X) Exit the program
You entered: S!
Select a menu item and hit enter:
C) Convert the string to uppercase
M) Display this menu
X) Exit the program
You entered:
Select a menu item and hit enter:
C) Convert the string to uppercase
M) Display this menu
X) Exit the program
^C
I'm pretty sure it's a problem with the while loop, haven't figured out how to fix it though. Eventually I'll have more menu items and cases. So for example, the user will enter A and it'll print a then print the menu again to wait for the next menu selection until the user enters 'X' to exit. Also, I do know there are two function calls for uppercase and that's intentional at this point.
EDIT: I'm wanting getchar() to read one character as it's designed to do. Then do whatever matches the case for that character. Then print the menu again (this part is included in each case statement). Then repeat from step 1 (which is "read one character").
I've changed my code to put getchar() outside the while loop and set the loop to while(1) instead which does result in reading only one character but also creates an infinite loop printing You entered: B and the menu. So that's progress, kind of.
How to read a single character with getchar in C?
You can read ONLY a single character using getchar.
I have to ask for user input, then use only the first character of the input in my program.
You can do that by reading an entire line and using only the first character of the line.
char line[100]; // Make it large enough.
while( fgets(line, 100, stdin) != NULL )
{
uppercase(line);
char selection = line[0];
switch (selection)
{
...
}
}
How to read a single character with getchar in C?
Just in this way:
getchar();
By the way, you are overwriting some memory here:
char i = 0, c = 0;
char selection[0]; // What?! An array of 0 elements?!
...
selection[i++] = c; // Where are you storing the value?
selection[i] = '\0'; // Same here
And note that after some while loops, your i variable will have a value of 12? 34? 1234?, because you don't initilize it to 0 before you do selection[i++] = c, each time you do it.
even though the following code has no undefined behavior and does allow the user to input up to 128 characters, there is nothing in the OPs code, nor does this code that makes use of the first (up to) 4 characters
#include <stdio.h> // getchar(), prinf()
//#include <string.h>
//#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <ctype.h> // toupper()
void print_menu( void );
//void uppercase( char *string );
#define MAX_CHARS (128)
int main( void )
{
size_t i = 0;
int c; // getchar() returns an int, not a char
//char selection[0];
char selection[ MAX_CHARS+1 ]; // +1 to allow for NUL terminator byte
print_menu();
while( i < MAX_CHARS && (c = getchar()) != EOF && 'X' != toupper(c) )
{
if( '\n' == c ) // ignore any newlines
continue;
selection[i++] = (char)toupper(c);
selection[i] = '\0'; //insert NUL byte at end of string
//uppercase(selection);
switch(selection[i-1]) // -1 because 'i' is the termination byte
{
//case 'c':
// //uppercase(selection);
// printf("%s\n", selection );
// print_menu();
// break;
//case 'X':
// printf("The program is exiting, Schuss!\n" );
// //exit(0);
// exit( EXIT_SUCCESS );
// break;
default:
printf("\nYou entered: %s\n", selection);
print_menu();
break;
}
}
printf("The program is exiting, Schuss!\n" );
//return 0; // not needed for 'main()' in modern C
} // end function: main
void print_menu() //gives code for menu
{
// note: C concatenates successive literal strings
printf("Select a menu item and hit enter: \n"
"X) Exit the program\n"
"any other key appends to input, except newline\n");
} // end function: print_menu
//void uppercase(char *string)
//{
// int c = 0;
// while (string[c] != '\0')
// {
// if (string[c] >= 'a' && string[c] <= 'z')
// {
// string[c] = string[c] - 32;
// }
// c++;
// }
//}
#include <iostream>
#include <conio.h>
int main(){
while(true){
printf("Hello World!\n");
}
return 0;
}
The above program will print "Hello World" endlessly. I want that as soon the user presses the "T" key on the keyboard, the program terminates.
Any clue on how to do this.......
If I do it like
#include <iostream>
#include <conio.h>
int main(){
char key;
while(true){
printf("Hello World!\n");
key = getch();
if(key=='T' || key=='t'){
break;
}
}
return 0;
}
Then the program will always wait for the user to press a key. I want that the program continues executing without pausing, and as soon as user presses any specific key, then the program terminates.
By the way my operating system is linux (debian) and I'm using gcc.
conio is Windows (OP tagged this as Linux). The question is frequently asked, usually answered pointing to termios, e.g.,
C++ Check Keypress Linux
Check for keypress on Linux xterm ?
On the other hand, ncurses provides functions which are useful -- but unless you use filter, the screen will be cleared. Here are useful links for functions to consider:
filter
timeout
napms
getch
By setting a short timeout (say 20 milliseconds), the program will respond faster than anyone's reaction time, and use little CPU.
Here is a revised program illustrating filter:
#include <ncurses.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
int ch = 0;
int n;
int code;
filter();
initscr();
timeout(20);
for (;;) {
move(0, 0);
for (n = 1; n < argc; ++n) {
printw("%s ", argv[n]);
}
printw("[y/n] ");
clrtoeol();
ch = getch();
if (ch == 'Y' || ch == 'y') {
code = EXIT_SUCCESS;
break;
} else if (ch == 'N' || ch == 'n') {
code = EXIT_FAILURE;
break;
}
}
endwin();
return code;
}
(Checking for y/n seems more useful than checking for "t" -- feel free to customize).
You can do a boolean checking before getting user input char getch() .
this link explains well about it To see if user press a key
If you don't want to wait for keyboard input, there is unfortunately no standard way to proceed.
If your conio.h supports kbhit() just use it. Otherwise, for any linux and posix, you can look here for Morgan Mattews's code to provide the same functionality.
Your body of your loop would then look like this:
...
if (kbhit()) {
key = getch();
if(key=='T' || key=='t'){
break;
}
}
My goal is to produce a program that will take a file as input and "encode" the text within by shifting the characters ahead 3 (so 'a' would be come 'd'). It should produce an output file with the encoded text. The menu is to take user input and execute the function that is assigned to the number selected.
I'm early on at creating this program, but running short on time and am struggling with how to structure it. Currently, I have the menu displaying, but when a sub function is called, it displays but then the menu overwrites it and I can't figure out why. Any help would be appreciated. Here is the code I have so far...
#include <stdio.h>
#define INPUT_FILE 1 //define statements
#define OUTPUT_FILE 2
#define NUM_TO_SHIFT 3
#define ENCODE 4
#define QUIT 0
int menu(); //function prototypes
int input();
int output();
int shift();
int encode();
void quit();
int main()
{
int choice; // main variables
char user_filename[100];
choice = menu(); // get user's first selection
while(choice != QUIT) //execute so long as choice is not equal to QUIT
{
switch(choice)
{
case INPUT_FILE:
printf("Enter the filename of the file to encode:\n");
printf("(hit the Enter key when done)\n");
gets(user_filename);
break;
case OUTPUT_FILE: output();
break;
case NUM_TO_SHIFT: shift();
break;
case ENCODE: encode();
break;
case QUIT: quit();
break;
default: printf("Oops! An invalid choice slipped through. ");
printf("Please try again.\n");
}
choice = menu(); /* get user's subsequent selections */
}
printf("Bye bye!\n");
return 0;
}
int menu(void)
{
int option;
printf("Text Encoder Service\n\n");
printf("1.\tEnter name of input file (currently 'Secret.txt')\n");
printf("2.\tEnter name of output file (currently not set)\n");
printf("3.\tEnter number of characters data should be shifted (currently +7)\n");
printf("4.\tEncode the text\n\n");
printf("0.\tQuit\n\n");
printf("Make your selection: ");
while( (scanf(" %d", &option) != 1) /* non-numeric input */
|| (option < 0) /* number too small */
|| (option > 4)) /* number too large */
{
fflush(stdin); /* clear bad data from buffer */
printf("That selection isn't valid. Please try again.\n\n");
printf("Your choice? ");
}
return option;
}
int input()
{
}
int output()
{
return 2;
}
int shift()
{
return 3;
}
int encode()
{
return 4;
}
void quit()
{
printf("Quiting...Bye!");
exit(0);
}
You shouldn't use gets(user_filename) to get the file name since gets() reads up to a \n and stops reading. Your scanf for the menu option does not read the \n at the end of the line when the user types in the menu option. Essentially, you're making gets read a string without words in it. The line you want to read is actually the next line. Using scanf instead of gets will fix it.
Otherwise, your program is working as expected - it's just that your functions don't do anything yet that your menu is "overwriting" the submenus. See http://ideone.com/F2pEs for an implementation with scanf instead of gets.
use getchar(); soon after the gets(user_filename); it will wait to get the character
gets(user_filename);
getchar();
break;
As in this question which Stackoverflow has highlighted as a match, you need to clear out the buffer to remove the newline that's waiting in there.
Add this code after reading a valid menu option:
do
{
c = getchar();
} while (c != EOF && c != '\n');
where c is a char declared up by option. This loops over remaining characters in the input stream until EOF (End Of File) or a newline character is reached, meaning they don't affect your call to gets(). Note that gets() is considered insecure because it doesn't protect against buffer overflow, a user could easily enter more than 100 characters (inc. newline) and start writing into memory that shouldn't be touched by their input. You would do well to lookup the secure equivalent when you see compiler warnings around function calls like this, typically they take a second parameter which is the maximum size of the buffer being read into.
Well, this answer is way late but having come across it, I can't help but write something.
Let's get straight to it. You will have an array of menus, with the array elements being the options you want in your menu. Then while in a truthy condition, loop through the elements of the array, selecting the option you want.
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
//function prototypes
int input();
int output();
int shift();
int encode();
void quit();
int main(){
int menus_on = 1;
const char *menus[5] = {"Input","Output","Shift","Encode","Quit"};
while(menus_on){
int menu,*temp;
for(int i =0;i<6;i++){
printf("%d: %s\n",i,menus[i]);
}
printf("Select menu\n");
scanf("%d",temp);
menu = *temp;
printf("Selected menu::%d\n",menu);
switch(menu){
case 0:
input();
break;
case 1:
output();
break;
case 2:
shift();
break;
case 3:
encode();
break;
case 4:
quit();
break;
default:
printf("Invalid selection\n");
break;
}
}
return 0;
}
int input() {
return 0;
}
int encode () {
return 0;
}
This question already has answers here:
How to avoid pressing Enter with getchar() for reading a single character only?
(14 answers)
Closed 6 years ago.
I'm writing a C program that prints something on terminal using ncurses. It should stop printing when user press 's' and continue again when press 's'. How can I read a key from input without waiting user to press the key?
I tried getch() and getchar() but they wait until a key is pressed...
Edit
This is my code:
int main(void)
{
initscr(); /* Start curses mode */
refresh(); /* Print it on to the real screen */
int i = 0, j = 0;
int state = 0;
while (1)
{
cbreak();
int c = getch(); /* Wait for user input */
switch (c)
{
case 'q':
endwin();
return 0;
case 'c':
state = 1;
break;
case 's':
state = 0;
break;
default:
state = 1;
break;
}
if(state)
{
move(i, j);
i++;
j++;
printf("a");
refresh();
}
}
nocbreak();
return 0;
}
EDIT 2
This works well. I got 100 points :)
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
int main(void)
{
initscr();
noecho();
cbreak(); // don't interrupt for user input
timeout(500); // wait 500ms for key press
int c = 0; // command: [c|q|s]
int s = 1; // state: 1= print, 0= don't print ;-)
int i = 0, j = 0;
while (c != 'q')
{
int c = getch();
switch (c)
{
case 'q':
endwin();
return 0;
case 'c':
s = 1;
break;
case 's':
s = 0;
break;
default:
break;
}
if (s)
{
move(i, j);
printw("a");
i++;
j++;
}
}
endwin();
nocbreak();
return 0;
}
ncurses has the capability to do this through it's own getch() function. See this page
#include <curses.h>
int main(void) {
initscr();
timeout(-1);
int c = getch();
endwin();
printf ("%d %c\n", c, c);
return 0;
}
Since you're using ncurses, you start by calling cbreak to turn off line buffering. Then you'll call nodelay to tell it not to wait before returning -- getch will always return immediately. When it does, you'll check whether a key was pressed, and if so what key that was (and react appropriately).
There's an answer to this in the comp.lang.c FAQ. See question 19.1, "How can I read a single character from the keyboard without waiting for the RETURN key? How can I stop characters from being echoed on the screen as they're typed?".
It's a bit long to post here.