I'm making a whack-a-mole program, and currently I have the setup for the mole to appear and disappear at random; however, while this is all going on I'll need to accept user input in order to "whack" the mole. Is there any way to do this without pausing the loop to wait for the user to input something, and rather have the loop run WHILE scanning for input? my code is below.
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <stdbool.h>
int main(){
// sets the mole to be initially under_ground
bool above_ground = false;
char mole_presence[1] = {0};
// keeps the mole running as long as the game is in play
while(1){
// while the mole is not above ground, wait until he randomly is
while(above_ground == false){
int r = rand() % 6;
if (r == 5){
printf("a mole has appeared, he's dancing around!\n");
mole_presence[0] = 1;
above_ground = true;
}
else{
printf("%d\n", mole_presence[0]);
sleep(1);
}
}
// while the mole is above ground, he dances until he randomly escapes
while(above_ground == true){
bool escaped = false;
// while he hasn't escaped, continue this loop
while (escaped == false){
int x = rand() % 10;
// if he randomly escapes, break out and show he is no longer above ground
if (x == 5){
printf("he disappeared!\n");
mole_presence[0] = 0;
escaped = true;
}
else{
printf("%d\n", mole_presence[0]);
sleep(1);
}
}
above_ground = false;
}
}
}
I faced the same problem while writing a snake-xenia kind of game. In Windows there is function called _kbhit which can be used to check whether the key is pressed or not. It's prototype is
int _kbhit(void)
Itreturns a nonzero value if a key has been pressed. Otherwise, it returns 0. Read more here : https://msdn.microsoft.com/en-us/library/58w7c94c.aspx
It's not available on linux as there is no conio.h in linux So for linux this answer can help Using kbhit() and getch() on Linux
Related
I have a thread that catches a key pressed through getch and if the key pressed is arrowUp or arrowDown then it scrolls my terminal using ncurses functions (incrementing an integer variable used to show elements from a linked list). This works fine most of the times but sometimes (usually when i hold an arrow pressed) ncurses prints on terminal weird and unexpected characters like 9;32H (it seems like an uncatched input). Does anyone know how i can solve this?
Here an MCVE
#include <ncurses.h>
#include <stdlib.h>
#include <pthread.h>
#define MAX(a,b) ((a) > (b) ? (a) : (b))
void * listener(void* p){
int* shift = (int*) p;
int ch;
while (1)
{
ch = getch();
if(ch == KEY_UP){
*shift = MAX(0, *shift - 1);
}
else if(ch == KEY_DOWN){
*shift = *shift + 1;
}
}
}
int main(){
char* c = malloc(100);
for(int i = 0; i < 100; i++){
c[i] = 'A' + (random() % 26);
}
int shift = 0;
pthread_t th;
initscr();
raw();
noecho();
keypad(stdscr, TRUE);
start_color();
curs_set(0);
pthread_create(&th, NULL, listener, (void*) &shift);
while(1){
for(int j = shift; j < shift+stdscr->_maxy; ++j){
move(j-shift,0);
clrtoeol();
mvaddch(j-shift, 0, c[j]);
refresh();
}
}
endwin();
free(c);
return 0;
}
This snippet shows my issue if you hold arrow down
EDIT:
The issue seems to be related to getch() from different thread that modifies global variables of ncurses library. Does anyone know a thread-safe way to get a char input?
I actually solved my issue using getchar instead of getch since it seems not to be thread safe. I found out that getch modifies ncurses global variable and calls refresh() at the end, so if another thread is changing ncurses stuff (e.g. cursor position) your program may have an unexpected behavior (in my case printing escape characters representing set cursor position)
#include <stdio.h>
void main()
{
int a=1;
char c;
x:for(a=1;a!=0;a++)
{
printf("Hello\n");
c=getch();
if(c=='n')
exit(0);
else
goto x;
}
}
//please assist me with this program by using primary operators only
This is a little different, to show you a simple solution. But if you are not allowed to use kbhit you are stuck.
#include <stdio.h>
#include <conio.h> // include the library header
int main(void) // correct signature for main
{
int c = 0; // note getch() returns `int` type
while(c != 'n') // until correct key is pressed
{
do { // forever
printf("Hello\n");
} while(!kbhit()); // until a key press detected
c = getch(); // fetch that key press
}
return 0;
}
Remember, it only tests for lower-case n.
the posted code does not compile!
The following code will do the job.
Notice that the goto is eliminated
Notice that the unneeded variables are eliminated
Notice that the appropriate header files are included
Notice the signature for the main() function is corrected
#include <stdio.h> // printf()
#include <conio.h> // getch() kbhit() <-- use correct header file
int main( void ) // <-- use valid signature
{
// <-- eliminate unneeded variables
while(1) // <-- non-confusing (and simple) loop statement
{
printf("Hello\n");
if( kbhit() )
{ // then some key has been pressed
if( 'n' == getch() )
{ // then 'n' key has been pressed
break; // <-- exit the loop
}
}
}
} // end function: main
Try this.
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
int main(void) {
char c='y';
while(c!='n') {
while(!kbhit()) {
printf("Hello\n");
}
c=getch();
}
}
Please note that I have not compiled this as conio.h is not available to me right now.
I am getting major amounts of input lag when I run my application.
More details:
When I press 'w', 'a', 's', 'd' (My assigned input keys) the object moves however it continues to move for an extended period of time after the key has been released. The source code is below however small parts of the code have been cut out to shorten the questions however if the source code below does not compile I have all of the code up on github.
https://github.com/TreeStain/DodgeLinuxGame.git Thankyou for your time. -Tristan
dodge.c:
#define ASPECT_RATIO_X 2
#define ASPECT_RATIO_Y 1
#define FRAMES_PER_SECOND 60
#include <ncurses.h>
#include "object.h"
#include "render.h"
int main()
{
initscr();
cbreak();
noecho();
nodelay(stdscr, 1);
object objs[1];
object colObj; colObj.x = 10; colObj.y = 6;
colObj.w = 2; colObj.h = 2;
colObj.sprite = '*';
colObj.ySpeed = 1;
colObj.xSpeed = 1;
objs[0] = colObj;
//halfdelay(1);
while (1)
{
char in = getch();
if (in == 'w')
objs[0].y -= objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 's')
objs[0].y += objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 'a')
objs[0].x -= objs[0].xSpeed * ASPECT_RATIO_X;
if (in == 'd')
objs[0].x += objs[0].xSpeed * ASPECT_RATIO_X;
render(objs, 1);
napms(FRAMES_PER_SECOND);
}
getch();
endwin();
return 0;
}
render.h:
void render(object obj[], int objectNum);
void render(object obj[], int objectNum) //Takes array of objects and prints them to screen
{
int x, y, i, scrWidth, scrHeight;
getmaxyx(stdscr, scrHeight, scrWidth); //Get terminal height and width
for (y = 0; y < scrHeight; y++)
{
for (x = 0; x < scrWidth; x++)
{
mvprintw(y, x, " ");
}
}
for (i = 0; i < objectNum; i++)
{
int xprint = 0, yprint = 0;
for (yprint = obj[i].y; yprint < obj[i].y + (obj[i].h * ASPECT_RATIO_Y); yprint++)
{
for (xprint = obj[i].x; xprint < obj[i].x + (obj[i].w * ASPECT_RATIO_X); xprint++)
mvprintw(yprint, xprint, "%c", obj[i].sprite);
}
}
refresh();
}
object.h:
typedef struct
{
int x, y, w, h, ySpeed, xSpeed;
char sprite;
}object;
P.S. please feel free to critique my methods and code as I am fairly new at programming and can take all the criticism I can get.
I believe the reason is because getch() will only release one input-character at a time (even if there are many queued up in the input stream) so if they queue up faster than you 'remove' them from the stream, the loop will continue until the queue is emptied even after you release the key. Also, you'll want to go (1000 / FRAMES_PER_SECOND) to get your desired delay-time in milliseconds (this creates 60 frames per second).
Try this in your while loop instead.
while (1)
{
char in;
/* We are ready for a new frame. Keep calling getch() until we hear a keypress */
while( (in = getch()) == ERR) {}
if (in == 'w')
objs[0].y -= objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 's')
objs[0].y += objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 'a')
objs[0].x -= objs[0].xSpeed * ASPECT_RATIO_X;
if (in == 'd')
objs[0].x += objs[0].xSpeed * ASPECT_RATIO_X;
render(objs, 1);
/* Clear out any other characters that have been buffered */
while(getch() != ERR) {}
napms(1000 / FRAMES_PER_SECOND);
}
From the top of your loop: while( (in = getch()) == ERR) {} will call getch() rapidly until a keypress is detected. If a keypress isn't detected, getch() will return ERR.
What while(getch() != ERR) {} does is keep calling getch() until all buffered input characters are removed from the queue, then getch() returns ERR and moves on. Then the loop should sleep ~17ms and repeat. These lines should force the loop to only 'count' one keypress every ~17ms, and no more often than that.
See: http://linux.die.net/man/3/getch
Ncurses does not detect key presses and key releases separately. You cannot move an object while a key is being held, and stop immediately after it is released.
The phenomenon you observe results from a ximbination of two factors: an auto-repeating keyboard, and a buffering keyboard driver. That is, the user holds a key, this generates a large amount of key events, and they are buffered by the driver and given to your application as it asks for key presses.
Neither the driver nor keyboard auto-repeat feature are under control of your application. The only thing you can hope to achieve is to process key events faster than they come out of the keyboard. If you want to do this, you have to get rid of napms in your main loop and process key presses as they come, between frame repaints. There are many ways to do that but the most straightforward is to use the timeout function.
timeout (timeToRefresh);
ch = getch();
if (ch == ERR) refresh();
else processKey(ch);
You need to calculate timeToRefresh each time using a real time clock.
I have a C program, where I just wanted to test if I could reproduce a console spinner used in npm install while it installs a module. This particular spinner simply spins in this order:
|
/
-
\
on the same space, so I use the following program:
#include <stdio.h>
int main() {
char sequence[4] = "|/-\\";
while(1) {
for(int i = 0; i < 4; i++) {
// \b is to make the character print to the same space
printf("\b%c", sequence[i]);
// now I want to delay here ~0.25s
}
}
}
So I found a way to make it rest for that long from <time.h> documentation and made this program:
#include <stdio.h>
#include <time.h>
void sleep(double seconds) {
clock_t then;
then = clock();
while(((double)(clock() - then) / CLOCKS_PER_SEC) < seconds); //do nothing
}
int main() {
char sequence[4] = "|/-\\";
while(1) {
for(int i = 0; i < 4; i++) {
printf("\b%c", sequence[i]);
sleep(0.25);
}
}
}
But now nothing prints to the console. Does anyone know how I can go about producing the behavior I want?
EDIT According to what appears to be popular opinion, I've updated my code above to be the following:
#include <stdio.h>
#include <unistd.h>
int main() {
char sequence[4] = "|/-\\";
while(1) {
for(int i = 0; i < 4; i++) {
printf("\b%c", sequence[i]);
/* fflush(stdout); */
// commented out to show same behavior as program above
usleep(250000); // 250000 microseconds = 0.25 seconds
}
}
}
You will need to flush after you wrote to the console. Otherwise, the program will buffer your output:
fflush(stdout);
Things do get printed to console, it's just does not get flushed. Add fflush(stdout) to see the results, or set the console in an unbuffered mode by calling setbuf:
setbuf(stdout, NULL);
A bigger problem with your code is that your sleep method runs a busy loop, which burns CPU cycles for no good reason. A better alternative would be to call usleep, which takes the number of microseconds:
usleep(25000);
The sleep function isn't really your problem. The issue is that the output is buffered. The simplest thing to do will be to research ncurses.
For now:
fflush(stdout);
I'm trying to exit a loop at anytime I want by pressing any key. I've tried the code below but it can't be done. Gotta need your help. Thank you in advance. I'm using a C-Free 5.0.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int b=0, i;
int seconds;
printf("\nEnter number of seconds : ");
scanf("%d", &seconds);
while (b==0)
{
for(i=1;i<=seconds;i++)
{
time_t end = time(0) + 1;
while(time(0) < end)
;
seconds -= 1;
printf("Number of seconds left : %d\n", seconds);
b=kbhit();
}
if(seconds == 0)
{
exit(0);
}
}
printf("Number of remaining seconds left : %d\n", seconds);
}
You are "busy-waiting" in the innermost while loop. That might not be the best solution, but if that is what you want to do, you need to add a test in that loop to check if a key has been hit.
To exit a loop use a function in c++ called khbit. It becomes 1 when any key is pressed and to empty it again assign the key pressed to clear the buffer using getch()
#include <conio.h>
#include <iostream>
using namespace std;
int main()
{
while(1)
{
if(kbhit()) // khbit will become 1 on key entry.
{
break; // will break the loop
}
// Try to use some delay like sleep(100); // sleeps for 10th of second to avoid stress on CPU
}
// If you want to use khbit again then you must clear it by char dump = getch();
// This way you can also take a decision that which key was pressed like
// if(dump == 'A')
//{ cout<<"A was pressed e.t.c";}
}