issue with user input (char give when asking for a #)

Hello,
It's pretty simple, I ask them for a #, and it put's my code into an infinite loop. Here's the specific function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void One::myTurn(){
	bool temp;
	temp=false;

	choice=0;
	cout << "\t   Player 1, it is your turn, pick an open space...\n\n\n\n\n\n\n";
	cin >> choice;
	temp = moveMe(choice);
	while(!temp){
		theBoard.drawBoard();
		if(choice>0 && choice<10) cout << "   That space is taken, please choose an open space, Player 1 it is your move...\n\n\n\n\n\n\n";
		else cout << "Invalid move, please choose an open space... Player 1 it is your move...\n\n\n\n\n\n\n";
		cin >> choice;
		temp = moveMe(choice); 
	}
}


moveMe function:
1
2
3
4
5
6
7
8
9
10
11
bool One::moveMe(int move){
	for(int i=0; i<3; i++){
		for(int k=0; k<3; k++){
			if(numBoard[i][k]==move && !Board[i][k]){
				Board[i][k]=PLAYER_NUM;
				return true;
			}
		}
	}
	return false;
}


Just in case you were curious as to what the moveMe function did. It's for a Tic-Tac-Toe game I've been making, remaking, and changing with the knowlege I get as a programmer. I ask them for their move (1-9) and if it's taken, they get "It's taken move again..." otherwise it's "Invalid move, move again..." but (and I've verified this in debugging) the cin >> choice; statement is bypassed if a letter is inputed. I've tryed most kinds of loops even to the extend just making a goto loop to no avail, I've tried a union, no go there. also, I just tried a new data class to see if it would work...but I haven't gotten it compiled yet, any ideas in the mean time?

My new data class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef INC_NOCHAR_H
#define INC_NOCHAR_H
#include <iostream>
#include <cstdlib>
// specific class/header to address the issue of asking for a number, getting a character, and going into an infinite loop
class noChar{
	// this class is only to be used for the ttt project as it has hardcoded strings and does not follow the common syntax of operator overloading
	char ch;
	int in;
public:
	int operator<<(noChar &o);
};

int noChar::operator<<(noChar &o){
	noChar temp;
	cin >> this->ch;
	temp.in = atoi(this->ch);
	return temp.in;
}

#endif /* INC_NOCHAR_H */ 


enduser000
Why not make choice a char, and convert to a number 0-9?
You would need to add a conditional to say 'Please enter a number', but it should solve your problem.
Faldrax is correct. Just to add more detail, when istream (in this case cin) sees a character in the input stream that does not match what is attempting to be read, it stops and leaves the character in the input stream.

Said another way, your line cin >> choice; is attempting to read an integer from the stream. Any character that is not '0'...'9' cannot be read into the integer, so cin >> choice; returns without actually consuming the bad character. The next time you do cin >> choice; it will again see the same bad character and again stop reading.

There are a couple of ways around the problem, one of which is to do as Faldrax says and make choice a char and convert. This avoids the problem because any character can be read into a char without problems. In your case, this is probably the simplest and best solution.

The most general solution is to flush the input stream to get rid of the bad character(s) and then retry the input.

How would I flush the input stream? Or how would I use a character, atoi()? atoi's parameter's are for a string though... and flush or endl are only for output streams... How would I go about doing that?

enduser000
Last edited on
If the variable is a char you can just cast it to an integer type if you want. The atoi does not necessarily take an array; just a pointer to a character. So you could pass it the memory address of a single character. As for resetting cin's error, I am not quite sure but I think you would use cin.clear().
Last edited on
cin.clear() hasn't worked, and using atoi() has failed also. What do you think I could do? it seems like a simple issue, understanding what '0' is and converting it to an integer or clearing a stream, but It's giving me trouble...
Here's what I'm getting: error C2664: 'atoi' : cannot convert parameter 1 from 'int' to 'const char *'

Thanks for looking,

enduser000
1
2
3
4
5
6
7
8
9
10
char choice;
cout << "Enter your move: " << flush;
cin >> choice;
if( !isdigit( choice ) )
    cout << "Invalid move" << endl;
else  
{
    int move = choice - '0';
    // etc, etc
}

Just to add a bit of explanation to jsmiths post, char is actualy a 1 byte integer which the compiler automatically converts to and from the ASCII equivelent (so '0' = 48).
So in the line
int move = choice - '0';
If you entered '5', this would become

=> int move = '5' - '0';
=> int move = 53 - 48;
=> int move = 5;

By using '0' rather than 48 directly you are guarding against a machine where the character coding is not standard ASCII (IE '0' != 48) - provided the character code has 0123456789 as sequential it will work.
Last edited on
Okay, so I went over my code and added your suggestion. It seems like it will work and thanks for that.

