user5143812
user5143812

Reputation:

The right way to use != in a While loop ?...Or ..?

I'm just a beginner in C++ and while making a code to create the TicTacToe game I got stuck at the while statement that will push the game to keep going until winning conditions are fulfilled :

 while(((table[0][0]!='X')&&(table[1][1]!='X')&&(table[2][2]!='X')))

This is just the diagonal condition (Putting all the conditions will give you eyesore...). THE PROBLEM IS that this is not working even if the conditions are fulfilled (I'm sure because I use a cout at the end), however when I change && with || the condition work ! I thought maybe because of the != that affect everything ??

EDIT: Minimal Example (I removed the floating point !) :

#include <iostream>
using namespace std;
int main()
{
    int tailleX(3),tailleY(3); //The Size of the table.
    char table[tailleX][tailleY]; //TictacToe table.
    table[0][0]='N';
    table[0][1]='N';
    table[0][2]='N';
    table[1][0]='N';
    table[1][1]='N';          //Randomly filling the array to avoid an error
    table[1][2]='N';
    table[2][0]='N';
    table[2][1]='N';
    table[2][2]='N';
    int coorP1; //The Coordinate of the square (Exp: x=1 , y=2 will be 1.2)
    while(((table[0][0]!='X')&&(table[1][1]!='X')&&(table[2][2]!='X'))) //For the minimal example I made just the diagonal condition
    {
    cout<<"PLAYER1: Enter the coordination to score: (Exemple: 1, 2, 3..) "<<endl;
    cin>>coorP1;
    switch(coorP1) //Filling the square depending on the coordinates.//I used If because Switch does not work.
    {
        case 1:
    table[0][0]='X';
    break;
        case 2:
    table[0][1]='X';
    break;
        case 3:
    table[0][2]='X';
    break;
        case 4:
    table[1][0]='X';
    break;
        case 5:
    table[1][1]='X';
    break;
        case 6:
    table[1][2]='X';
    break;
        case 7:
    table[2][0]='X';
    break;
        case 8:
    table[2][1]='X';
    break;
        case 9:
    table[2][2]='X';
    break;
    }
    }
    cout<<"You won"<<endl;
    return 0;
}

Upvotes: 0

Views: 142

Answers (2)

JSF
JSF

Reputation: 5321

A critical part of learning to program is learning to avoid doing similar things over and over in your source code. You need to abstract the similar behaviors so they can share one chunk of code. So the computer does all that work, but when writing the source code, you don't do all that work.

Your attempt to build up a giant boolean expression of the game state is an extreme example of how not to program. It is a common beginner mistake (and far from the only example of that beginner mistake in your code). Fixing that giant boolean expression is possible, but it would be terribly counter productive along your path of learning to program. Instead you should take that as an example to learn how to combine and abstract work:

First understand the game concepts: A game state is one of wonX, wonY, draw, inProgress. You could define an enum for those possibilities. Each of the eight lines through the board has the same four possible states, where the game state is wonX or wonY if any line has that state, and the game state is inProgress is no line is wonX or wonY and some line is inProgress.

Because draw combines from individual rline up to board level in the opposite way that wonX or wonY does, the combination operation would be tricky at a high level and easier in the code that also determines line state.

So I suggest writing a function that takes the three values of one line as input and also takes game state accumulator as input, and returns an updated game state. In each round, you would start computing the game state as draw, then call the function for each of 8 lines to update it. If the line is a win for X or Y, then the state would unconditionally change to that. If the line is inProgress, the state would change to that only if the state was draw. If the line is draw, that doesn't change the state (one line in a draw state says nothing about the game state).

Good design would further abstract and combine several more aspects of your code, but the one that is the big problem from your failure to abstract and combine, is as I indicated the step that looks at one line and computes its impact on the state of the whole board.

In general you will find your most powerful tool for abstracting and combining work is to move that chunk of the work into a function (as I described above). Trying to do too much not split out into seperate functions is a major beginner mistake.

Upvotes: 1

Cornstalks
Cornstalks

Reputation: 38218

The problem here is your test condition. Your loop repeats if you enter 2, 3, 4, 6, 7, or 8. As soon as you enter 1, 5, or 9, the loop exits. If you enter 1, 5, or 9, then one of the diagonal values is set to 'X'. while loops while the condition is true. As soon as the condition evaluates to false, it exits. When you enter 1, 5, or 9, you cause the condition to be false.

Imagine, for a second, that table[0][0] is 'X', table[1][1] is 'N', and table[2][2] is 'N'. In other words, the board looks like this:

X | N | N
--+---+---
N | N | N
--+---+---
N | N | N

Then your test condition is:

table[0][0] != 'X' && table[1][1] != 'X' && table[2][2] != 'X'
^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^
false                 true                  true

If you logically AND these together (as you are with &&), this evaluates to false (which makes sense: false AND true should evaluate to false; only true AND true should evaluate to true).

So what should your test condition be?

What you really want is to do is loop only if the user doesn't have 3 in a row. In other words, check if the user has 3 in a row; if he does not have 3 in a row, then proceed.

We can construct that logical statement as:

// This checks if the user has 3 in a row
table[0][0] == 'X' && table[1][1] == 'X' && table[2][2] == 'X'

// We want to check if the user does NOT have 3 in a row,
// so we can negate the above with !
!(table[0][0] == 'X' && table[1][1] == 'X' && table[2][2] == 'X')

// With De Morgan's laws, we can simplify this to:
table[0][0] != 'X' || table[1][1] != 'X' || table[2][2] != 'X'

Thus, your looping condition should be one of (they're both equivalent; pick whichever one makes more sense to you):

  • !(table[0][0] == 'X' && table[1][1] == 'X' && table[2][2] == 'X')
    This checks if the user does not have 3 in a row
  • table[0][0] != 'X' || table[1][1] != 'X' || table[2][2] != 'X'
    This checks if the user does not have an 'X' any one of the needed positions. It logically follows that if the user is missing an 'X' in one of these positions, the user cannot have 3 in a row. This is just an application of De Morgan's laws to the previous logical statement.

Upvotes: 3

Related Questions