C++ console game input && refresh function - c++

I am writing a small console adventure game and I faced a few problems.
1. The input is kinda laggy, I'm using while loop ( while(getch() == 'w') ). After a key is pressed the first time, nothing happens (you have to press it 2 times) and if you switch directions (press key A/D/S) it also doesn't react the 1st time. If you hold a key, it works fine. How can this be fixed?
2. Here's the function I use to refresh game (render the game scene when a key is pressed):
void refresh(char map[Y][X])
{
system("cls");
for (int i = 0; i<UP; i++)
{
cout<<endl;
}
for (int i = 0; i<Y; i++)
{
for (int k = 0; k<LEFT; k++)
{
cout<<" ";
}
for (int j = 0; j<X; j++)
{
cout<<map[i][j];
}
cout<<endl;
}
}
When I use this function once, it's ok, but when they key is pressed or held multiple times - the game field starts to flash. I figured that I need to redraw only a part of the field (where changes are made/movements are done), not the whole field. Can you offer any ideas how to do that?
The sample code for input:
while(getch() == 'w')
{
if (map[y-1][x]!= WALL)
{
map[y-1][x] = CHARACTER;
map [y][x] = ' ';
y--;
refresh(map);
Sleep(SPEED); // this is unnecessary, SPEED is 0, I just kept it for tests
}
}
Basically, the main function looks like this:
int main()
{
(...) Variables (...)
generateMap(FROM FILE);
refresh(); // First initialization of the field
while (getch() != 'q') // While not quitting
{
while(getch() == 'w')
{
if (THE FIELD ABOVE IS NOT OCCUPIED)
{
setSomeVariables();
refresh(THE GAMEFIELD);
}
}
}
while(getch() == 's')
{
if (THE FIELD BELOW IS NOT OCCUPIED)
{
setSomeVariables();
refresh(THE GAMEFIELD);
}
}
}
while(getch() == 'a')
{
if (THE FIELD ON THE LEFT IS NOT OCCUPIED)
{
setSomeVariables();
refresh(THE GAMEFIELD);
}
}
}
while(getch() == 'd')
{
if (THE FIELD ON THE RIGHT IS NOT OCCUPIED)
{
setSomeVariables();
refresh(THE GAMEFIELD);
}
}
}
return 0;
}

Don't use system("cls"), it is really slow, instead set the cursor on the beginning of the screen with the following code:
COORD cur = {0, 0};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cur);
You should call getch() only one time in the loop, like this:
char key;
do
{
key = getch();
if(key == 'w')
{
//do something
}
//the other if statements
}while(key != 'q');

The code should have only one getch() in the whole code (if possible) and inside a switch you do the action for each input. So, it is switch inside the loop, and not loops inside the switch. Something like this:
while ((ch = getch()) != 'q')
{
switch (ch)
{
case 'a':
GoLeft();
break;
...
}
}
There is a library called ncurses that you can use to move the cursor around the screen, and write anything anywhere.

It looks like your problem is calling getch() multiple times. Have only one loop, and call getch() once per loop, and store the result. Test that result for each value (q, w, s, ...) for each iteration of the loop.

