• Advertisement
Sign in to follow this  

TTTwithpntrs.cpp debugging (Last one I swear :) )

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

If you intended to correct an error in the post then please contact us.

Recommended Posts

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

//function prototypes
void DisplayInstructions();
char* GetHumanTurn();
char* AskYesorNo(string question);
char* GetOpponentTurn(char* Human);
void DisplayBoard(const vector<char>& board);
char* winner(vector<char>* board);
int GetHumanMove(const vector<char>* board);
bool ItisLegal(const vector<char> board, int move);
int AskNumber(string question);
int GetComputerMove(const vector<char>* board, char* Computer);

//constant characters
char EMPTY = ' ';
char X = 'X';
char O = 'O';
char NO_ONE = 'N';
char TIE = 'T';

int main()
{
    //setting up the game
    int move;
    DisplayInstructions();
    vector<char> board(9, EMPTY);
    char* Human = GetHumanTurn();
    char* Computer = GetOpponentTurn(Human);
    cout << endl << "Displaying real board:" << endl << endl;
    DisplayBoard(board);
    char* turn = &X;
    
    //the game loop
    while(winner(&board) == &NO_ONE)
    {
                         if(*Human == *turn)
                         {
                                   move = GetHumanMove(&board);
                                   board[move] = *Human;
                                   }
                         else
                         {
                                   move = GetComputerMove(&board, Computer);
                                   board[move] = *Computer;
                                   }
                                   
                        DisplayBoard(board);
                        //now switch the turn symbol from X to O or O to X
                        GetOpponentTurn(turn);
                        }

    system("PAUSE");
    return 0;
}

void DisplayInstructions()
{
     cout << "Welcome to Tic Tac Toe! \nWhere you will test your might against man's greatest creation, the computer!\nBe prepared to go one on one with the processor as you battle for supremacy,\nand the entire human race!" << endl;
     cout << "The rules are simple, pick a number that corresponds with the position on the \nboard.\n\nExample board:\n\n";
     cout << "| 0 | - - | 1 | - - | 2 |\n\n| 3 | - - | 4 | - - | 5 |\n\n| 6 | - - | 7 | - - | 8 |" << endl;
}

char* GetHumanTurn()
{
      char* HumanTurn = AskYesorNo("\nDo you want to go first? (y or n)");
      if(*HumanTurn == X)
      {
                    return &X;
                    }
      else
      {
          return &O;
          }
}

char* AskYesorNo(string question)
{
      char answer;
      cout << endl;
      do
      {
      cout << question;
      cin >> answer;
      }
      while(answer != 'y' && answer != 'n');
      if(answer == 'y')
      {
                return &X;
                }
                if(answer == 'n')
                {
                          return &O;
                          }
}     

char* GetOpponentTurn(char* Human)
{
      if(*Human == X)
      {
                return &O;
                }
                else
                {
                    return &X;
                    }
}

void DisplayBoard(const vector<char>& board)
{
     cout << "| " << board[0] <<" | - - | " << board[1] << " | - - | " << board[2] << " |\n\n| " << board[3] <<" | - - | " << board[4] << " | - - | "<< board[5] << " |\n\n| " << board[6] << " | - - | " << board[7] << " | - - | " << board[8] << " |" << endl;
}     

char* winner(vector<char>* board)
{
      int WINNING_MOVES[8][3] = { {0, 1, 2},
                            {3, 4, 5},
                            {6, 7, 8},
                            {0, 3, 6},
                            {1, 4, 7},
                            {2, 5, 8},
                            {0, 4, 8},
                            {2, 4, 8} };
                            
      const int ALL_ROADS = 8;
      
      for(int i = 0; i < ALL_ROADS; ++i)
      {
              if( ((*board)[WINNING_MOVES[0]] != EMPTY) &&
              ((*board)[WINNING_MOVES[0]] == (*board)[WINNING_MOVES[1]]) &&
              ((*board)[WINNING_MOVES[1]] == (*board)[WINNING_MOVES[2]]) )
              {
                                             return &(*board)[WINNING_MOVES[0]];
                                             }
                                             }
              if(count(board->begin(), board->end(), EMPTY) == 0)
              {
                                       return &TIE;
                                       }
                                       
                                       return &NO_ONE;
}
              
