Sign in to follow this  
MikeWulf

First try at TicTacToe

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[i] = 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[i] == (tic[i+1]) && (tic[i+2])) && ((tic[i] && tic[i+2] && tic[i+2]) != 42)) {
  if(player == tic[i]) if(player == tic[i+1]) if(player == tic[i+2]) {
  return player;
  }
  if(player == tic[i]) 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 this post


Link to post
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[i] = 42;*/ //one liners are a bad habit!

// Set all pieces to default
for(i = 0; i < 9; i++)
{
tic[i] = 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 this post


Link to post
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 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[i] = 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[i] == (tic[i+1]) && (tic[i+2])) && ((tic[i] && tic[i+2] && tic[i+2]) != 42)) {
if(player == tic[i]) if(player == tic[i+1]) if(player == tic[i+2]) {
return player;
}
if(player == tic[i]) 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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 c2
row 0 0 1 2
row 1 3 4 5
row 2 6 7 8

Share this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Guest Anonymous Poster
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 this post


Link to post
Share on other sites
Quote:
42 or * is the unused spaces
79 is player O
88 is player X


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

const int PLAYER_1 = 79

Share this post


Link to post
Share on other sites
Thanks for the input but I was more asking about the structure of the code. The general idea...I know I could have used classes but since I don't know classes beyond defining members and making public and private members I just used what I did know to make the game. The reason I made it was so that I could actually make something...anything. It's crushing seeing the making of even simple games so far off.

Share this post


Link to post
Share on other sites
Quote:
Original post by MikeWulf
Thanks for the input but I was more asking about the structure of the code. The general idea...I know I could have used classes but since I don't know classes beyond defining members and making public and private members I just used what I did know to make the game. The reason I made it was so that I could actually make something...anything. It's crushing seeing the making of even simple games so far off.


That's perfectly fine. I just wanted to show you ways to clean up the code and make it more elegant. Now, there's still even more things which could be done, but I thought classes was enough.

Now you'll have something to refer to when you start using classes [smile]

Share this post


Link to post
Share on other sites
Personally, i think you should hold off on graphics until you feel really comfortable with the language. You should get comfortable with using classes, pointers, functions, and everything before going onto the hardcore stuff.

Share this post


Link to post
Share on other sites
If you did want a good, simple book to teach you how to use graphics with C++ and Windows, I can suggest Michael Morrison's "Beginning Game Programming". Even if some of the code is complex at the beginning, if you stick with it then it will become more understandable. You also gradually just pick up more C++ knowledge as you go.

With it I made some pretty cool games like Pong and stuff, which my friends all really like.

So I say it would be an excellent choice because it is a simple book that teaches you how to use graphics.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this