Tic Tac Toe

Started by
22 comments, last by GameDev.net 19 years, 5 months ago
I made this for one of my classes. Just thought i'd share the source code. It's a console application in c++.

// prog3.cpp     Tic Tac Toe
//
//The artificial intelligence is found in the compMove() method in the
//class Board. First it checks for a winning move, then it 
//checks for a move to avoid losing. If neither a winning move or
//a move to avoid losing is found, it will evaluate each position using
//the evaluation fuction discussed in class. 
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;

class Board
{        
public:
		
	// I won't use the 0 index of board[].  Just 1 through 9
	char board[10];

	char chip;     //player's X/O
	char compChip; //computers X/O

	//pointer to character is a String
	//These strings are used to draw the board on the command line.
    char * line1;
    char * line2;
    char * line3;
    char * line4;   
    char * line5;
    char * line6;

	//constructor sets the strings used to draw the board
    Board() 
    {
        line1 = " 1| 2| 3";
        line2 = "__|__|__";
        line3 = " 4| 5| 6";
        line4 = "__|__|__";
        line5 = " 7| 8| 9";
        line6 = "  |  |  ";
    }

	//draws the board
    void drawBoard()
    {
        cout << endl;
        cout << line1 << endl;
        cout << line2 << endl;
        cout << line3 << endl;
        cout << line4 << endl;
        cout << line5 << endl;
        cout << line6 << endl;
    }

	//checks for all 8 possible winning rows. If a winning row is found
	//the game is over.
	bool checkForWin(char chip)
	{
		if(board[1]==chip && board[2]==chip && board[3]==chip)
		{
			return 1; //end game;
		}
		else if(board[4]==chip && board[5]==chip && board[6]==chip)
		{
			return 1; //end game;
		}
		else if(board[7]==chip && board[8]==chip && board[9]==chip)
		{
			return 1; //end game;
		}
		else if(board[1]==chip && board[4]==chip && board[7]==chip)
		{
			return 1; //end game;
		}
		else if(board[2]==chip && board[5]==chip && board[8]==chip)
		{
			return 1; //end game;
		}
		else if(board[3]==chip && board[6]==chip && board[9]==chip)
		{
			return 1; //end game;
		}
		else if(board[1]==chip && board[5]==chip && board[9]==chip)
		{
			return 1; //end game;
		}
		else if(board[3]==chip && board[5]==chip && board[7]==chip)
		{
			return 1; //end game;
		}
		else
			return 0; //keep playing
	}
	
	//same as above function but with an extra array parameter.
	bool checkForWin2(char chip, char array[10])
	{
		if(array[1]==chip && array[2]==chip && array[3]==chip)
		{
			return 1; //end game;
		}
		else if(array[4]==chip && array[5]==chip && array[6]==chip)
		{
			return 1; //end game;
		}
		else if(array[7]==chip && array[8]==chip && array[9]==chip)
		{
			return 1; //end game;
		}
		else if(array[1]==chip && array[4]==chip && array[7]==chip)
		{
			return 1; //end game;
		}
		else if(array[2]==chip && array[5]==chip && array[8]==chip)
		{
			return 1; //end game;
		}
		else if(array[3]==chip && array[6]==chip && array[9]==chip)
		{
			return 1; //end game;
		}
		else if(array[1]==chip && array[5]==chip && array[9]==chip)
		{
			return 1; //end game;
		}
		else if(array[3]==chip && array[5]==chip && array[7]==chip)
		{
			return 1; //end game;
		}
		else
			return 0; //keep playing
	}

	//If all the spaces on the board are taken, the game is tied. This function is called
	//after checkForWin() so there is no mistaking a win for a tie.
	bool checkForTie()
	{
		if(board[1]!='\0' && board[2]!='\0' && board[3]!='\0' && board[4]!='\0' && board[5]!='\0' 
		&& board[6]!='\0' && board[7]!='\0' && board[8]!='\0' && board[9]!='\0')
			return true;
		else
			return false;
	}

