• 9
• 11
• 9
• 20
• 12

# First try at TicTacToe

This topic is 4707 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hey. New here. Great, friendly community. On to buisness... I've been learning C++ as my first language for about a month now and have just finished my first 'game' a dos Tic Tac Toe using dev-c++ compiler. It's got no computer controlled movement. I know my code is pretty bad so you don't have to spell that out. I was just wondering if I have grasped the general idea or if I am going off into some stupid hole that I should have seen comming and any optimisations that are obvious to you (and should be to me). I've commented anything that is causing me problems.
/*
TicTacToe by Wulf :)
The basic idea is the board is updated and redisplayed (as I don't know a way
in which I can clear the screen)
42 or * is the unused spaces
79 is player O
88 is player X
*/

#include <iostream>
using namespace std;

void showboard(); // Shows the gameboard
int getinput(); // Gets valid input
int convert(int coord); // Converts the coordinates into a single value
int makemove(int move); // Makes the move
int wincon(); // Holds winconditions

// I know globals are meant to be bad but in this case arn't they best?
char tic[9];
char player = 79;

int main() {
int move; // Should this have a pointer?
char win;
int i;

do {
win = 0;

for(i=0; i<9; i++) tic = 42; // Set all pieces to default
showboard();
do {
do {
move = getinput();
if(move == 0) {
cout << "Thanks for playing!";
return 0;
}
move = convert(move);
move = makemove(move);
} while(move == 50);
win = wincon();
showboard();
// Switch the players around
switch(player) {
case 88:
player = 79;
break;
case 79:
player = 88;
break;
}
} while(win == 0);
/* switch the players around so that it displays the right person as the
winner and makes the loser the first to move if you play again */
switch(player) {
case 88:
player = 79;
break;
case 79:
player = 88;
break;
}
cout << "The winner is player " << player << "!\n";

cout << "\n\nPlay again? (y n): ";
cin >> win ;
} while(win == 'y');

return 0;
}

// ----------------Gets the input here-----------------
int getinput() {
int input;
do {
cout << "\nPlayer - "
<< player
<< " - Give your move in the form of 'yx' (0 to quit): ";
cin >> input;
// I couldn't work out how to do this the right way or a better way...
if(input == 0) return 0;
else if((input == 11) || (input == 12) || (input == 13) ||
(input == 21) || (input == 22) || (input == 23) ||
(input == 31) || (input == 32) || (input == 33)) return input;
else {
cout << "\nInvalid input!\n";
showboard();
input = 0;
}
} while(input == 0);
}

// -----------Converts the coordinates to a single value.---------------
// There has to be a better way than this!
int convert(int coord) {
switch(coord) {
case 11:
coord = 0;
break;
case 12:
coord = 1;
break;
case 13:
coord = 2;
break;
case 21:
coord = 3;
break;
case 22:
coord = 4;
break;
case 23:
coord = 5;
break;
case 31:
coord = 6;
break;
case 32:
coord = 7;
break;
case 33:
coord = 8;
break;
default:
cout << "This can't have happened! ERROR!!!!!!!!!";
break;
}
return coord;
}

// -----------------Makes the move-------------------
int makemove(int move) {
if(tic[move] == 42) tic[move] = player;
else {
return 50;
}
}

// -----------------------Show the board---------------------------
void showboard() {
cout << "\n 123\n"
<< "1" << tic[0] << tic[1] << tic[2] << "\n"
<< "2" << tic[3] << tic[4] << tic[5] << "\n"
<< "3" << tic[6] << tic[7] << tic[8] << "\n";
}

//------------------------Win conditions------------------------
int wincon(){
int i;
for(i=0; i<9; i = i+3) {
// if((tic == (tic[i+1]) && (tic[i+2])) && ((tic && tic[i+2] && tic[i+2]) != 42)) {
if(player == tic) if(player == tic[i+1]) if(player == tic[i+2]) {
return player;
}
if(player == tic) if(player == tic[i+3]) if(player == tic[i+6]) {
return player;
}
// Shouldn't be in the loop I know...
if(player == tic[0]) if(player == tic[4]) if(player == tic[8]) {
return player;
}
// Yes...I know...
if(player == tic[2]) if(player == tic[4]) if(player == tic[6]) {
return player;
}
}
return 0;
}