int GetHumanMove(const vector<char>* board)
{
    
    int move = AskNumber("Where do you want to move? (0-8)");
    while(!ItisLegal(*board, move))
    {
                                   cout << endl << "That space is occupied. . . choose another." << endl;
                                   move = AskNumber("Where do you want to move? (0-8)");
}
return move;
}              
               
int AskNumber(string question)
{
     int move;
     do 
     {
     cout << endl << question;
     cin >> move;
     }
     while(move < 0 || move > 8);
     return move;
}

bool ItisLegal(const vector<char> board, int move)
{
     return (board[move] == EMPTY);
}

int GetComputerMove(const vector<char>* board, char* Computer)
{
         const int MAX_SPACES = 8;
         for(int move = 0; move < MAX_SPACES; ++move)
         {
                 board[move] = *Computer;
                 if(ItisLegal(*board, move))
                 {
                                      if(winner(&board) == &Computer)
                                      {
                                                        return move;
                                                        }
                                                        }
                                                        *board[move] = EMPTY;
                                                        }
         
         char* Human = GetOpponentTurn(Computer);
         
         for(int move = 0; move < MAX_SPACES; ++move)
         {
                 *board[move] = *Human;
                 if(ItisLegal(&board, move))
                 {
                                      if(winner(&board) == *Human)
                                      {
                                                        return move;
                                                        }
                                                        }
                                                        *board[move] = EMPTY;
                                                        }
                                                        
         vector<int>AllMoves;
         AllMoves.push_back(0);
         AllMoves.push_back(1);
         AllMoves.push_back(2);
         AllMoves.push_back(3);
         AllMoves.push_back(4);
         AllMoves.push_back(5);
         AllMoves.push_back(6);
         AllMoves.push_back(7);
         AllMoves.push_back(8);
         random_shuffle(AllMoves.begin(), AllMoves.end());
         
         for(int move = 0; move < MAX_SPACES; ++move)
         {
                 AllMoves[move] = *Computer;
                 if(ItisLegal(&board, move))
                 {
                                      return move;
                                      }
                                      AllMoves[move] = EMPTY;
                                      }
                                      
}

 E:\Documents and Settings\Kev\Desktop\Book Exercizes\tictactoewithpntrs.cpp In function `int GetComputerMove(const std::vector<char, std::allocator<char> >*, char*)': 
182 E:\Documents and Settings\Kev\Desktop\Book Exercizes\tictactoewithpntrs.cpp non-lvalue in assignment 
185 E:\Documents and Settings\Kev\Desktop\Book Exercizes\tictactoewithpntrs.cpp cannot convert `const std::vector<char, std::allocator<char> >**' to `std::vector<char, std::allocator<char> >*' for argument `1' to `char* winner(std::vector<char, std::allocator<char> >*)' 
190 E:\Documents and Settings\Kev\Desktop\Book Exercizes\tictactoewithpntrs.cpp no match for 'operator*' in '**((+(((unsigned int)move) * 12u)) + board)' 
197 E:\Documents and Settings\Kev\Desktop\Book Exercizes\tictactoewithpntrs.cpp no match for 'operator*' in '**((+(((unsigned int)move) * 12u)) + board)' 
198 E:\Documents and Settings\Kev\Desktop\Book Exercizes\tictactoewithpntrs.cpp conversion from `const std::vector<char, std::allocator<char> >**' to non-scalar type `std::vector<char, std::allocator<char> >' requested 
200 E:\Documents and Settings\Kev\Desktop\Book Exercizes\tictactoewithpntrs.cpp cannot convert `const std::vector<char, std::allocator<char> >**' to `std::vector<char, std::allocator<char> >*' for argument `1' to `char* winner(std::vector<char, std::allocator<char> >*)' 
205 E:\Documents and Settings\Kev\Desktop\Book Exercizes\tictactoewithpntrs.cpp no match for 'operator*' in '**((+(((unsigned int)move) * 12u)) + board)' 
223 E:\Documents and Settings\Kev\Desktop\Book Exercizes\tictactoewithpntrs.cpp conversion from `const std::vector<char, std::allocator<char> >**' to non-scalar type `std::vector<char, std::allocator<char> >' requested 

Not much to say here guys, just work your magic :). Thanks a ton. . .

Share this post