	//edit's the strings used to draw the Board.
	void editStrings(int position, char chipp)
	{
		switch(position)
		{
			case 1:
				*(line1+1) = chipp;		
				break;
			case 2:
				*(line1+4) = chipp;
				break;
			case 3:
				*(line1+7) = chipp;
				break;
			case 4:
				*(line3+1) = chipp;
				break;
			case 5:
				*(line3+4) = chipp;
				break;
			case 6:
				*(line3+7) = chipp;
				break;
			case 7:
				*(line5+1) = chipp;
				break;
			case 8:
				*(line5+4) = chipp;
				break;
			case 9:
				*(line5+7) = chipp;
				break;
		}
	}

	//This fuction allows the user to enter a position on the board.
	void playerMove(char chip)
	{
		CHOOSEAGAIN: //goto this label if they input an incorrect board position.
		drawBoard();
		int position;
		cout << "Enter your position: ";
		cin >> position;
		if(position<1 || position>9)
		{
			cout << "Must enter number 1-9\n";
			goto CHOOSEAGAIN;
		}
		if(board[position] != '\0')
		{
			cout << "That position is taken\n";
			goto CHOOSEAGAIN;
		}
		//they have selected a valid position, so assign it to the array
		board[position] = chip;

		//edit the strings in class Board
		editStrings(position, chip);
	}