I really want to move onto a graphical game (maybe pong, eh?) so if you think I am ready, say it, and point me in the right direction. Thanks! <edit> I also seem to get this '[Warning] no newline at end of file' in dev. What is the importance of the newline?</edit>

##### Share on other sites
The cod eitself doesnt look to bad. IMHO though your formatting is horrible to the point of making it entirely too hard to read. You need more white space. Bigger tabs would be nice. Also if your gonna use your braces ilke you do put some whitespace under them. I find that putting braces on lines by them selves help delineate sections of code. So if I had written your code it would look like this:

int main() {    int move; // Should this have a pointer?    char win;    int i;     do     {        win = 0;        /*for(i=0; i<9; i++) tic = 42;*/ //one liners are a bad habit!                 // Set all pieces to default        for(i = 0; i < 9; i++)        {            tic = 42;        }                    showboard();        do        {            do            {                move = getinput();                if(move == 0)                 {                    cout << "Thanks for playing!";                    return 0;                }                                move = convert(move);                move = makemove(move);                            } while(move == 50);                        win = wincon();            showboard();                        // Switch the players around            switch(player)             {                 case 88:                    {                        player = 79;                        break;                    }                    case 79:                    {                        player = 88;                        break;                    }                }                } while(win == 0);         /* switch the players around so that it displays the right person as the        winner and makes the loser the first to move if you play again */        switch(player)         {            case 88:            {                player = 79;                break;            }                case 79:            {                player = 88;                break;            }            }                 cout << "The winner is player " << player << "!\n";          cout << "\n\nPlay again? (y n): ";        cin >> win ;        } while(win == 'y');     return 0;}

Isnt that easier to read? the number of tabs tell you how deep code id in a loop so you can easy tell whats what. Formatting is really upto personal preference though so do whatever your comfortable with. Just remember that alot of coede you write youll go backto one day or another. If its look like what you posted itll confuse the hell outta you. Nevertheless it looks pretty good.

A couple more pointers. using namespace should be avoided. when you need something form a namespace either type namespace::whatever(); or do
a using namespace std::cin; to avoid typing it for cin. All I can really say though is work on readability. For graphics you should check out SDL and allegro. I highly recomend trying to make pong with both them, as one is going to suit your style better than the other and only you can figure that out.

##### Share on other sites
There, I reformatted it with the amazing power of Visual Studio's "Format Selection" command :)

/*TicTacToe by Wulf :)The basic idea is the board is updated and redisplayed (as I don't know a wayin which I can clear the screen)42 or * is the unused spaces79 is player O88 is player X*/#include <iostream>using namespace std;void showboard(); // Shows the gameboardint getinput(); // Gets valid inputint convert(int coord); // Converts the coordinates into a single valueint makemove(int move); // Makes the moveint wincon(); // Holds winconditions// I know globals are meant to be bad but in this case arn't they best?char tic[9];char player = 79;int main() {	int move; // Should this have a pointer?	char win;	int i; 	do {		win = 0;		for(i=0; i<9; i++) tic = 42; // Set all pieces to default		showboard();		do {			do {				move = getinput();				if(move == 0) {					cout << "Thanks for playing!";					return 0;				}				move = convert(move);				move = makemove(move);			} while(move == 50);			win = wincon();			showboard();			// Switch the players around			switch(player) {    case 88:	   player = 79;	   break;   case 79:	   player = 88;	   break;			}		} while(win == 0);		/* switch the players around so that it displays the right person as the		winner and makes the loser the first to move if you play again */		switch(player) {   case 88:	   player = 79;	   break;   case 79:	   player = 88;	   break;		}		cout << "The winner is player " << player << "!\n"; 		cout << "\n\nPlay again? (y n): ";		cin >> win ;	} while(win == 'y');	return 0;}// ----------------Gets the input here-----------------int getinput() {	int input;	do {		cout << "\nPlayer - " 			<< player 			<< " - Give your move in the form of 'yx' (0 to quit): ";		cin >> input;		// I couldn't work out how to do this the right way or a better way...		if(input == 0) return 0;		else if((input == 11) || (input == 12) || (input == 13) || 			(input == 21) || (input == 22) || (input == 23) || 			(input == 31) || (input == 32) || (input == 33)) return input;		else {			cout << "\nInvalid input!\n";			showboard();			input = 0;		}	} while(input == 0);}// -----------Converts the coordinates to a single value.---------------// There has to be a better way than this!int convert(int coord) {	switch(coord) { case 11:	 coord = 0;	 break; case 12:	 coord = 1;	 break; case 13:	 coord = 2;	 break; case 21:	 coord = 3;	 break; case 22:	 coord = 4;	 break; case 23:	 coord = 5;	 break; case 31:	 coord = 6;	 break; case 32:	 coord = 7;	 break;  case 33:	 coord = 8;	 break; default:	 cout << "This can't have happened! ERROR!!!!!!!!!";	 break;	}	return coord;}// -----------------Makes the move-------------------int makemove(int move) {	if(tic[move] == 42) tic[move] = player;	else {		cout << "\nSpace already taken!\n";		return 50;	}}// -----------------------Show the board---------------------------void showboard() {	cout << "\n 123\n"		<< "1" << tic[0] << tic[1] << tic[2] << "\n"		<< "2" << tic[3] << tic[4] << tic[5] << "\n"		<< "3" << tic[6] << tic[7] << tic[8] << "\n";}//------------------------Win conditions------------------------int wincon(){	int i;	for(i=0; i<9; i = i+3) {		// if((tic == (tic[i+1]) && (tic[i+2])) && ((tic && tic[i+2] && tic[i+2]) != 42)) {		if(player == tic) if(player == tic[i+1]) if(player == tic[i+2]) {			return player;		}		if(player == tic) if(player == tic[i+3]) if(player == tic[i+6]) {			return player;		}		// Shouldn't be in the loop I know...		if(player == tic[0]) if(player == tic[4]) if(player == tic[8]) { 			return player;		}		// Yes...I know...		if(player == tic[2]) if(player == tic[4]) if(player == tic[6]) {			return player;		}	}	return 0;}

##### Share on other sites
Cool. I've only read from the same book and it keeps switching between lots of different formatting styles so I kinda just stuck with the lower whitespace. With the namespace thing, why?
<edit> The auto formatting looks alright until I look at the switch's. That is some wacky stuff to me. </edit>

[Edited by - MikeWulf on May 27, 2005 2:09:33 PM]

##### Share on other sites
Name spaces are there so that things dont interfere with eachother. By doing using namespace withjust a namespace you effectivly negate that effect. Granted you probably arent going to name anything cin or cout, but in larger programs that use things from multiple name spaces things would get real messy if you just stripped away all the name spaces(by doing using namespace) many things could even conflict. I doubt this is a good enough explanaition but im sure someone here will clear it up more.

Edit: the auto formatting isnt that bad...And you can get plugins for dev-cpp that do the same thign. But... I really think it ought to be habit that yuo do it right the first time, it'll make spotting error 100 times easier.

##### Share on other sites
To solve you nasty switch statement in convert coords try something like this.

instead of taking in y and x at once you could take them in seperatly and do this:
where y is the row # and x is the column #

int board_width = 3;
int place_on_board = y * board_width + x;

you board would have to be laid out like this:
        c0  c1  c2row 0   0   1   2row 1   3   4   5row 2   6   7   8

##### Share on other sites
For that to work I would need to either ask the user to input the first coord then the second or use something to seperate the two digits into two variables...which I don't know how to do.

##### Share on other sites
I looked over your code. It wasn't bad, but it wasn't good. So, I took the pleasure of rewriting most of it. I didn't go all out, but I tried to make it better, to an extent. Note the code is untested, and incomplete. I didn't redo everything. Those things which I left unaltered I noted in the code, so you'll know.

Also, I tried to comment it well enough so you'd know why I did what I did. Anyway, here it is:

#include <iostream>// It's best not to use entire namespaces, to avoid name clashes.// Rather, use just those componenets which you need:using std::cout;using std::cin;// An easy way of storing coordinates:struct Coord{	Coord() : x( 0 ), y( 0 ) {}	int x, y;		// There's no need to do the huge switch statement.		// Here's why:		//  0 1 2	//  _ _ _	// |_|_|_| 0	// |_|_|_| 1	// |_|_|_| 2		// To convert { column 1, row 2 } to a single int,	// start at the x-offset into the table (column 1), and go down y rows.	// To go down y rows, you simply add the number of cells which	// would make up y rows, in this case y * 3, because a row	// is made up of 3 cells.	// So, the above example would be 1 + 2 * 3 = 7,	// provided the first cell is {0, 0}.		// In code:	const int ToSingleInt() const { return x + ( y * 3 ); }};// Instead of having game data trashed about in the global namespace,// let's encapsulate it into a nice class:class GameBoard{public:	// Instead of using numbers to differentiate between cells,	// use constants:	enum CellContents { // An 'enum' is simply an enumeration of constants,	       // hence the name :)			UNUSED = 0,		PLAYER_X,		PLAYER_O	};	       GameBoard()       {		       // set all cells to unused:	       Reset();       }		// Put the reset logic into it's own function:	void Reset() const	{			for ( int i = 0; i < 9; ++i )			m_boardData[ i ] = UNUSED; // does having constants		                                   // make it so much easier						    // to read?	}		void Draw() const	{			// you can do better here!!				for ( int col = 0; col < 3; ++col ) {						cout << " - - - \n";					for ( int row = 0; row < 3; ++row ) {								cout << "|";				CellContents cell = m_boardData[ col + row * 3 ];								switch ( cell ) {									case PLAYER_X:						cout << "X";						break;											case PLAYER_O:						cout << "O";						break;											case UNUSED:						cout << " ";						break;				};			}						cout << "|\n";		}				cout << " - - - \n";	}		// This method lets the player and computer go	// Returns false if someone won.	const bool DoNextRound() 	{			SetCell( GetPlayerMove(), PLAYER_X );		SetCell( GetCPUMove(), PLAYER_O );				// show the updated board		Draw();				return CheckForWin();	}	private:	CellContents m_boardData[ 9 ]; // Users of this class shouldn't ever                               // directly modify this data		void SetCell( const Coord& Cell, const CellContents Contents ) 	{			m_boardData[ Cell.ToSingleInt() ] = Contents;	}			      	// helper function, pretty much self-explanatory		       	const bool IsCellEmpty( const Coord& Cell )	{				CellContents cell = m_boardData[ Cell.ToSingleInt() ];				if ( cell == UNUSED )			return true;				return false;	}			       	// the external world shouldn't be calling these	const Coord GetPlayerMove() const	{			Coord input;		cout << "\nPlayer: choose column: ";		cin >> input.x;		cout << "\nPlayer: choose row: ";		cin >> input.y;				// check for valid input:		int singleInt = input.ToSingleInt();		if ( singleInt < 0 || singleInt > 8 ) {					cout << "\nInvalid input!\n";						// use recursive logic to keep going			// until they get it right			return GetPlayerMove();		}				return input;	}		const Coord GetCPUMove() const	{			// get random coord:				Coord move = Coord( rand() % 3, rand() % 3 );				if ( !IsCellEmpty( move ) )			return GetCPUMove(); // more recursive logic				return move;	}		// did someone win?	const bool CheckForWin() const	{				// i don't feel like implementing a good, board-size independent		// algorithm here, so just hardcode in the conditions. I didn't do this		// because I'm lazy, and you already did that in your code :)				return true; // just so it compiles	}};void main(){	// instantiate a board	GameBoard board = GameBoard();		bool playAgain = true;		while ( playAgain ) {			// show the initial board		board.Draw();		while ( board.DoNextRound() );				cout << "Play again? ";		cin >> playAgain;	}}

##### Share on other sites
Nooo! Classes in a basic tick tack toe game? There are a time and place for classes, but I don't see this as being one. It is better practice to understand procedural programming before bogging yourself down with classes. They tend to be very hard to follow for beginners (and for none-beginners if they are not done well).

##### Share on other sites
Quote:
 42 or * is the unused spaces79 is player O88 is player X

Using literals directly in code is bad practise, use some consts or #defines instead, viz.:

const int PLAYER_1 = 79