Link to post
Share on other sites
Advertisement
0a) Good god, fix your indentation style. I.e., when you put a closing brace, *unindent*!

0b) If your errors were all in the one function, we don't need to see the whole program. Just show the function, and possibly the functions that it directly calls. Posting the whole thing makes it quite harder for people, especially when the function in question is right at the end -.-

0c) Omg, you're doing it with pointers now, 1337! Ok, so once I explain what the problems are, I hope you'll come back to your senses and use them only where appropriate in future projects, rather than everywhere that it might be "educational" to do so. In particular, use references when you can and pointers when you have to.

OK, so here's the function, bracing fixed (to my particular style; plenty are valid, but I tend to just apply my formatting to everything I'm fixing, if I can get away with it - at work I can't :\ ). I also added comments matching up the compile errors to lines, and descriptive comments along the lines of what I would put in if I were coding it.


int GetComputerMove(const vector<char>* board, char* Computer) {
// First, search for a winning move and play it if present
const int MAX_SPACES = 8;
for (int move = 0; move < MAX_SPACES; ++move) {
board[move] = *Computer; // "non-lvalue in assignment"
if (ItisLegal(*board, move)) {
if (winner(&board) == &Computer) { // "cannot convert..."
return move;
}
}
*board[move] = EMPTY; // "no match for 'operator*'..."
}

// If no winning move is present, try to play something that would win
// for the opponent.
char* Human = GetOpponentTurn(Computer);
for (int move = 0; move < MAX_SPACES; ++move) {
*board[move] = *Human; // "no match for 'operator*'..."
if (ItisLegal(&board, move)) { // "conversion from ... to ... requested"
if (winner(&board) == *Human) { // "cannot convert..."
return move;
}
}
*board[move] = EMPTY; // "no match for 'operator*'..."
}

// If opponent can't win on the next turn either, pick a random move
vector<int> AllMoves;
AllMoves.push_back(0);
AllMoves.push_back(1);
AllMoves.push_back(2);
AllMoves.push_back(3);
AllMoves.push_back(4);
AllMoves.push_back(5);
AllMoves.push_back(6);
AllMoves.push_back(7);
AllMoves.push_back(8);
random_shuffle(AllMoves.begin(), AllMoves.end());

for (int move = 0; move < MAX_SPACES; ++move) {
AllMoves[move] = *Computer;
if (ItisLegal(&board, move)) { // "conversion from ... to ... requested"
return move;
}
AllMoves[move] = EMPTY;
}
}


1) You have definite logical errors and style problems that I will address once I get your compile errors out of the way.

2a) "non-lvalue in assignment": 'board' is a pointer, so it's assuming that your input pointer-to-vector is pointing at an array of vectors, and trying to grab a vector out of that array, and assign *to* this fictitious vector (rather than to a vector *element*).

2b) "no match for 'operator*' in '**((+(((unsigned int)move) * 12u)) + board)'":
'*board[move]' does NOT mean "dereference board and then subscript with move", as you'd like it to, but instead "subscript board with move and then dereference the result". Since the subscript would yield a vector, it complains because you can't dereference a vector. The rest of the error message is weird because of how GCC reports type errors: it has already parsed the expression into some internal format ("abstract syntax tree"), and then re-constitutes what it parsed when it writes the error message. So that weird expression actually is semantically equivalent to what you wrote: the '12u' is simply an unsigned int '12', which is the sizeof(std::vector<char>) on your implementation.

In both of these cases, what you want is actually (*board)[move].

3) "conversion from `const std::vector<char, std::allocator<char> >**' to non-scalar type `std::vector<char, std::allocator<char> >' requested": Take a close look at your three calls to ItisLegal(). The first call is not complained about, because it's valid. You have a pointer to vector of char, and the function expects a vector of char, so you dereference with the '*' operator. The second and third calls use the '&' operator, which *references*, yielding a pointer-to-pointer-to-vector of char. That's clearly not a valid substitute for a vector of char.

4) "cannot convert `const std::vector<char, std::allocator<char> >**' to `std::vector<char, std::allocator<char> >*' for argument `1' to `char* winner(std::vector<char, std::allocator<char> >*)'": Similarly, winner() takes a pointer-to-vector of char, and both times that you try to call it, you reference what was already a pointer-to-vector of char, yielding a pointer-to-pointer-to-vector of char. Those two types of pointers are not compatible. The reason this error message looks different from the other one is that the compiler checks conversion between two pointer types differently from how it reports the error of conversion between pointer and non-pointer.