I actually cut 2 headers out of my project because they were almost the same and I just changed a few functions to reuse a lot of code.

I am creating objects of type Player for both player1 and player2 and a pointer to point to them (don't need to know who's move it is).

I want to have variables called PLAYER1_SYM and PLAYER2_SYM. I need there to only be one copy of these variables, you do that with the static modifier in a class right?

Because now I'm getting: error LNK2001: unresolved external symbol "public: static char Player::PLAYER2_SYM" (?PLAYER2_SYM@Player@@2DA)

and can't seem to figure out what's causing it.

enduser000
The static keyword will make it so there is one variable that exists for every instance of the class; sort of like a global variable accessible only using class_name::static_var. If that is what you want, go ahead.

On the error: Do you have something like this? It would be easier for me to figure out the problem if I could see the class code.
1
2
3
4
5
class Player {
public:
  static char PLAYER1_SYM;
}
Player::PLAYER1_SYM = 'x';
Here is what I have,

Player.h
1
2
public:
static char PLAYER1_SYM, PLAYER2_SYM;


Main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <cstdlib>
#include "Board.h"
#include "Player.h"
#include "Computer.h"
using namespace std;

int Board[3][3];
void init();

int main()
{
	init();
	gameBoard theBoard;
	Player *pointme, player1(1), player2(2);
	bool player1turn;
	char c; // for choosing who goes first

	cout << "\n\n\n\n\t\t\t   Welcome to Tic-Tac-Toe!\n\n";
	//cout << "\t\t\t     One or two Players?\n\n";
	cout << "\t\t   Player 1, would you like to go first (y/n)?\n\n";
	cin >> c;
	c = tolower(c);
	while(c!='y' && c!='n'){
		cout << "\t\t   Player 1, would you like to go first (y/n)?\n\n";
		cin >> c;
	}

	if(c=='y'){
		player1turn = true;
		pointme = &player1;
		player2.pickSym(2);
	}
	else{
		player1turn = false;
		pointme = &player2;
		player1.pickSym(1);
	}

	// first 4 moves
	for(int i=0; i<4; i++){
		theBoard.drawBoard();
		pointme->myTurn(pointme->PLAYER_NUM);
		if(player1turn) player1turn=false;
		else player1turn=true;
		if(player1turn) pointme = &player1;
		else pointme = &player2;
	}

	// check for wins while finishing game
	for(;;){
		if(pointme->winCheck()) break;
		theBoard.drawBoard();
		pointme->myTurn(pointme->PLAYER_NUM);
		if(player1turn) player1turn=false;	
		else player1turn=true;					  
		if(player1turn) pointme = &player1;
		else pointme = &player2;
	}

	return 0;
}
									
void init(){
	for(int i=0; i<3; i++){
		for(int k=0; k<3; k++){
			Board[i][k]=0;
		}
	}
}


PLAYER1_SYM is accessed in pickSym(), which is a member of Player, and looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Player::pickSym(int PLAYER_NUM){
	do{
		cout << "\t\t\t      Player " << PLAYER_NUM << ", X or O?\n\n";
		cin >> symChoice;
		symChoice = toupper(symChoice);
		if(symChoice=='X' && PLAYER_NUM==1){
			PLAYER1_SYM='X'; PLAYER2_SYM='O';
		}										
		else if(symChoice=='O' && PLAYER_NUM==1){
			PLAYER1_SYM='O'; PLAYER2_SYM='X';
		}
		else if(symChoice=='X' && PLAYER_NUM==2){
			PLAYER1_SYM='O'; PLAYER2_SYM='X';
		}
		else if(symChoice=='O' && PLAYER_NUM==2){
			PLAYER1_SYM='X'; PLAYER2_SYM='O';
		}
	}while(symChoice!='X' && symChoice!='O');
}


Those are the only things that access PLAYER1_SYM I believe... also, it says the LINK error is in main.obj, does that mean main.cpp's causing the error?

enduser000
Alright,
When I was posting above I realized I could make PLAYER1_SYM and PLAYER2_SYM global, which I just did and it works great. However, I would like to know who to fix the problem above because this is one of my first multi-file projects and it is imperative I learn how to control and eliminate linking errors, as virtually all projects in the professional world are more than one file. Also, I have noticed a considerable speed decrease (yeah, even in a command line tic tac toe game) since making those values global, is that because of it's location in the program or in the files? Thanks for the help so far, the character idea was great, and worked well, later,

enduser000
Last edited on
I would probably make the symbol a member of the Player class.
1
2
3
4
class Player {
public:
  char symbol;
}

Then you simply set and access player1.symbol and player2.symbol and avoid global variables.
I am surprised there is a noticable difference in performance by usign the Globals, but yuo could try eliminating them as above and see if that helps.
Topic archived. No new replies allowed.