	//Handles the computer's move. First it looks ahead for a winning move. Then looks
	//ahead for a move to avoid losing. If no winning or losing moves are found, it 
	//will then evaluate each possilbe move and choose the one with the highest score.
	void compMove(char compChip)
	{
		//look ahead for a win
		for(int i=1; i<=9; i++)
		{
			if(board=='\0')//if the space is free
			{
				//copy the board
				char tempBoard[10];
				for(int n=0; n<=9; n++)
				{
					tempBoard[n]= board[n];
				}

				//assigning to the temporary board lets me "look ahead" one move
				tempBoard= compChip;

				//if it found a win, take it
				if(checkForWin2(compChip, tempBoard))
				{
					board = compChip; //makes the move
					editStrings(i,compChip);
					return;
				}
			}	
		}
		
		//look ahead for a loss. Similar to looking ahead for a win,
		//but i "look ahead" with the opponents chip.
		for(int i=1; i<=9; i++)
		{
			if(board=='\0')//if the space is free
			{
				//copy the board
				char tempBoard[10];
				for(int n=0; n<=9; n++)
				{
					tempBoard[n]= board[n];
				}

				//assigning to the temporary board lets me "look ahead" one move
				tempBoard= chip;

				//if it found a position that let's your oppenent win, take it
				//so the oppenent can't.
				if(checkForWin2(chip, tempBoard))
				{
					board = compChip; //makes the move
					editStrings(i,compChip);
					return;
				}
			}	
		}

		//evaluate moves
		//array[] used to hold the evaluation score for each position
		int array[10];

		//fill array with very small initial scores. Smaller than any position 
		//can evaluate to during the game.
		for(int i=0; i<=9; i++)
		{
			array = -1000;
		}
		//evaluate each space on the board
		for(int i=1; i<=9; i++)
		{
			if(board == '\0')//if space is free, evaluate it
			{
				//copy the board
				char tempBoard[10];
				for(int n=0; n<=9; n++)
				{
					tempBoard[n]= board[n];
				}

				//assigning to the tempBoard lets me Look ahead 1 move before i actually
				//make it and computes the score using the evaluation function discussed
				//in class.
				tempBoard = compChip;
				int score = 0;
				int compTotal = 0;
				int humanTotal =0;
				int r=0;
				int c=0;
				int d=0;
	
				//add up ways computer can win.
				//rows
				if((tempBoard[1]=='\0'||tempBoard[1]==compChip) && (tempBoard[2]=='\0'||tempBoard[2]==compChip) && (tempBoard[3]=='\0'||tempBoard[3]==compChip))
					r++;
				if((tempBoard[4]=='\0'||tempBoard[4]==compChip) && (tempBoard[5]=='\0'||tempBoard[5]==compChip) && (tempBoard[6]=='\0'||tempBoard[6]==compChip))
					r++;
				if((tempBoard[7]=='\0'||tempBoard[7]==compChip) && (tempBoard[8]=='\0'||tempBoard[8]==compChip) && (tempBoard[9]=='\0'||tempBoard[9]==compChip))
					r++;
				//columns
				if((tempBoard[1]=='\0'||tempBoard[1]==compChip) && (tempBoard[4]=='\0'||tempBoard[4]==compChip) && (tempBoard[7]=='\0'||tempBoard[7]==compChip))
					c++;
				if((tempBoard[2]=='\0'||tempBoard[2]==compChip) && (tempBoard[5]=='\0'||tempBoard[5]==compChip) && (tempBoard[8]=='\0'||tempBoard[8]==compChip))
					c++;
				if((tempBoard[3]=='\0'||tempBoard[3]==compChip) && (tempBoard[6]=='\0'||tempBoard[6]==compChip) && (tempBoard[9]=='\0'||tempBoard[9]==compChip))
					c++;
				//diagonals
				if((tempBoard[1]=='\0'||tempBoard[1]==compChip) && (tempBoard[5]=='\0'||tempBoard[5]==compChip) && (tempBoard[9]=='\0'||tempBoard[9]==compChip))
					d++;
				if((tempBoard[3]=='\0'||tempBoard[3]==compChip) && (tempBoard[5]=='\0'||tempBoard[5]==compChip) && (tempBoard[7]=='\0'||tempBoard[7]==compChip))
					d++;
				compTotal = r+c+d;
				

				//add up ways the human can win.
				r=0;
				c=0;
				d=0;
				//rows
				if((tempBoard[1]=='\0'||tempBoard[1]!=compChip) && (tempBoard[2]=='\0'||tempBoard[2]!=compChip) && (tempBoard[3]=='\0'||tempBoard[3]!=compChip))
					r++;
				if((tempBoard[4]=='\0'||tempBoard[4]!=compChip) && (tempBoard[5]=='\0'||tempBoard[5]!=compChip) && (tempBoard[6]=='\0'||tempBoard[6]!=compChip))
					r++;
				if((tempBoard[7]=='\0'||tempBoard[7]!=compChip) && (tempBoard[8]=='\0'||tempBoard[8]!=compChip) && (tempBoard[9]=='\0'||tempBoard[9]!=compChip))
					r++;
				//columns
				if((tempBoard[1]=='\0'||tempBoard[1]!=compChip) && (tempBoard[4]=='\0'||tempBoard[4]!=compChip) && (tempBoard[7]=='\0'||tempBoard[7]!=compChip))
					c++;
				if((tempBoard[2]=='\0'||tempBoard[2]!=compChip) && (tempBoard[5]=='\0'||tempBoard[5]!=compChip) && (tempBoard[8]=='\0'||tempBoard[8]!=compChip))
					c++;
				if((tempBoard[3]=='\0'||tempBoard[3]!=compChip) && (tempBoard[6]=='\0'||tempBoard[6]!=compChip) && (tempBoard[9]=='\0'||tempBoard[9]!=compChip))
					c++;
				//diagonals
				if((tempBoard[1]=='\0'||tempBoard[1]!=compChip) && (tempBoard[5]=='\0'||tempBoard[5]!=compChip) && (tempBoard[9]=='\0'||tempBoard[9]!=compChip))
					d++;
				if((tempBoard[3]=='\0'||tempBoard[3]!=compChip) && (tempBoard[5]=='\0'||tempBoard[5]!=compChip) && (tempBoard[7]=='\0'||tempBoard[7]!=compChip))
					d++;
				humanTotal = r+d+c;
				score = compTotal - humanTotal;
				//assign the score to array[]
				array=score;
			}
			else //space is not free
			{
				//if the space is already taken, give it a super
				//low score so the computer doens't move there.
				array=-10000000;
			}
		}//end for loop

		//The index of the highest score in array[] will be the index
		//of board[] that the computer will put it's chip on.
		int bestScore = -100;
		int bestPosition;
		for(int i=1; i<=9; i++)
		{
			if(array>bestScore)
			{
				bestScore=array;
				bestPosition=i;
			}
		}
		//bestPosition has been found so put the chip on the board.
		board[bestPosition] = compChip;

		//edit the strings that display the board on the command line
		editStrings(bestPosition, compChip);
	}
};//end class Board


