I am trying to get a basic simultaneous movement of both a player and an enemy working. Is there any way I can accomplish this correctly?
Below is a crude way of accomplishing. I would like to move while the x is also moving but I can only get one of them at a time to work. I tried using while loops but perhaps something else is needed...
Any tips?
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <windows.h>
WINDOW* createwindow();
char theplayer();
char theenemy();
int main()
{
initscr();
WINDOW* border=createwindow();
while(1)
{
theplayer();
theenemy();
}
wgetch(border);
endwin();
return 0;
}
WINDOW* createwindow()
{
WINDOW* temp=newwin(15,40,10,10);
box(temp,0,0);
return temp;
}
char theplayer(WINDOW* border)
{
int playerlocationy=3;
int playerlocationx=3;
int input;
char player='#';
keypad(border,true);
mvwprintw(border,3,3,"%c",player);
while(1)
{
input=wgetch(border);
switch (input)
{
case KEY_LEFT:
mvwprintw(border,playerlocationy,playerlocationx,"%c",' ');
playerlocationx--;
mvwprintw(border,playerlocationy,playerlocationx,"%c", player);
break;
case KEY_RIGHT:
mvwprintw(border,playerlocationy,playerlocationx,"%c",' ');
playerlocationx++;
mvwprintw(border,playerlocationy,playerlocationx,"%c", player);
break;
case KEY_UP:
mvwprintw(border,playerlocationy,playerlocationx,"%c",' ');
playerlocationy--;
mvwprintw(border,playerlocationy,playerlocationx,"%c", player);
break;
case KEY_DOWN:
mvwprintw(border,playerlocationy,playerlocationx,"%c",' ');
playerlocationy++;
mvwprintw(border,playerlocationy,playerlocationx,"%c", player);
break;
default:
break;
}
break;
}
return player;
}
char theenemy(WINDOW* border)
{
char enemy='X';
int enemylocationy=9;
int enemylocationx=9;
while(1)
{
mvwprintw(border,enemylocationy,enemylocationx,"%c", enemy);
mvwprintw(border,enemylocationy,enemylocationx,"%c",' ');
enemylocationx++;
mvwprintw(border,enemylocationy,enemylocationx,"%c", enemy);
wrefresh(border);
Sleep(1000);
}
return 0;
}
To start, these function declarations
char theplayer();
char theenemy();
are an obsolescent feature of C, that say these functions will take an unspecified but fixed number of arguments. With this, the compiler cannot reason about what an invocation of these functions should look like.
This is hiding the fact that your program has undefined behaviour. Both function definitions require a WINDOW * argument, but you call them with no arguments.
while(1)
{
theplayer();
theenemy();
}
To any extent that this program functions as is, is pure chance. Always use proper function prototypes in your declarations
WINDOW *createwindow(void);
char theplayer(WINDOW *);
char theenemy(WINDOW *);
which will help to find errors.
Using <windows.h> for Sleep reduces the portability of your program. You are already using curses, which provides the very similar napms function.
As for "simultaneous movement", the general idea is to have only one main event loop. Every iteration, this loop
handles input
updates entities
redraws the screen
Nothing else should block execution of the program (i.e., loop forever).
To update your enemy, you will need some way of tracking how much time has passed. This can be as advanced as delta timing or as simple as a frame counter, as shown below.
Here is a cursory example to get you started:
#include <curses.h>
struct entity {
int y;
int x;
unsigned char repr;
};
void update_player(struct entity *, int);
void update_enemy(struct entity *, unsigned);
void draw_entity(struct entity *, WINDOW *);
int main(void)
{
initscr();
noecho();
curs_set(0);
WINDOW *field = newwin(15, 40, 10, 10);
keypad(field, TRUE);
wtimeout(field, 0);
struct entity player = { 3, 3, '#' };
struct entity enemy = { 9, 9, 'X' };
unsigned tick = 1;
while (1) {
/* handle input */
int c = wgetch(field);
if ((c & A_CHARTEXT) == 'q')
break;
/* update stuff */
update_player(&player, c);
update_enemy(&enemy, tick);
/* draw things */
werase(field);
box(field, 0, 0);
draw_entity(&player, field);
draw_entity(&enemy, field);
wrefresh(field);
tick = (tick > 60) ? 0 : tick + 1;
napms(16);
}
delwin(field);
endwin();
}
void update_player(struct entity *p, int ch)
{
switch (ch) {
case KEY_LEFT:
p->x--;
break;
case KEY_RIGHT:
p->x++;
break;
case KEY_UP:
p->y--;
break;
case KEY_DOWN:
p->y++;
break;
}
}
void update_enemy(struct entity *e, unsigned t)
{
if (t == 60)
e->x++;
}
void draw_entity(struct entity *et, WINDOW *f)
{
mvwaddch(f, et->y, et->x, et->repr);
}
Related
I am trying to write a simple parking arrangement code, I want to sort the capacity by 1000 vehicles, color, license plate and model
#include <stdio.h>
#include <stdlib.h>
void NewCar()
{
char model[1000][20];
char color [1000][20];
char number[1000][20];
int x = 1;
printf("\nModel: ");
scanf("%s",model[x]);
printf("Color: ");
scanf("%s",color[x]);
printf("Number: ");
scanf("%s",number[x]);
}
void CarList()
{
int x;
char model[1000][20];
char color [1000][20];
char number[1000][20];
for (x ; x >= 1 ; x--)
{
printf("\n%d. Car: %s %s %s",x,number[x],model[x],color[x]);
}
}
int main()
{
char model[1000][20];
char color [1000][20];
char number[1000][20];
char menu;
int x = 1;
flag:
printf("New Car(N)\nCar List(L)\n");
scanf("%s",&menu);
if (menu == "n" || menu == "N")
{
NewCar();
goto flag;
}
if (menu == "l" || menu == "L")
{
CarList();
goto flag;
}
}
when i don't use void, the code works but i have to use void
Example of the output I want;
1. Car Red Jeep FGX9425
2. Car Yellow Truck OKT2637
3. Car Green Sedan ADG4567
....
This is prefaced by my top comments.
Never use goto. Use (e.g.) a while loop.
Your scanf for menu would [probably] overflow.
As others have mentioned, a number of bugs.
I've refactored your code with your old code and some new code. This still needs more error checking and can be generalized a bit more, but, I've tested it for basic functionality:
#include <stdio.h>
#include <stdlib.h>
// description of a car
struct car {
char model[20];
char color[20];
char number[20];
};
int
NewCar(struct car *cars,int carcount)
{
struct car *car = &cars[carcount];
printf("\nModel: ");
scanf("%s", car->model);
printf("\nColor: ");
scanf("%s", car->color);
printf("\nNumber: ");
scanf("%s", car->number);
++carcount;
return carcount;
}
void
CarList(struct car *cars,int carcount)
{
struct car *car;
int caridx;
for (caridx = 0; caridx < carcount; ++caridx) {
car = &cars[caridx];
printf("%d. Car: %s %s %s\n",
caridx + 1, car->number, car->model, car->color);
}
}
int
main(int argc,char **argv)
{
#if 1
int carcount = 0;
struct car carlist[1000];
#endif
#if 0
char menu;
int x = 1;
#else
char menu[20];
#endif
// force out prompts
setbuf(stdout,NULL);
while (1) {
printf("New Car(N)\nCar List(L)\n");
#if 0
scanf("%s", &menu);
#else
scanf(" %s", menu);
#endif
// stop program
if ((menu[0] == 'q') || (menu[0] == 'Q'))
break;
switch (menu[0]) {
case 'n':
case 'N':
carcount = NewCar(carlist,carcount);
break;
case 'l':
case 'L':
CarList(carlist,carcount);
break;
}
}
return 0;
}
UPDATE:
As you said, there are a few minor errors, it's not a problem for me, but I can write errors if you want to know and fix them.(if you write the plate with a space between it, the code repeats the "new car car list" command many times)
Okay, I've produced an enhanced version that replaces the scanf with a function askfor that uses fgets. The latter will prevent [accidental] buffer overflow. And, mixing scanf and fgets can be problematic. Personally, I always "roll my own" using fgets as it can provide finer grain control [if used with wrapper functions, such as the askfor provided here]
Edit: Per chux, I've replaced the strlen for removing newline with a safer version that uses strchr:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRMAX 20
// description of a car
struct car {
char model[STRMAX];
char color[STRMAX];
char number[STRMAX];
};
// askfor -- ask user for something
void
askfor(const char *tag,char *ptr)
{
printf("Enter %s: ",tag);
fflush(stdout);
fgets(ptr,STRMAX,stdin);
// point to last char in buffer
// remove newline
#if 0
ptr += strlen(ptr);
--ptr;
if (*ptr == '\n')
*ptr = 0;
#else
// remove trailing newline [if it exists]
ptr = strchr(ptr,'\n');
if (ptr != NULL)
*ptr = 0;
#endif
}
int
NewCar(struct car *cars,int carcount)
{
struct car *car = &cars[carcount];
askfor("Model",car->model);
askfor("Color",car->color);
askfor("Number",car->number);
++carcount;
return carcount;
}
void
CarList(struct car *cars,int carcount)
{
struct car *car;
int caridx;
for (caridx = 0; caridx < carcount; ++caridx) {
car = &cars[caridx];
printf("%d. Car: %s %s %s\n",
caridx + 1, car->number, car->model, car->color);
}
}
int
main(int argc,char **argv)
{
int carcount = 0;
struct car carlist[1000];
char menu[STRMAX];
// force out prompts
setbuf(stdout,NULL);
while (1) {
askfor("\nNew Car(N)\nCar List(L)",menu);
// stop program
if ((menu[0] == 'q') || (menu[0] == 'Q'))
break;
switch (menu[0]) {
case 'n':
case 'N':
carcount = NewCar(carlist,carcount);
break;
case 'l':
case 'L':
CarList(carlist,carcount);
break;
}
}
return 0;
}
UPDATE #2:
Thank you for your bug fix, but as I said in my question, I have to do the "New car" feature using void. You did it with int, can you do it with void?
Okay. When you said "using void", what you meant wasn't totally clear to me [or some others]. There were enough bugs that they overshadowed some other considerations.
So, I have to assume that "using void" means that the functions return void.
Your original functions were defined as void NewCar() and void CarList(). Those could not have done the job as is, so they had to be changed.
If you have similar criteria, a better way to phrase that would be:
I must create two functions, with the following function signatures ...
Anyway, here's the updated code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRMAX 20
// description of a car
struct car {
char model[STRMAX];
char color[STRMAX];
char number[STRMAX];
};
// askfor -- ask user for something
void
askfor(const char *tag,char *ptr)
{
printf("Enter %s: ",tag);
fflush(stdout);
fgets(ptr,STRMAX,stdin);
// remove trailing newline [if it exists]
ptr = strchr(ptr,'\n');
if (ptr != NULL)
*ptr = 0;
}
void
NewCar(struct car *cars,int *countptr)
{
int carcount = *countptr;
struct car *car = &cars[carcount];
askfor("Model",car->model);
askfor("Color",car->color);
askfor("Number",car->number);
carcount += 1;
*countptr = carcount;
}
void
CarList(struct car *cars,int carcount)
{
struct car *car;
int caridx;
for (caridx = 0; caridx < carcount; ++caridx) {
car = &cars[caridx];
printf("%d. Car: %s %s %s\n",
caridx + 1, car->number, car->model, car->color);
}
}
int
main(int argc,char **argv)
{
int carcount = 0;
struct car carlist[1000];
char menu[STRMAX];
// force out prompts
setbuf(stdout,NULL);
while (1) {
askfor("\nNew Car(N)\nCar List(L)",menu);
// stop program
if ((menu[0] == 'q') || (menu[0] == 'Q'))
break;
switch (menu[0]) {
case 'n':
case 'N':
#if 0
carcount = NewCar(carlist,carcount);
#else
NewCar(carlist,&carcount);
#endif
break;
case 'l':
case 'L':
CarList(carlist,carcount);
break;
}
}
return 0;
}
However, given your original functions, it may be possible that the signatures have to be: void NewCar(void) and void CarList(void) and that the car list variables must be global scope.
This would be a less flexible and desirable way to do things, but here is a version that uses only global variables for the lists:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRMAX 20
// description of a car
struct car {
char model[STRMAX];
char color[STRMAX];
char number[STRMAX];
};
#if 1
int carcount = 0;
struct car carlist[1000];
#endif
// askfor -- ask user for something
void
askfor(const char *tag,char *ptr)
{
printf("Enter %s: ",tag);
fflush(stdout);
fgets(ptr,STRMAX,stdin);
// remove trailing newline [if it exists]
ptr = strchr(ptr,'\n');
if (ptr != NULL)
*ptr = 0;
}
void
NewCar(void)
{
struct car *car = &carlist[carcount];
askfor("Model",car->model);
askfor("Color",car->color);
askfor("Number",car->number);
carcount += 1;
}
void
CarList(void)
{
struct car *car;
int caridx;
for (caridx = 0; caridx < carcount; ++caridx) {
car = &carlist[caridx];
printf("%d. Car: %s %s %s\n",
caridx + 1, car->number, car->model, car->color);
}
}
int
main(int argc,char **argv)
{
#if 0
int carcount = 0;
struct car carlist[1000];
#endif
char menu[STRMAX];
// force out prompts
setbuf(stdout,NULL);
while (1) {
askfor("\nNew Car(N)\nCar List(L)",menu);
// stop program
if ((menu[0] == 'q') || (menu[0] == 'Q'))
break;
switch (menu[0]) {
case 'n':
case 'N':
#if 0
carcount = NewCar(carlist,carcount);
#else
NewCar();
#endif
break;
case 'l':
case 'L':
#if 0
CarList(carlist,carcount);
#else
CarList();
#endif
break;
}
}
return 0;
}
I am making a "Clicker-Game". It's my first real game that I'm doing alongside school. I got all of the game code complete but I want to have a loop in the background that adds geldps(money per second) after every second.
I tried threads but I don't really understand it and we won't learn that until next year, so I´m asking, if anyone can either tell me a better way to make a loop in the background that runs independent from the main program, and can just add geldps to geld every second. Thanks.
PS: I am sorry for the German variables. Ask me if you don't know what sth means or anything, and it´s probably not very well organised and everything.
#include <stdio.h>
int geldps=0,geld=0;
int main()
{
int stand=0, oil=0, Mine=0, Bank=0,standzahl=100, Minezahl=500, Bankzahl=1000, oilzahl=10000, Werkzeug=0, Werkzeugzahl=10;
char input, input2;
float faktor;
do
{
system("cls");
faktor=1+Werkzeug/10;
printf("%c%c%c%c%c%c%c%c%c%c%c\n",201,205,205,205,205,205,205,205,205,205,187);
printf(" %d$\n",geld);
printf("%c%c%c%c%c%c%c%c%c%c%c\n",200,205,205,205,205,205,205,205,205,205,188);
printf(" Space to get money\n U to go to Upgrades\n Escape to leave");
input=getch();
if(input==32)
{
geld=geld+faktor;
continue;
}
if(input == 117 || input == 85)
{
system("cls");
do
{
system("cls");
printf(" 0 - Tools(10 for 1 more Money)(%d)(%d$)\n 1 - Lemon Stands(%d)(%d$)\n 2 - Mines(%d)(%d$)\n 3 - Banks(%d)(%d$)\n 4 - Oil Refinerys(%d)(%d$)\nBackspace to go back", Werkzeug, Werkzeugzahl, stand, standzahl, Mine, Minezahl, Bank, Bankzahl, oil, oilzahl);
input2=getch();
if(input2== 48)
{
if(geld<Werkzeugzahl)
{
system("cls");
printf("Not enough money(%d/%d$)\n",Werkzeugzahl,geld);
system("pause");
continue;
}
geld=geld-Werkzeugzahl;
Werkzeug++;
Werkzeugzahl=Werkzeugzahl+Werkzeugzahl/10;
}
if(input2== 49)
{
if(geld<standzahl)
{
system("cls");
printf("Not enough money(%d/%d$)\n",standzahl,geld);
system("pause");
continue;
}
geld=geld-standzahl;
stand++;
standzahl=standzahl+standzahl/10;
}
if(input2== 50)
{
if(geld<Minezahl)
{
system("cls");
printf("Not enough money(%d/%d$)\n",Minezahl,geld);
system("pause");
continue;
}
geld=geld-Minezahl;
Mine++;
Minezahl=Minezahl+Minezahl/10;
geldps=geldps+1;
}
if(input2== 51)
{
if(geld<Bankzahl)
{
system("cls");
printf("Not enough money(%d/%d$)\n",Bankzahl,geld);
system("pause");
continue;
}
geld=geld-Bankzahl;
Bank++;
Bankzahl=Bankzahl+Bankzahl/10;
geldps=geldps+10;
}
if(input2== 52)
{
if(geld<oilzahl)
{
system("cls");
printf("Not enough money(%d/%d$)\n",oilzahl,geld);
system("pause");
continue;
}
geld=geld-oilzahl;
oil++;
oilzahl=oilzahl+oilzahl/10;
geldps=geldps+100;
}
}
while(input2!=8);
}
}
while(input!=27);
return 0;
}
update: I was procrastinating and cleaned and improved your code. At the end of this answer.
If all you need is for a number to be consistently incremented based on time, add a function that updates a value based on time.
Here's an example showing not only how to do that but also how to compartmentalize your code into functions and how to use better code formatting and variable names.
#include <stdio.h> // printf()
#include <time.h> // time()
#include <stdlib.h> // random()
#include <unistd.h> // sleep()
int updateValue(int lastValue, int amountPerSecond) {
static time_t lastTime = -1;
time_t currentTime = time(NULL);
int newValue = lastValue;
if (lastTime != -1) {
newValue += amountPerSecond * (currentTime - lastTime);
}
lastTime = currentTime;
return newValue;
}
void seedRandom() {
// Don't use this in production code.
srandom(time(NULL));
}
int sleepRandomly() {
const int SLEEP_RANGE_IN_SECS = 5;
// sleep( 0..5 seconds )
int timeToSleep = random() % (SLEEP_RANGE_IN_SECS + 1);
sleep(timeToSleep);
return timeToSleep;
}
int main() {
const int AMOUNT_PER_SECOND = 5;
int value = 0;
// How many times to run the loop
int numCycles = 5;
seedRandom();
// Initialize the updateValue() start time
value = updateValue(value, AMOUNT_PER_SECOND);
while (numCycles--) {
int amountSlept = sleepRandomly();
int newValue = updateValue(value, AMOUNT_PER_SECOND);
printf("Slept %d seconds.", amountSlept);
printf("Value updated: %10d + (%d secs * %4d amount/secs) = %10d\n",
value, amountSlept, AMOUNT_PER_SECOND, newValue);
value = newValue;
}
return 0;
}
Cleaned up version of your code, and then I just kept improving it.
#include <stdio.h> // printf()
#include <string.h> // strlen()
#include <stdarg.h> // vsnprintf()
#include <unistd.h> // STDIN_FILENO
#include <sys/time.h> // gettimeofday()
#include <time.h>
#include <termios.h>
#include <errno.h>
#include <signal.h>
// TODO: convert the menus to tables:
// key - desc count cost effect
// TODO: add all income sources in a list so it's easy to add more
// without changing code
// TODO: the current pricing is off
// TODO: convert to C++ so we can use classes
// TODO: only use one menu, it's nicer to use
// one global value so we can ensure that we restore
// stdin's terminal settings
struct termios g_oldStdinTermios;
int g_keepGoing = 1;
typedef struct {
int count;
int zahl;
int zahlIncrement;
int geldPerSecondIncrement;
} IncomeSource;
typedef struct {
char lastMessage[100];
// try to avoid global variables, pass them instead
// one variable per line
int geld;
int geldPerSecond;
int geldPerClick;
IncomeSource werkzeug;
IncomeSource stand;
// Use consistent capitalization: sound be "mine"
IncomeSource mine;
IncomeSource bank;
IncomeSource oil;
} Values;
void setLastMessage(Values *values, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
(void) vsnprintf(values->lastMessage, sizeof(values->lastMessage), fmt, ap);
va_end(ap);
}
void clearLastMessage(Values *values) {
// clear lastMessage after valid choice
values->lastMessage[0] = '\0';
}
void initializeValues(Values *values) {
clearLastMessage(values);
// use spaces around assignment and operators
values->geldPerSecond = 0;
values->geld = 10000;
// count, cost, cost increment (1/n), geldPerSecond increment
values->werkzeug = (IncomeSource){0, 10, 10, 0};
// BUG: number of stands doesn't increase geld per second
// or geld per click
values->stand = (IncomeSource){0, 100, 10, 0};
values->mine = (IncomeSource){0, 500, 10, 1};
values->bank = (IncomeSource){0, 1000, 10, 10};
values->oil = (IncomeSource){0, 10000, 10, 100};
values->geldPerClick = 1 + values->werkzeug.count / 10;
}
void clearScreen() {
// use ANSI escape sequences
const char *ANSI_MOVE_TO_1_1 = "\x1B[1;1H";
const char *ANSI_CLEAR_SCREEN = "\x1B[2J";
printf("%s%s", ANSI_CLEAR_SCREEN, ANSI_MOVE_TO_1_1);
}
char upcase(char c) {
if (c < 'a' || c > 'z') {
return c;
}
return 'A' + (c - 'a');
}
void setNonBlockingBufferingStdinTermios() {
struct termios new_;
tcgetattr(STDIN_FILENO, &g_oldStdinTermios);
new_ = g_oldStdinTermios;
new_.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &new_);
}
void restoreStdinTermios() {
tcsetattr(STDIN_FILENO, TCSANOW, &g_oldStdinTermios);
}
long getElapsedTimeInMs(struct timeval *start) {
struct timeval now;
gettimeofday(&now, NULL);
// in microseconds
long elapsed = ((now.tv_sec - start->tv_sec) * 1000000
+ now.tv_usec - start->tv_usec);
return elapsed / 1000;
}
char getCharacter() {
struct timeval start;
gettimeofday(&start, NULL);
char input = -1;
while (read(STDIN_FILENO, &input, 1) == -1
&& errno == EAGAIN
&& getElapsedTimeInMs(&start) < 500) {
}
return upcase(input);
}
void updateGeld(Values *values) {
static time_t lastTime = -1;
time_t currentTime = time(NULL);
if (lastTime != -1) {
values->geld += values->geldPerSecond * (currentTime - lastTime);
}
lastTime = currentTime;
}
void printHeader(Values *values) {
const char *UPPER_LEFT = "\u2554";
const char *UPPER_RIGHT = "\u2557";
const char *LOWER_LEFT = "\u255a";
const char *LOWER_RIGHT = "\u255d";
const char *HORIZONTAL = "\u2550";
const char *VERTICAL = "\u2551";
updateGeld(values);
// Automatically expand the box as the size
// of geld grows.
const int BORDER_WIDTH = 3;
char geldStr[20];
snprintf(geldStr, sizeof(geldStr), "$ %d", values->geld);
// Move code used more than once into its own function
clearScreen();
printf("%s", UPPER_LEFT);
for (int i = 0; i < (2 * BORDER_WIDTH + strlen(geldStr)); i++) {
printf("%s", HORIZONTAL);
}
printf("%s\n", UPPER_RIGHT);
// use spaces around commas
printf("%s %s %s %s\n",
VERTICAL, geldStr, VERTICAL, values->lastMessage);
printf("%s", LOWER_LEFT);
for (int i = 0; i < (2 * BORDER_WIDTH + strlen(geldStr)); i++) {
printf("%s", HORIZONTAL);
}
printf("%s\n", LOWER_RIGHT);
}
void upgrade(Values *values, IncomeSource *source) {
if (values->geld < source->zahl) {
setLastMessage(values, "Not enough money(%d/%d$)",
source->zahl, values->geld);
return;
}
clearLastMessage(values);
values->geld -= source->zahl;
source->count++;
source->zahl += source->zahl / source->zahlIncrement;
values->geldPerSecond += source->geldPerSecondIncrement;
}
char getUpgradeInput(Values *values) {
clearScreen();
printHeader(values);
printf(" 0 - Tools(10 for 1 more Money)(%d)(%d$)\t\t+%d/click\n",
values->werkzeug.count, values->werkzeug.zahl, values->geldPerClick);
printf(" 1 - Lemon Stands(%d)(%d$)\t\t\t+%d/sec\n",
values->stand.count, values->stand.zahl,
values->stand.count * values->stand.geldPerSecondIncrement);
printf(" 2 - Mines(%d)(%d$)\t\t\t\t+%d/sec\n",
values->mine.count, values->mine.zahl,
values->mine.count * values->mine.geldPerSecondIncrement);
printf(" 3 - Banks(%d)(%d$)\t\t\t\t+%d/sec\n",
values->bank.count, values->bank.zahl,
values->bank.count * values->bank.geldPerSecondIncrement);
printf(" 4 - Oil Refinerys(%d)(%d$)\t\t\t+%d/sec\n",
values->oil.count, values->oil.zahl,
values->oil.count * values->oil.geldPerSecondIncrement);
printf(" Q - Back to main menu\n");
printf("> ");
fflush(stdout);
return getCharacter();
}
void upgradeLoop(Values *values) {
char input = ' ';
while (input != 'Q' && g_keepGoing) {
input = getUpgradeInput(values);
switch (input) {
case '0':
upgrade(values, &values->werkzeug);
values->geldPerClick = 1 + values->werkzeug.count / 10;
break;
case '1':
upgrade(values, &values->stand);
break;
case '2':
upgrade(values, &values->mine);
break;
case '3':
upgrade(values, &values->bank);
break;
case '4':
upgrade(values, &values->oil);
break;
case 'Q':
break;
default:
break;
}
}
}
char getMainInput(Values *values) {
printHeader(values);
// make this easier to read in the code...
printf(" _ - [Space] get money\n");
printf(" U - Upgrades\n");
printf(" Q - Quit\n");
printf("> ");
fflush(stdout);
return getCharacter();
}
void mainLoop(Values *values) {
char input = ' ';
// while..do is easier to read and understand than do..while
while (input != 'Q' && g_keepGoing) {
// Encapsulate code in functions to make your program's logic
// easier to follow
input = getMainInput(values);
// Use a switch statement here and use character values
// rather than integers
switch (input) {
case ' ':
values->geld += values->geldPerClick;
clearLastMessage(values);
break;
case 'U':
upgradeLoop(values);
clearLastMessage(values);
case 'Q':
break;
default:
break;
}
};
}
void sigintHandler(int signal) {
printf("SIGINT received, cleaning up.\n");
restoreStdinTermios();
g_keepGoing = 0;
}
int main() {
Values values;
initializeValues(&values);
setNonBlockingBufferingStdinTermios();
signal(SIGINT, sigintHandler);
mainLoop(&values);
restoreStdinTermios();
return 0;
}
Im trying to do a basic menu in C. I'm supposed to do this with ncurses lib. I was working with this tutorial:
Video On YouTube
But mine version has some problems:
1)The menu will not print properly, it will reveal only while choosing menu items. Then the highlight won't go off
2)Option made on menu won't print on the top
Can you help me? Is that idea of Menu good or should i look for other tutorial (any help ?).
#include <stdio.h>
#include <ncurses.h>
#include <string.h>
#include <menu.h>
int main(int argc, char **argv)
{
int i, c;
char powitanie[]="SLOWNIK UNIWERSALNY";
int szer, dlug; //wartosci dlugosci i szerokosci terminalu
initscr(); //Inizjalizacja całości ncurses, kolory itp
raw();
noecho();
keypad(stdscr, TRUE);
start_color();
//init_pair(1, COLOR_BLUE, COLOR_BLACK); //wybór kolorów
getmaxyx(stdscr, szer, dlug); //pobranie rozmiarów terminalu
move(szer/2, (dlug-strlen(powitanie))/2); //przesuwamy kursor na środek (tak aby się ładnie wydrukowało)
//attron(COLOR_PAIR(1)); //Aktywujemy wybrane kolory
printw(powitanie); //Drukujemy powitanie
//attroff(COLOR_PAIR(1));//Dezaktywujemy kolory
refresh();//Odswiezamy (inaczej się nie wyswietli)
WINDOW * menuwin=newwin(6, dlug-12, szer-8, 6); //Definiujemy i tworzymy 'okno'
box(menuwin, 0, 0);
refresh();//ponownie odświeżamy aby okno się pojawiło
wrefresh(menuwin);//odświeżamy samo okno
keypad(menuwin, TRUE);//umozliwiamy dzialanie klawiatury w oknie
char *opcje[] = {
"Tlumacz z Polskiego na Angielski",
"Tlumacz z Angielskiego na Polski",
"Edystuj slownik",
"Wybierz slownik",
"Wyjdz",
};
int wybor;
int zaznacz=0;
while(1)//cala ta petla sluzy ciaglemu tworzeniu menu z podswietleniem wybranego elementu
{
for(i=0; i<5; i++)
{
if(i==zaznacz)
{
wattron(menuwin, A_REVERSE);
mvwprintw(menuwin, i+1, 1, opcje[i]);
wattroff(menuwin, A_REVERSE);
}
wybor = wgetch(menuwin);
switch(wybor)
{
case KEY_UP:
zaznacz--;
if(zaznacz==-1) zaznacz=0;//zabezpieczenie przed wyjsciem "poza" menu
break;
case KEY_DOWN:
zaznacz++;
if(zaznacz==5) zaznacz=4;
break;
default:
break;
}
if(wybor==10) break;
}
printw("Wybrano:%s", opcje[zaznacz]);
}
return(0);
}
PS: Code comments are not in English but i hope the won't be necessary
There are quite a few problems here. I have included a modified version of your code that works, and I will attempt to describe the changes.
There were some unused variables, namely argc, argv, and c, so I cast these to void in order to silence compiler warnings. You can remove the c and change to int main(void), if you like, removing these variables altogether.
I have added the stdlib.h header file to your #includes for the exit() function. This is used in the new error function, fail(), that I added to your code. You should always check the return values of any function that you call when programming in C. Here it is particularly important to check, first if the terminal supports color with the has_colors() function, and then if the call to start_color() is successful. If either of these fail, the fail() function is called with an error message, and the program exits with the EXIT_FAILURE value. The function has_colors() returns a bool, and the start_color() function returns an int (OK if successful, otherwise ERR).
Now that colors have been initialized, I see that the lower border of your menu selection window is being overwritten by the menu text. To fix this, I changed the size of your window, making it one line taller:
WINDOW * menuwin=newwin(7, dlug-12, szer-9, 6);
The fundamental problem of improper printing that you reported was because of a misplaced brace in the for loop controlling the printing of the menu items. I took the opportunity to reorganize the loop a bit; now there is only one call to mvwprintw(). The A_REVERSE attribute is set before printing if the current item is also the selected item, and it is again unset after printing.
I also changed the limit tests in the switch statement from equalities to inequalites. It is better practice to use , e.g., if (zaznacz < 0) instead of if (zaznacz == -1) in such cases.
I added a newline character to the beginning of the format string in the final printw(), since some of the selections are too long to fit in the window at the end of the title. You can move this output wherever you like.
Finally, I added a refresh() after the final printw() statement, and a getch() to wait for the user to hit ENTER before exiting the program. It is very important to cleanup by calling endwin() before exiting an NCurses program. This function reverses changes made to your terminal by NCurses while your program was running, and failure to do this can lead to terminal unpleasantries.
#include <stdio.h>
#include <ncurses.h>
#include <string.h>
#include <menu.h>
#include <stdlib.h> // added for exit() function
void fail(char *msg) {
endwin();
puts(msg);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
/* Commandline argument currently unused */
(void) argc;
(void) argv;
int i, c;
(void) c; // c is currently unused
char powitanie[]="SLOWNIK UNIWERSALNY";
int szer, dlug; //wartosci dlugosci i szerokosci terminalu
initscr(); //Inizjalizacja całości ncurses, kolory itp
raw();
noecho();
keypad(stdscr, TRUE);
/* Test to see if terminal has colors */
if (has_colors() == false) {
fail("Colors unavailable\n");
}
if (start_color() != OK) {
fail("Unable to start colors\n");
}
//init_pair(1, COLOR_BLUE, COLOR_BLACK); //wybór kolorów
getmaxyx(stdscr, szer, dlug); //pobranie rozmiarów terminalu
move(szer/2, (dlug-strlen(powitanie))/2); //przesuwamy kursor na środek (tak aby się ładnie wydrukowało)
//attron(COLOR_PAIR(1)); //Aktywujemy wybrane kolory
printw(powitanie); //Drukujemy powitanie
//attroff(COLOR_PAIR(1));//Dezaktywujemy kolory
refresh();//Odswiezamy (inaczej się nie wyswietli)
WINDOW * menuwin=newwin(7, dlug-12, szer-9, 6); //Definiujemy i tworzymy 'okno'
box(menuwin, 0, 0);
refresh();//ponownie odświeżamy aby okno się pojawiło
wrefresh(menuwin);//odświeżamy samo okno
keypad(menuwin, TRUE);//umozliwiamy dzialanie klawiatury w oknie
char *opcje[] = {
"Tlumacz z Polskiego na Angielski",
"Tlumacz z Angielskiego na Polski",
"Edystuj slownik",
"Wybierz slownik",
"Wyjdz",
};
int wybor;
int zaznacz=0;
while(1)//cala ta petla sluzy ciaglemu tworzeniu menu z podswietleniem wybranego elementu
{
for(i = 0; i < 5; i++) {
if(i == zaznacz)
wattron(menuwin, A_REVERSE);
mvwprintw(menuwin, i+1, 1, opcje[i]);
if (i == zaznacz)
wattroff(menuwin, A_REVERSE);
}
wybor = wgetch(menuwin);
switch(wybor)
{
case KEY_UP:
zaznacz--;
if(zaznacz < 0) zaznacz = 0;//zabezpieczenie przed wyjsciem "poza" menu
break;
case KEY_DOWN:
zaznacz++;
if(zaznacz > 4) zaznacz = 4;
break;
default:
break;
}
if(wybor==10) break;
}
printw("\nWybrano:%s", opcje[zaznacz]);
refresh();
/* Wait for user to press enter to exit */
getch();
/* Need to cleanup before exit */
endwin();
return 0;
}
This is my first time using SDL2 library. When I push W and S (handling the first player position) hold them, and then I push UP on my keyboard, nothing happens. Curious is that other buttons work just fine while I am holding the W and S buttons.
The same thing happens when I do it the opposite way (holding the UP and DOWN buttons...).
Header file:
/**
* Header file for theGame.
*
*/
#include <SDL2/SDL_ttf.h>
// macro definitions
#define HEIGHT 540
#define WIDTH 960
#define SPEED 300
#define MAXBULLETS 1000
#define BULLETSPEED 700
// structs definitions
typedef struct{
bool up, down, left, right, up2, down2, left2, right2;
bool p1Shooting, p2Shooting;
} Action;
typedef struct{
int x;
int y;
bool walking, shooting, alive, facingLeft;
int currentSprite;
int hp;
SDL_Texture *sheetTexture;
} Man;
typedef struct{
int x, y;
bool display; // is it on the screen
bool goingRight;
} Bullet;
typedef struct{
Man *p_p1;
Man *p_p2;
Bullet *bullets;
Action *action;
SDL_Texture *bulletTexture;
int frames;
} gameState;
/**
* Function detects keyboard actions.
*/
void eventsDetection(SDL_Event *event, Action *action, bool *running);
//other stuff
void renderStuff(SDL_Renderer *renderer, gameState game);
void logicStuff(gameState game);
int isInWindow(int x, int y);
void drawText(SDL_Renderer *renderer, char *text, int x, int y);
The problematic function:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_timer.h>
#include <SDL2/SDL_ttf.h>
#include "theGame.h"
void eventsDetection(SDL_Event *event, Action *p_action, bool *running)
{
switch(event->type)
{
case SDL_QUIT:
*running = false;
break;
case SDL_KEYDOWN:
switch (event->key.keysym.scancode)
{
case SDL_SCANCODE_W:
p_action->up = true;
break;
case SDL_SCANCODE_S:
p_action->down = true;
break;
case SDL_SCANCODE_D:
p_action->right = true;
break;
case SDL_SCANCODE_A:
p_action->left = true;
break;
case SDL_SCANCODE_SPACE:
p_action->p1Shooting = true;
break;
}
break;
case SDL_KEYUP:
switch (event->key.keysym.scancode)
{
case SDL_SCANCODE_W:
p_action->up = false;
break;
case SDL_SCANCODE_S:
p_action->down = false;
break;
case SDL_SCANCODE_D:
p_action->right = false;
break;
case SDL_SCANCODE_A:
p_action->left = false;
break;
case SDL_SCANCODE_SPACE:
p_action->p1Shooting = false;
break;
}
break;
}
switch(event->type)
{
case SDL_QUIT:
*running = false;
break;
case SDL_KEYDOWN:
switch (event->key.keysym.scancode)
{
case SDL_SCANCODE_UP:
p_action->up2 = true;
break;
case SDL_SCANCODE_DOWN:
p_action->down2 = true;
break;
case SDL_SCANCODE_RIGHT:
p_action->right2 = true;
break;
case SDL_SCANCODE_LEFT:
p_action->left2 = true;
break;
case SDL_SCANCODE_P:
p_action->p2Shooting = true;
break;
}
break;
case SDL_KEYUP:
switch (event->key.keysym.scancode)
{
case SDL_SCANCODE_UP:
p_action->up2 = false;
break;
case SDL_SCANCODE_DOWN:
p_action->down2 = false;
break;
case SDL_SCANCODE_RIGHT:
p_action->right2 = false;
break;
case SDL_SCANCODE_LEFT:
p_action->left2 = false;
break;
case SDL_SCANCODE_P:
p_action->p2Shooting = false;
break;
}
break;
}
/*printf("----------------------\nACTIONS\n"
"1p: %d up, %d down, %d left, %d right\n"
"2p: %d up, %d down, %d left, %d right\n",
p_action->up,p_action->down,p_action->left,p_action->right,
p_action->up2,p_action->down2,p_action->left2,p_action->right2
);*/
}
And finally important part of main:
// Animation loop
while(running)
{
// events processing
while(SDL_PollEvent(&event))
{
eventsDetection(&event, p_action, &running);
}
// logic stuff
logicStuff(game);
// rendering
renderStuff(renderer, game);
// Show what was drawn
SDL_RenderPresent(renderer);
game.frames = game.frames + 1;
} // end of animation loop
Thank you very much for help.
It's has nothing to do with SDL, it's your keyboard. A common keyboard doesn't handle more than 2-3 keys at the same time and only if the keys are not on the same line or column. More info on wiki
HI.. I want an example of how to implement FSM using function pointers in C.
See this simple example on how to implement a finite state machine in C.
An example is too big to write as an answer here.
Here's an existing example, which I found by Googling for state machine c "function pointer": Implementing Efficient State Machines
Here is a little demo of using function pointers in ARDUINO. This example does not allow for concurrency. It perfectly transferable to normal C if make write the setup and loop inside main()
Each state is a void() function. Each state function is responsible for reading input and setting output. When this is done the function should return immediately. It will be called again directly. The function is also responsible for state-transition by calling the leave function immediately before returning. Each state function should have a static long variable for timekeeping.
A global variable state is set to point to the initial state in the setup routine.
I wanted timekeeping in the different states so i implemented the state transitions by 2 functions:
void enter(long *stateTime), this should be called the very first thing when entering the state functions. It activates the state if inactive end keeps time.
void leave(void (*next)(), long *statetime), this changes the global state pointer and deactivates the current state.
void (*state)();//function pointer for state machine
long prevMillis = 0;//timekeeper
const int LEDPIN = 13;
int counter1 = 0;
void enter(long *statetime){
if(*statetime==-1){//check for passive state
prevMillis = millis();//set timemark when entering state
}//if(statetime==0)
*statetime = millis()-prevMillis;//keep time
}//enter()
void leave(void (*next)(), long *statetime){
*statetime=-1;//set state to passive
state=next;//point to next state
}//leave()
void off500ms(){
static long stateMillis;//timer for this state
enter(&stateMillis);//update timer
digitalWrite(LEDPIN, LOW);
if(stateMillis>499){//check if time is up
leave(on500ms, &stateMillis);
}//if(stateMillis>499)
}//off500ms()
void off2s(){
static long stateMillis;//timer for this state
enter(&stateMillis);//update timer
digitalWrite(LEDPIN, LOW);
if(stateMillis>1999){//check if time is up
leave(on500ms, &stateMillis);
}//if(stateMillis>499)
}//off2s()
void on500ms(){
static long stateMillis;//timer for this state
enter(&stateMillis);//update timer
digitalWrite(LEDPIN, HIGH);
if(stateMillis >499){//time is up
if(++counter1==6){//number of blinks
leave(off2s, &stateMillis);
counter1=0;//reset counter
}else{//if(++counter1==6)
leave(off500ms, &stateMillis);
}//if(++counter1==6)
}//if(stateMills>499)
}//on500ms
void setup(){
pinMode(LEDPIN, OUTPUT);
state = on500ms;//set initial state
}/setup()
void loop(){
state();//start FSM
}//loop
I would say initialize a array of pointers to event handlers. So each element of a array is a function pointer to a particular event which is part of an enum.
if foo is your array of function pointers which is initialized to event then call foo[event]() when any event occurs.
Try coding for calling a function pointer first, next you can move to array and come back to SO if there are more doubts.
For a start you can read about function pointers here.
State transtion code can be utilize either by array or switch case. Written under if else directive.
#include <stdio.h>
#include <stdlib.h>
int entry_state(void);
int foo_state(void);
int bar_state(void);
int exit_state(void);
enum state_codes lookup_transitions(enum state_codes, enum ret_codes);
/* array and enum below must be in sync! */
int (* state[])(void) = { entry_state, foo_state, bar_state, exit_state};
enum state_codes { entry, foo, bar, end};
enum ret_codes { ok, fail, repeat};
struct transition {
enum state_codes src_state;
enum ret_codes ret_code;
enum state_codes dst_state;
};
/* transitions from end state aren't needed */
struct transition state_transitions[] = {
{entry, ok, foo},
{entry, fail, end},
{foo, ok, bar},
{foo, fail, end},
{foo, repeat, foo},
{bar, ok, end},
{bar, fail, end},
{bar, repeat, foo}};
int main(int argc, char *argv[]) {
enum state_codes cur_state = entry;
enum ret_codes rc;
int (* state_fun)(void);
for (;;) {
state_fun = state[cur_state];
rc = state_fun();
if (end == cur_state)
break;
cur_state = lookup_transitions(cur_state, rc);
}
return EXIT_SUCCESS;
}
/*
* lookup_transition() function has time complexity of class O(n).
* We can optimize it.
* */
enum state_codes
lookup_transitions(enum state_codes cur_state, enum ret_codes rc)
{
#if 0
switch (cur_state) {
case entry:
cur_state = ((rc == ok) ? (foo) : (end));
break;
case foo:
cur_state = ((rc == ok) ? (bar) : ((rc == fail) ? (end) : (foo)));
break;
default:
cur_state = ((rc == ok) ? (end) : ((rc == fail) ? (end) : (foo)));
break;
}
return cur_state;
#else
char arr_size = (sizeof(state_transitions) / sizeof(state_transitions[0])); /* This can be shifted to main function to avoid redundant job. */
char count;
for (count = 0; count < arr_size; count++) {
if ((state_transitions[count].src_state == cur_state) && (state_transitions[count].ret_code == rc)) {
return (state_transitions[count].dst_state);
}
}
#endif
}
int entry_state(void)
{
int st;
enum ret_codes rc;
printf("YOU ARE IN ENTRY STATE.\nEnter 0/1: ");
scanf("%d", &st);
rc = ((st == 1) ? (fail) : (ok));
return rc;
}
int foo_state(void)
{
int st;
enum ret_codes rc;
printf("YOU ARE IN FOO STATE.\nEnter 0/1/2: ");
scanf("%d", &st);
rc = ((st == 0) ? (ok) : ((st == 2) ? (repeat) : (fail)));
return rc;
}
int bar_state(void)
{
int st;
enum ret_codes rc;
printf("YOU ARE IN BAR STATE.\nEnter 0/1/2: ");
scanf("%d", &st);
rc = ((st == 0) ? (ok) : ((st == 2) ? (repeat) : (fail)));
return rc;
}
int exit_state(void)
{
printf("YOU ARE IN EXIT STATE.\n");
exit(EXIT_SUCCESS);
}