I would recommend you to use function input() as it stands:
void input()
{ if (GetKeyState('W') & 0x8000) // That means when button "W" is pressed
... your code ...
// (changes global variables, or you can pass variables by reference)
}
without any getch() command that stops your game every single time.
You can use it with the draw() and calculate()/refresh() functions in the main(), like:
int main()
{
... other part of code ...
while (!gameover)
{
input();
calculate();
draw();
}
}
so that you will always have some sort of input value before the calculations, and then you would draw (+ it's much easier to debug ;) )

Related

Can't figure out how to loop playerturns and moves Tic Tac Toe (C++) [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
EDIT: Solved now thank you triple_r and AJNeufield for your help on this problem I was having.
I've looked around multiple websites and YouTube about this and I can't seem to find anything on what I am specifically looking for this as my format for the program is a good bit different than others. Therefore, it's hard to decipher where I need to put the things I do need that I know of.
Please note that I'm relatively new to C++ so I'd appreciate all the feedback or criticism you might provide me.
Also, note my code does compile and run it just does not allow me to put in more than one input and more than likely does not allow for a switch of player turns.
Quick Edit: Switched the code with the new setup suggested by triple_r but I seemed to have messed it up somewhere along the line and it does compile(with the exception of x and y not being utilized and one other error) but it always starts off with player 2 going first and as soon as it receives input it ends automatically with a segmentation fault.
#include <iostream>
#include <cstdlib>
using namespace std;
//////////////////////////////////////////////////////////
void initboard(char board[3][3])
{
int x,y;
for (x=0;x<3;x++)
for (y=0;y<3;y++)
board[x][y]=' ';
return;
}
//////////////////////////////////////////////////////////
void printboard(char board[3][3])
{
int x,y;
for (x=0;x<3;x++)
{
cout<<"\n";
for (y=0;y<3;y++)
{
cout<<" "<<board[x][y]<<" ";
if (y<2) cout<<"|";
}
if (x<2) cout<<"\n===========";
}
return;
}
//////////////////////////////////////////////////////////
void getmove(char board[3][3], int player)
{
return;
}
//////////////////////////////////////////////////////////
int main()
{
bool done=false;
char board[3][3];
int x,y,player=1,turn,playerchoice,playermark;
initboard(board);
turn=0;
do
{
if (player==1)
playermark='X';
else
playermark='O';
if (turn%2)
player=1;
else
player=2;
cout<<"Player "<<player<<" where do you want to move?: ";
cin>>playerchoice;
if (playerchoice==1)
{
board[0][0]=playermark;
}
else if (playerchoice==2)
{
board[0][1]=playermark;
}
else if (playerchoice==3)
{
board[0][2]=playermark;
}
else if (playerchoice==4)
{
board[1][0]=playermark;
}
else if (playerchoice==5)
{
board[1][1]=playermark;
}
else if (playerchoice==6)
{
board[1][2]=playermark;
}
else if (playerchoice==7)
{
board[2][0]=playermark;
}
else if (playerchoice==8)
{
board[2][1]=playermark;
}
else if (playerchoice==9)
{
board[2][2]=playermark;
}
else
{
cout<<"Invalid move ";
}
if (board[x][y]!=' ')
cout<<"Move is already taken.";
board[x][y]=playermark;
if(board[x][y]==' ')
turn++;
}while (!done);
void printboard(char board[3][3]);
return 0;
}
EDIT: based on the updated code
So, the first thing I can see is that you are using x and y in your program but you don't initialize them or assign any values to them. Also, try to use functions/classes/... yo make your code more readable. You already have a function for player move but you are not using it. You can move the large if statement inside that function and that will make your main code shorter and more readable.
Here are my comments on the main part of your program:
int main()
{
// add a new variable to see if the move was valid or not:
bool done=false, validmove = true;
char board[3][3];
int x, y, player = 1, turn = 0, playerchoice, playermark;
initboard(board);
do
{
// swap the two `if`s so you decide who`s turn it is then assign the player mark,
// also, reverse the condition to make sure turn '0' is player 1's turn.
if (!(turn % 2))
player = 1;
else
player = 2;
if (player == 1)
playermark = 'X';
else
playermark = 'O';
cout << "Player " << player << " where do you want to move?: ";
cin >> playerchoice;
// Assign `x` and `y` here instead of updating the board, because you want to make
// sure that the move is valid before putting the mark:
validmove = true;
if (playerchoice == 1)
{
x = 0; y = 0;
}
else if (playerchoice == 2)
{
x = 0; y = 1;
}
else if (playerchoice == 3)
{
x = 0; y = 2;
}
else if (playerchoice == 4)
{
x = 1; y = 0;
}
else if (playerchoice == 5)
{
x = 1; y = 1;
}
else if (playerchoice == 6)
{
x = 1; y = 2;
}
else if (playerchoice == 7)
{
x = 2; y = 0;
}
else if (playerchoice == 8)
{
x = 2; y = 1;
}
else if (playerchoice == 9)
{
x = 2; y = 2;
}
else
{
cout << "Invalid move, try again!";
// Make sure to mark the move as invalid so they get a chance to
// change their move:
validmove = false;
}
// check to see if the turn was valid:
if(validmove)
{
if (board[x][y] != ' ')
{
cout << "Move is already taken, try again";
}
else
{
board[x][y] = playermark;
turn++;
}
}
// have to make sure you have a condition for end of game. A simple
// one is to check if turn is less than `9`, otherwise the board is
// full:
if(turn == 9)
done = true;
// you probably want to add a few more checks to see who won the game.
}while (!done);
// when calling a function, no need to put the return type or parameter type:
printboard(board);
return 0;
}
========================================================================
There are two do-while loops in your program and both seem to be meant as a game loop. What I would do is:
initboard(...);
turn = 0;
do{
//this is the game loop
...;
if( validturn )
turn++;
}while(!done);
release_resources(...);
return 0;
so, you fold everything into one loop. In that loop, you want to:
find who's turn it is:
if (turn % 2)
player = 1;
else
player = 2;
get users input:
std::cin >> playerchoice;
...
convert player choice to grid location:
switch ( move )
{
case 0:
x = 0;
y = 0;
break;
case 1:
...;
...
default:
//invalid move
}
see if the move is valid:
if( board[x][y] != ' ' )
//already taken, invalid move
then apply the move:
board[x][y] = playermark;
I hope this helps.
Your cin >> playerchoice is outside your do { ... } while ( moves != 9); loop. Move it inside.

GetAsyncKeyState not working

I'm fairly new to C++ as I'm more experienced with Java/Python etc. But, I've been trying to implement a simple Trigger Bot, but added a failsafe so that if I press a certain key, the program will call the exit(0) method. But the way I implemented key input doesn't seem to work, could someone maybe help me out?
void MainScan(Contents scan) {
#if DB
int debug = clock();
#endif
while (true) {
for (int y = scan.startY; y < scan.compareY; y++) {
for (int x = scan.startX; x < scan.compareX; x++) {
//SetCursorPos(x, y);
if (GetAsyncKeyState(VK_DELETE)) {
exit(0);
}
}
}
}
}
Here's how you use it: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646293(v=vs.85).aspx
Here's a snippet of code from an old project of mine for a console based maze game:
Difficulty AskDifficulty() {
// xy norm = 1, 2 y++ for rest
point base(1, 2);
drawatxy(1, 2, '*');
while (GetAsyncKeyState(VK_RETURN)) // while it is being pressed, do not consider any input until we let go of the key
g_delay(0.001);
while (true) { // now we let go of it, consider inputs once more
if (GetAsyncKeyState(VK_RETURN) & 0x8000) {
switch (base.y) {
case 2:
return DIFF_EASY;
case 3:
return DIFF_NORM;
case 4:
return DIFF_HARD;
default:
return DIFF_INVALID;
}
}
else if (GetAsyncKeyState(VK_DOWN) & 0x8000) {
if (base.y < 4) {
drawatxy(1, base.y, ' ');
base.y++;
drawatxy(1, base.y, '*');
g_delay(0.125);
}
}
else if (GetAsyncKeyState(VK_UP) & 0x8000) {
if (base.y > 2) {
drawatxy(1, base.y, ' ');
base.y--;
drawatxy(1, base.y, '*');
g_delay(0.125);
}
}
else
_getch();
}
}

Can I use GetAsyncKeyState() outside of the main() function?

I'm writing, just to get confidence with the programming procedures, a win32 application that responds to keyboard input. To do so I'm using the GetAsyncKeyState() function.
At first I wrote all my code in the main() function, and all seemed well, it worked. So I decided to complicate things, but that requires me to use the GetAsyncKeyState() function in a different function called by the main(). I thought I just needed to declare some variables outside the main() and move the code from the main to the new function like this:
int btnup_down = 0;
int close = 1;
int main(void){
while (1){
Sleep(50);
listentokb();
if (close == 0){
break;
}
}return 0;
}
int listentokb(void){
if ((GetAsyncKeyState(0x4C) & 0x8000) && (ko == 0)){
ko = 1;
printf("Ok you pressed k");
return 0;
} else if (((GetAsyncKeyState(0x4C) == 0) && (ko == 1)) {
ko = 0;
printf("Now you released it");
close = 0;
return 0;
}return 0;
}
When I run this code, the loop keeps going and it doesn't matter if I press the key or not, it keeps looping without printf-ing anything. Any help will be greatly appreaciated.
Your problem has nothing to to with main() or not. You can call winapi function such as GetAsyncKeyState() from wherever you want in your code, as long as you provide the good arrguments.
According to this list of virtual key codes the code 0x4c corresponds to the key L and not to the key K. So after a bracket correction typo in your code, I could run it succesfully interupting the loop with L
Some remarks about your function:
Your function listentokb() always returns 0. On the other hand, you use a global variable close to tell the calling function the result of your keyboard scanning. This is a very bad practice: whenever possible avoid global variables.
Here a slightly updated version of your code that bans global variables, and use return value to communicate the results:
const int KEY_K = 0x4B; // avoid using values directly in the code
int listentokb (void){ // returns 'K' if K is released and 0 otherwise
static int ko; // this is like a global variable: it will keep the value from one call to the other
// but it has teh advantage of being seen only by your function
if((GetAsyncKeyState(KEY_K) & 0x8000) && (ko == 0)){
ko = 1;
printf("Ok you pressed k");
return 0;
}
else if((GetAsyncKeyState(KEY_K) == 0) && (ko == 1)) {
ko = 0;
printf("Now you released it");
return 'K';
}
return 0;
}
int main(void){
bool go_on = true; // The state of the loop shall be local variable not global
while(go_on){
Sleep(50);
go_on= ! listentokb(); // if returns 0 we go on
}
return 0;
}

c++ tron 2d array repeating

I am trying to get a trail to appear behind the player bike but for some reason instead of a "x" appearing behind the player every time it moves, the player would actually duplicate itself. It sounds a little confusing but you should compile this code yourself and see what i mean. What I am trying to do is just have a trail of "x" behind the player instead of the player leaving a trail of "P". thanks
#include <iostream>
#include "windows.h"
#include <conio.h>
#include <ctime>
using namespace std;
//prototype functions used
void DisplayMap();
void PlayerBike();
void setCursorTo();
void SetBike();
//global variables that will be used by different functions
int PlayerX = 10;
int PlayerY = 70;
bool GameOver = false;
const int H = 25; // const variable so it doesnt change size
const int W = 82;// const variable so it doesnt change size
char Map[H][W]; // char map with HxW
char trail = 'x'; // this is where the trail is initialized as a *
int main()
{
SetBike();
DisplayMap();
while (GameOver == false){
setCursorTo();
PlayerBike();
} // end while loop
return 0;
}//end main
void DisplayMap(){ // display map function
for(int i = 0; i < H; i++ ){
for(int j = 0; j < W; j++){
if(i == 0 || i == 24 || j == 0 || j == 81 ){ Map[i][j] = 'x';} // characters in row 24x81 are changed to x
cout << Map[i][j]; // output map
} // end for loop
cout << "\n"; // create new line to output the map correctly
} //end for loop
} // end DisplayMap function
void SetBike(){
Map[PlayerX] [PlayerY] = 'P';
}
void PlayerBike(){
Map[PlayerY][PlayerX]= trail; // I would like this trail to repeat behind the player but it does not appear at all.
if (kbhit()) {// get user key input
char GetCh = getch(); // GetCh equal to the button the user presses
if (GetCh == 'w'){PlayerX = PlayerX - 1; Trailx = Trailx -1;}
else if (GetCh == 's'){PlayerX = PlayerX +1; Trailx = Trailx +1;}
else if (GetCh == 'd'){PlayerY = PlayerY +1;}
else if (GetCh == 'a'){PlayerY = PlayerY - 1;}
}// end kbhit
}// end PlayerBike function
void setCursorTo() // stops constant flashing on the map
{
HANDLE handle;
COORD position;
handle = GetStdHandle(STD_OUTPUT_HANDLE);
position.X = 0;
position.Y = 0;
SetConsoleCursorPosition(handle, position);
}
Your DisplayMap function is flawed.
First, it seems you are not only displaying the map, you are also actively modifying it. Put the drawing-a-border into a separate initMap function, which also zeros out all other positions with a space (it seems you didn't do that yet, so perhaps that is where it goes wrong). You only need to call initMap once.
Next, do not draw the player P in the DisplayMap function. Draw this once, before entering the game loop. Then: if the user pressed a valid move key,
put an x on the player's position
update player position
put a P on the new position
redraw the screen by calling DisplayMap
and you'll see the trail stays put.
Possible refinements: before accepting a 'move' command by updating the position, check if the map contains a space or something else. If it contains a space, you can execute the move; if not, play an explosion animation (*oO*+.). Also, consider looking up the switch statement in your favourite C reference, to avoid endless long sequences of if..else.

Changing while loop to accommodate two situations

Suppose I have a while loop that depends on two separate inputs. In situation one, the while loop will take the value 1, and in situation two, it should take !cin.eof(). Is there a way I can do this efficiently? To be more concise:
string hello;
cin >> hello;
if(hello == "one")
{
//make the while loop depend on value 1
}
else if(hello == "two")
{
//make the while loop depend on value !cin.eof()
}
while(/*depends on above conditional*/)
{}
I don't want to do something like:
if(hello == "one)
{
while(1){}
}
else if(hello == "two")
{
while(!cin.eof){}
}
because the while loop essentially does the same thing in each situation.
For readability and in the interest of cohesion, I think you should move the contents of your loop into a separate function:
void DoSomething() { /* ... */ }
// ...
if(hello == "one)
{
while(1){ DoSomething(); }
}
else if(hello == "two")
{
while(!cin.eof){ DoSomething(); }
}
It's easier to see that the different while loops are doing the same thing but their conditions are different.
I believe you're looking for something like this:
while((hello == "one") || (hello == "two" && !cin.eof)) {
}
This code will do what you want, because it checks 'is the variable "one"? If so, keep executing. If it's not, it'll check: Is the variable "two"? If so, it'll check for cin.eof.
If it's neither, the loop won't execute. (the && 1 in the first condition was omitted, because it's always 'true', equalling and infinite loop)
Edit:
To simplify things, you may want to consider this code (as suggested in the comments):
bool HelloIsOne = (strcmp(hello, "one") == 0);
bool HelloIsTwo = (strcmp(hello, "two") == 0);
while(HelloIsOne || HelloIsTwo && !cin.eof) {
}
The brackets, which I placed in the previous example are actually unnecessary, because && binds stronger than ||, but they help the general clarity of the code.
Simply use or (||) as a condition in the while loop. Set the first condition if(hello == "one"). Now you have a while loop that will loop if one of the conditions is true.
bool value = hello == "one";
while (value || !cin.eof) {}
If you're using C++11:
#include <functional>
auto check = (hello == "one") ? []() bool -> { return 1; } :
[]() bool -> { return !cin.eof(); };
while(check) {
};
How about this:
switch(hello)
{
case 'one':
{
for(; 1; );
{
// your loop here
}
break;
}
case 'two':
{
for(;!cin.eof; )
{
// your other loop here
}
break;
}
default:
{
cout << " shouldnt get here unless bad user input" << endl;
break;
}
}
You can do something like this:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string hello;
cin >> hello;
while(hello=="one"?1:(!cin.eof()))
{
//do stuff
}
return 0;
}
It checks if the string hello is "one" and if it's true, the condition of the while is 1, else it is !cin.eof() as you wanted.