int main()
{
    START:           //This label is used to reset the game.
    bool first;      //if human player goes first, then true
	Board b;         //Board object

	cout << "PRESS CONTROL + C TO QUIT THE GAME AT ANY TIME\n";

	//prompt for chip type
    cout << "Do you want to be X or O? ";
    cin >> b.chip;
    if(b.chip != 'x' && b.chip != 'o' && b.chip != 'X' && b.chip != 'O')
    {
        cout << "You must enter X or O \n\n";
        goto START; 
    }
	//the computer's chip is the opposite of the players.
	if(b.chip=='x' || b.chip=='X')
		b.compChip = 'O';
	else
		b.compChip = 'X';

	//ask whether 1st or 2cd
    cout << "Do you want to go first? Y/N ";
    char yn;
    cin >> yn;
    if(yn=='y' || yn=='Y')
        first = true;
    else if(yn=='n' || yn=='N')
        first = false;
    else
    {
        cout << "You must enter 'Y' or 'N'\n\n";
        goto START; 
    }

	//fill each position of b.board[] with null '\0'
    for(int i=0; i<=9; i++)
    {
		b.board = '\0';
    }

	//infinite loop exited with break statment when the game is over.
    while(true)
    {
        if(first)//human player 1st
		{
			//human moves, then checks to see if you won or tied.
			b.playerMove(b.chip);
			if(b.checkForWin(b.chip))
			{
				b.drawBoard();
				cout << "You won!!!\n";
				break;
			}
			if(b.checkForTie())
			{
				b.drawBoard();
				cout << "Tie game!!!\n";
				break;
			}

			//computer moves, then checks if it won or tied.
			b.compMove(b.compChip);
			if(b.checkForWin(b.compChip))
			{
				b.drawBoard();
				cout << "You lose!!!\n";
				break;
			}
			if(b.checkForTie())
			{
				b.drawBoard();
				cout << "Tie game!!!\n";
				break;
			}
		}
		else //human player moves 2cd
		{
			//human moves, then checks to see if you won or tied.
			b.compMove(b.compChip);
			if(b.checkForWin(b.compChip))
			{
				b.drawBoard();
				cout << "You lose!!!\n";
				break;
			}
			if(b.checkForTie())
			{
				b.drawBoard();
				cout << "Tie game!!!\n";
				break;
			}

			//computer moves, then checks if it won or tied.
			b.playerMove(b.chip);
			if(b.checkForWin(b.chip)) /*check for win*/
			{
				b.drawBoard();
				cout << "You won!!!\n";
				break;
			}
			if(b.checkForTie()) /*check for tie*/
			{
				b.drawBoard();
				cout << "Tie game!!!\n";
				break;
			}
		}
    }//end infinite loop

	return 0;
}//end main()

Advertisement
some of the indentations got messed up when i cut and pasted, so it looks a little messy.
The game crashes whenever I make a move. Also, have you thought about spreading your code over the span of more than one files? It makes everything much more easier to find and readable.
It runs just fine on my home computer(windows XP) when complied it with Visual STudio.NET C++.

When i ftp'd it to ark (sun system) and compiled it with the g++ compiler, I got a segmentation fault whenever i made a move. Not sure why it is crashing on the non-windows systems, but it does work.