OK, back to the style problems and logical errors. Notwithstanding the issue of passing pointers around in all kinds of places where it's hugely unnecessary and unhelpful:

5) The way ItisLegal() is used can't possibly work: The move is tried out and *then* the function is called to see if the square is empty. But the square was just now mindlessly filled! Meanwhile, if the move is a winner, you return from the function before you can undo it. What you need to do is check if the move is legal, then play it, then check the win status, and finally undo it.

6) There's all kinds of repetition in here of the move-trying process. That should be factored out into a separate function. I'll call it IsWinningMove().

7) That repetition actually led to *additional bugs*: When trying a random move, you "try" the move not on the board, but into the vector of possible moves! Also, since winner() returns a char*, you would want to compare it against Human and Computer directly (both also char*'s), not *Computer and &Human (again, a bizarre asymmetry here). I'm surprised the compiler doesn't at least warn about both of these. Of course, that compares the actual pointer values, which here is OK because there's only one version of the chars in question, so the pointers are going to be equal. But if you wanted to compare the pointed-at values, you could do it by something of the form "*foo == *bar".

8) You can't mark 'board' as const, because you *will* change the vector (even though you're going to revert the change).

9) There are 9 spaces on a tic-tac-toe board, not 8, so that's what you should have MAX_SPACES set to. Remember, you're looping with a strict less-than condition. Because it's NOT "the last space number", it should be called something else, like NUM_SPACES for example.

Also, you ought to give that constant a wider scope, and use it when you set the board up, too.

10) For that matter, using a std::vector to represent the board is a little bit sketchy, too - if only because you know what the size will be and it never changes. But I'd really have to redo the whole program then...


const int NUM_SPACES = 9;

// I'm going to use "normal" parameter passing here...
// You can see how the other function, with its parameter passing preserved,
// has to jump through a few hoops, while this code is nice and clean, except
// where it calls some other function ;)
bool isWinningMove(vector<char>& board, int move, char player) {
if (!ItisLegal(board, move)) { return false; }
board[move] = player;
bool result = winner(&board) == &player);
// Always undo the move
board[move] = EMPTY;
return result;
}

int GetComputerMove(const vector<char>* board, char* Computer) {
// First, search for a winning move and play it if present
for (int move = 0; move < NUM_SPACES; ++move) {
if (isWinningMove(*board, move, *Computer)) { return move; }
}

// If no winning move is present, try to play something that would win
// for the opponent.
char* Human = GetOpponentTurn(Computer);
for (int move = 0; move < NUM_SPACES; ++move) {
if (isWinningMove(*board, move, *Human)) { return move; }
}

// If opponent can't win on the next turn either, pick a random move.
// By the way, you don't *need* a standard library container to use
// standard library algorithms. But I'll ignore that for now ;)
vector<int> AllMoves(NUM_SPACES);
for (int i = 0; i < NUM_SPACES; ++i) {
AllMoves = i;
}
// When the 'Standard Template Library' was first written, it included an
// algorithm 'iota' that could be used to replace that loop. But it didn't
// make the cut for the Standard C++ Library :(

random_shuffle(AllMoves.begin(), AllMoves.end());

for (int move = 0; move < NUM_SPACES; ++move) {
// We don't want to test these moves out anyway; we just care if they're legal
if (ItisLegal(*board, move)) { return move; }
}
}

Share this post


Link to post
Share on other sites
Thanks for the long post :). I have one question, after reading through everything I decided to take the pointer to the vector out of the computermove function. This reduced it to only two compile errors. Both of them are when I'm testing the winner function to see if it is equal to a pointer to either human or computer. How do I fix it so it tests this properly?

Share this post


Link to post
Share on other sites
Ok, listen closely, because I'm only going to say this... probably hundreds of times to different people, but the more times you listen closely, the better the chance it has to sink in. ;)

Every variable has a type. The type of the thing you pass to a function needs to be compatible with the type that the function expects. The types of two things that you compare need to be compatible with each other.

The '*' (dereference) operator, applied to a variable, dereferences it. This reduces the "level of pointing" by 1: applied to a pointer-to-pointer-to-thing, it yields pointer-to-thing, and applied to pointer-to-thing, it yields the pointed-at thing. This naturally cannot be applied to non-pointer things.