Ya, I probably should have the functions for the board Class in ther own file. compMove() is a huge function.
I'm running Windows XP as well and compiled it in VC++.NET and there weere no compile errors but it still crashed when I made a move. Have you ever tried running your program in debug mode? It sure helps a lot when your facing a runtime error and you can't find what line(s) of code is causing it.
See my comments in-line with the source.
I reformatted things to match my preferred style a bit more. :/

class Board {          public:  // I won't use the 0 index of board[].  Just 1 through 9  char board[10];  // no no no no no. Trust me, to be an effective programmer you must learn to  // work with zero-based indexing rather than against it.  // Also, you didn't initialize the board in your constructor. That's bad.  // It explains things working in debug mode and not release mode, too.  // Also, learn to use initializer lists in constructors.  char chip;     //player's X/O  char compChip; //computers X/O  // public data is bad, btw. You should be determining the player's or  // computer's X/O in the constructor, according to a constructor parameter  // telling you who moves first. That makes sure the two values are consistent  // and don't get modified later.  //pointer to character is a String  // No, it isn't. You included <string>. Use it for heaven's sake.  // Also, use an array for these variables, so you can iterate over them  // in drawBoard, etc. Any time you want to put a number into a variable  // name, this is a sign that something's not right.  //checks for all 8 possible winning rows. If a winning row is found  //the game is over.  bool checkForWin(char chip)  {    if(board[1]==chip && board[2]==chip && board[3]==chip) {      return 1; //end game;    }  else if(board[4]==chip && board[5]==chip && board[6]==chip) {      return 1; //end game;    }    // etc.    // There are more concise ways to express this sort of thing, but it's    // OK for now. Writing the 1's and 0's for a function returning bool is    // bad stylistically though; return true and false like you do later on.  }    //same as above function but with an extra array parameter.  bool checkForWin2(char chip, char array[10]) {    // Why??? You shouldn't have arrays representing a board, but just separate    // Board objects, and you call the checkForWin on the appropriate Board.    // That's why you make a Board class in the first place. Although I don't    // see why you'd need that, even.    // Also, having cut-and-paste things like this is bad practice in general.    // If you must create something like this in the future, make one of the    // functions *call* the other, e.g.:    // bool checkForWin(char chip) {    //   checkForWin2(chip, board);    // }    // Finally, methods that don't actually rely on the Board state should    // usually be declared 'static'.  }  //If all the spaces on the board are taken, the game is tied. This function is called  //after checkForWin() so there is no mistaking a win for a tie.  bool checkForTie() {    if(etc)      return true;    else      return false;    // Instead, please please please get in the habit of writing this as:    // return etc;    // If it's a long expression like it is here, you may want to put brackets    // around it for clarity.    // The if-else logic here adds code and removes clarity. Instead of saying    // "tell me whether every square is occupied", it says "if every square    // is occupied, tell me every square is occupied; otherwise, tell me    // not every square is occupied".  }  //edit's the strings used to draw the Board.  void editStrings(int position, char chipp) {    // It's ok to have a parameter with the same name as a member variable.    // Inside that method, the name will default to referring to the parameter.    // To force using the member variable, you can write this->chip.    switch(position) {      // AARGH. Use arithmetic to deal with these things and avoid the      // redundancy. This is why you must learn to use zero-based indexing      // and arrays of similar variables.      // BTW, it's true that asking the user to start counting at zero makes      // for bad UI. You should do conversions right at the "surface layer".      // The whole switch statement could then be reduced to:      // position -= 1; // convert to zero-based index      // if (position < 0 || position > 8) return; // reject bad input      // // now use div and mod to get a row/column value, and scale the      // // column value appropriately for the ascii graphics.      // line[position/3][position % 3 * 3 + 1] = chip;      // (Here I assume the "constant" lines are not stored in the array.)    }  }  // Although, it looks really bad that you have this "editStrings" thing that  // gets called to update the string contents each time a move is made, and   // you also record the moves inside a 'board' array.  // This duplicates the information about where the pieces are.  // Instead, try to make it so that the drawBoard() *looks at the board[]*  // and draws it piece by piece.  //This fuction allows the user to enter a position on the board.  void playerMove(char chip) {    CHOOSEAGAIN: //goto this label if they input an incorrect board position.    // Gotos have their place, but *emulating a while loop* is not that place.    // URRRGH. The baby Stroustrup cries.    drawBoard();    int position;    cout << "Enter your position: ";    cin >> position;    // NOTE: You are completely and utterly screwed if something non-numeric    // is entered here. For more information, see:    // http://www.augustcouncil.com/~tgibson/tutorial/iotips.html    // etc.  }  // Also, I/O type stuff like this generally should *not* go inside the class.  // You should get the move from the user in the main loop, and then call  // a Board method to actually make the move.  //Handles the computer's move. First it looks ahead for a winning move. Then looks  //ahead for a move to avoid losing. If no winning or losing moves are found, it   //will then evaluate each possilbe move and choose the one with the highest score.  void compMove(char compChip) {    //look ahead for a win    for(int i=1; i<=9; i++) {      if(board=='\0') { //if the space is free        //copy the board        char tempBoard[10];        for(int n=0; n<=9; n++) {          tempBoard[n]= board[n];        }        //assigning to the temporary board lets me "look ahead" one move        tempBoard= compChip;        // Oh, now I see what you're doing. But um... in tic-tac-toe, it is        // trivial to undo a move, so why not just make it on the existing        // board?        //if it found a win, take it        if(checkForWin2(compChip, tempBoard)) {          board = compChip; //makes the move          editStrings(i,compChip);          // You see the burden you have with this duplication - every time,          // you have to update the array AND call editStrings. Doh.          return; // obviously not needed.        }      }      }        //look ahead for a loss. Similar to looking ahead for a win,    //but i "look ahead" with the opponents chip.    for(int i=1; i<=9; i++)  {      // (snipped out loop body)      // Doesn't this part look rather familiar? o_O      // You need a function like "checkIfMoveWins(int location, char player)".    }    // etc.    //fill array with very small initial scores. Smaller than any position     //can evaluate to during the game.    for(int i=0; i<=9; i++) {      array = -1000;    }    // There are cleaner ways to express "very small value", but this is OK    // for now.    //evaluate each space on the board    for(int i=1; i<=9; i++) {      if(board == '\0') { //if space is free, evaluate it        // etc.          //add up ways computer can win.        //rows        if((tempBoard[1]=='\0'||tempBoard[1]==compChip) && (tempBoard[2]=='\0'||tempBoard[2]==compChip) && (tempBoard[3]=='\0'||tempBoard[3]==compChip))          r++;        // Doesn't this stuff look kind of like the checking you do for        // an outright win? Maybe you need a helper function like        // int fullLines(bool allowX, bool allowO, bool allowEmpty) {        //   // calculate number of lines containing only allowed symbols        // }        // Oh, adding 'r', 'c', and 'd' separately is of no real value.                //add up ways the human can win.        // And again, duplication. Duplication is bad mm'kay?        // And then we get:        score = compTotal - humanTotal;        //assign the score to array[]        array=score;      } else { //space is not free        //if the space is already taken, give it a super        //low score so the computer doens't move there.        array=-10000000;        // This is bad; it's again a 'magic number', and when you come back        // you are going to wonder why this isn't the same '-1000' value as        // you used before for initialization.        // Actually, why do you bother changing it at all? Just leave in the        // initialized 'low value'.      }    }//end for loop    //The index of the highest score in array[] will be the index    //of board[] that the computer will put it's chip on.    int bestScore = -100;    // And again, a different number for the "low value", why?    int bestPosition;    for(int i=1; i<=9; i++) {      if(array>bestScore) {        bestScore=array;        bestPosition=i;      }    }    // And the usual comment about placing pieces.  }};//end class Board// Actually, having a Board class isn't very useful at all for this kind of// game. I assume they want you to learn some OO programming or something.// If the justification is that thin, you really need to find a more competent// teacher who can give you a better problem. :Pint main() {  START:           //This label is used to reset the game.  // Same comment about while loops.  bool first;      //if human player goes first, then true  // Why not instead give this a name that reflects its meaning? e.g.  // "bool playerGoesFirst;" <-- now no comment is needed.  Board b;         //Board object  // Don't construct the board until you know who goes first (see comments  // in the Board class about improving the constructor).  cout << "PRESS CONTROL + C TO QUIT THE GAME AT ANY TIME\n";  // This is something the OS implements for you, and probably isn't worth  // advertising ;)  //prompt for chip type    cout << "Do you want to be X or O? ";    cin >> b.chip;    if(b.chip != 'x' && b.chip != 'o' && b.chip != 'X' && b.chip != 'O')    {        cout << "You must enter X or O \n\n";        goto START;     }  //the computer's chip is the opposite of the players.  if(b.chip=='x' || b.chip=='X')    b.compChip = 'O';  else    b.compChip = 'X';  // nonononononono. This is what constructors are for.  // This is logic specific to the board, so it goes in the Board class.  // (Again, assuming you bother with one after all.)  // Anything worth doing is worth doing right, and that goes for OO too.  //ask whether 1st or 2cd  cout << "Do you want to go first? Y/N ";  char yn;  // etc.  // I've never heard of a tic-tac-toe variant where the symbol did not  // determine who played first. Or did you want to support both "tic-tac-toe"  // and "noughts and crosses"? :)  //fill each position of b.board[] with null '\0'  for(int i=0; i<=9; i++) {    b.board = '\0';  }  // Ditto here. This goes in your constructor dammit.  //infinite loop exited with break statment when the game is over.  while(true) {    if(first) { //human player 1st      //human moves, then checks to see if you won or tied.      b.playerMove(b.chip);      if(b.checkForWin(b.chip)) {        b.drawBoard();        cout << "You won!!!\n";        break;      }      if(b.checkForTie()) {        b.drawBoard();        cout << "Tie game!!!\n";        break;      }      //computer moves, then checks if it won or tied.      b.compMove(b.compChip);      if(b.checkForWin(b.compChip)) {        b.drawBoard();        cout << "You lose!!!\n";        break;      }      if(b.checkForTie()) {        b.drawBoard();        cout << "Tie game!!!\n";        break;      }    } else { //human player moves 2cd      // GAH! Now you duplicated all the code, just in order to reverse the      // order of movement. Checking for a win/tie should be an integral part      // of making the move; you could have the move-making function return      // some value to indicate the result of the move (player win/comp win/      // tie/game still going).      // And guess what? You *didn't* actually reverse the order, so you      // introduced a *bug* by your copy-and-paste. See how evil duplication is!      // All you really need to do is this:      // if (!first), do a compMove() before the loop.      // Within the loop, do a playerMove(), which *calls compMove()* since      // play will always alternate, and returns the game status.      // If it's not "game still going", then output something and break.      // Also, you should set it up so that you always draw the board *after*      // a move, so that you don't have to draw it before and then also draw      // after in each game-ending case. That way you avoid having to write      // the drawBoard() call four times.    }  }//end infinite loop  return 0; // not needed}//end main()

Zahlman, I'm very impressed. I wish I had that much patience as you. Very good job. I hope Shining Blue will take the this as a constructive criticism.
it looks nice :P
We'll fight till we win, or we'll fight util we die !!!
Thanks for taking the time look at the code. I think i'll go back and re-write the entire thing using your suggestions and give it a graphical interface.

The one thing I disagree is about the 0 index of arrays. I never use the 0 index. What's the big deal if you waste 1 word of memory? Using 1 through 9 lets the index of the array directly coorelate the the 1-9 of the tic tac toe board. You don't have to do array[i-1] each time and can just do array. Other than wasting a word of memory, i don't see anything wrong with it.
This project is already turned in and graded, but i'll redo it anyway just to get better at designing the code.

This topic is closed to new replies.

Advertisement