The '&' (reference) operator, similarly, references a variable, increasing the "level of pointing". The result is a pointer to the thing that you applied '&' to. Pointers themselves are "things" too, so you can reference a pointer to get pointer-to-pointer.

So to avoid *syntactic* errors, you need to apply the right number of & or * to make things match up.

To avoid *semantic* (logical) errors, you need to compare things at the right level, as well.


char* foo;
char* bar;
// ...
if (foo == bar) {
// Do they point at the same character? I.e. do they have the same values,
// indicating the same location in memory where that character is?
}
if (*foo == *bar) {
// Do they point at a character which is the same? I.e. do the two pointed-at
// locations in memory hold the same value?
// The former implies the latter (because a memory location can't hold two
// different values at once), but not the other way around!
}


Now, when you have a pointer to something, it could also be interpreted as pointing to an array of things. This is where the evil comes in. Arrays and pointers are *not* "the same thing"; rather, array *indexing* is the same thing as pointer *arithmetic*. That is, "foo[bar]" is equivalent to "*(foo + bar)". Note that when you add to a pointer, the size of the type of pointed-at thing is *automatically accounted for*, so if you had an array of integers, and compared the numeric values of pointers to adjacent elements, they would be sizeof(int) apart; and if you took a pointer to an int and incremented it with ++, and compared the values before and after, there would be a change of sizeof(int) in the value.

Arrays and pointers are evil because:
- Given just a passed-in pointer to your function, there is NO WAY, FULL STOP, to determine:

  1. - Is the value a real pointer that was properly initialized, or some garbage that happened to be lying around in memory under that pointer variable?

  2. - Does the pointed-at memory belong to me?

  3. - Is the memory dynamically allocated, or on the stack? (Actually, you can sort of figure this one out, by comparing the address with a known on-the-stack address, but not very reliably.)

  4. - Is there an array of things being pointed at, or just a single thing? If it's an array, how many elements are there, and which element is pointed at?


- "At the slightest provocation", an array name "decays" into a pointer, losing the information about the array-ness.
- An N-dimensional, static array (i.e. all dimensions specified in compile-time constants) decays to a *single* pointer (not N-level pointer), because a single contiguous block is actually allocated for the array (the compiler automatically translates the multiple subscriptings into a combined index into that block). That means that given just the line "int bar = foo[0][0];", you can't tell if 'foo' is an int* or an int**!

- Lots of other reasons.

Share this post


Link to post
Share on other sites
Ok compile errors aside, theres some major run-time errors. The computer dosen't even make any moves if you decide to go first, and if you don't decide to go first,the board display just infinitly loops.

=[

Share this post


Link to post
Share on other sites
After some debugging, I KNOW the problem lies in two lines of code that get the computer's move and appyly it, it's like they are totally skipped. . .

Share this post


Link to post
Share on other sites
Did you have a version without all the unnecessary pointer usage working first?

Have you considered trying to implement less of the program at once?

Which lines are you talking about? What is your evidence for them being "skipped"?

Share this post


Link to post
Share on other sites
The book has a version without pointers, and the debugger freezes when it gets to the computer move function, plus i've ran the program numerous times and decided that all parts of the loop work besides the computer taking a move. I salso took out all the pointers to test it and got the same run-time errors.

Share this post


Link to post
Share on other sites
Just a bump in hopes that someone will catch the error tonight. =]

Share this post


Link to post
Share on other sites
Define "the debugger freezes". Can you step into the function? Can you step through the function? Can you step out of the function? Does it get stuck in a loop?

You can't just "take out" pointers. Design from scratch in a way that doesn't use the pointers (because you don't need them).

And again,

Quote:

Have you considered trying to implement less of the program at once?


Make a simpler program that does work, and then work it back towards being a complete TTT program.

Share this post


Link to post
Share on other sites
Ok after some redesign/coding I think the origonal problem occurs at the switching of the turns after the if statement in the main loop. When you declare that you want to go first, it gets your move, re-displays the board, but dosen't switch turns. Same thing when you say you don't want to go first, the computer keeps taking it's turn (I think. . .) only there is no pause to declare the end of the game (unlike with the human turn).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement