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

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;
}

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.

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.

Related

What if an if and else statement are true?

Hello I'm trying to code a tic-tac-toe game and wanted to create an AI that plays randomly, except when the human player is about to win. However, I've run into a small issue here. Suppose the index(?) of the 3x3 grid goes like this:
123
456
789
If a and b are filled by the opponent's shape, then I want to check if:
a) the two numbers are in a row/column/diagonal
then,
b) the winning position (that the AI will block) is empty or filled already
def preventOwin(start): #win conditions
#rows
if start[1] == start[2] == 'O':
a = 3
if checkfree(a) == True:
print(start.update({a:'X'}))
return True
elif start[1] == start[3] == 'O':
a = 2
if checkfree(a) == True:
print(start.update({a:'X'}))
return True
etc.
So I want the AI to check if there are two O's in a row (eg in position 1 and 2), and see if the third position is filled already. If no, then I just continue with my game by updating the 3rd position's dictionary value to O. If yes, then there's a problem because then I want to check the rest of the elif statements and see if they're true or not.
eg:
board is:
-|x|o
-|o|-
x|o|-
the human plays:
o|x|o
-|o|-
x|o|-
The AI sees 1 and 3 are O's, but 2 is filled. I want to check the rest of the conditions after this because, in this case, 1 and 5 are also O's and 9 is empty.
Is this question contradictory to the nature of if-else statements?

C++ The if statement is still true even when only one condition is met

I am creating a tic tac toe program in c++ and am almost finished with it, the issue I'm having is when the game needs to end in a tie. I created an if-statement to check when specific combinations are selected and it finds it true even when only one condition is met.
Here is the code...
//takes the users selections and sees if all spots are filled in without a match.
if((b1== "O" || "X") && (b2 == "O" || "X") && (b3 == "O" || "X")){
cout << "The game ended in a tie.";
// Flip = 3 ends the game in a tie.
flip = 3;
}
ex... the game ends in a tie when i change `b1` to and `"O"` or an `"X"` even though `b2` and `b3` are still empty.
The condition:
b1== "O" || "X"
evaluates as:
(b1=="O") || "X"
which is always true. You probably want:
(b1=="O" || b1=="X")

While loop not acting as expected in Dev C++

I'm trying to get a while loop to accept the condition of looping until the values 1 or 2 are entered using the code below
while((choice != 1) || (choice != 2))
{
cout << "\nEnter 1 to read a file, Enter 2 to enter your own values: ";
cin >> choice;
}
As best as I can remember this has worked for me previously so I'm not sure why it's not working now and I think it may be something to do with how I installed Dev C++ as per the screenshot linked here. I vaguely recall having to fix this issue on my old PC but can't remember how to fix it exactly. Any help with either explaining where I've gone wrong in the code or how to fix the Dev C++ error would be greatly appreciated.
The issue with your loop is that this condition:
(choice != 1) || (choice != 2)
is guaranteed to be true for every value. If choice is 1, the second clause of the disjunction will be true; if it's 2, the first clause will be true; if it's any other number both will be true. If any of the clauses are true in a disjunction, the entire condition is true. This means your condition will always be true.
What you need is a conjunction of the clauses:
(choice != 1) && (choice != 2)
// ^^
With this the loop condition will only be true when both clauses are true, and this requires choice to be something other than 1 or 2.
If choice is equal to 1, one of your conditions is false but the other is still true. It is similar when choice is 2. Change your "OR" statement to an "AND" and it should only loop when the user enters something other than one or two.

C++ Do - While loop until a string meets certain criteria

I'm asking the user for an input, but I want the question to stay on screen until the input meets one of the allowed inputs. Here's my code
string input = "";
string departure = "";
cout << "Please enter an airport code: ";
do
{
getline(cin,input);
stringstream(input) >> departure;
} while(departure.compare("MAN") != 0 || departure.compare("EMA") != 0 || departure.compare("LHR") != 0 );
}
I want it to loop until the user enters MAN or EMA or LHR; also if they are lowercase I would like for it to be accepted aswell.
Every time I run this, even if I enter a correct input, it just keeps taking words in and doesn't do anything else.
The condition
departure.compare("MAN") != 0 || departure.compare("EMA") != 0 || departure.compare("LHR") != 0
is always true, regardless of what departure is.
compare returns 0 on equality. So what you're basically telling the compiler is
Run the loop while departure is different than "MAN" OR different than "EMA" OR different than "LHR".
You need && instead of || in your condition.
This condition always returns true since it can't not be all 3 at once.
The && will return false as soon as the input is one of the 3 accepted.
Consider using boost::to_upper to convert the input into upper case before you perform the comparison in the while(...) statment. This will resolve the lowercase/uppercase issue.
http://www.boost.org/doc/libs/1_41_0/doc/html/boost/algorithm/to_upper.html
Also, when dealing with C++ strings, I recommend you simply do
departure == "MAN" || departure == "EMA" || departure == "LHR"
You don't need to do string.compare in C++, unlike some other languages (for example Java), as the == operator is overloaded to compare the /content/ of the string, rather than the string object itself.
Also somebody else beat me to it about the compare method returning 0 when equal.
First Your conditional for the while loop is incorrect. Right now it reads, while departure is not 'MAN' or is not "EMA" or is not "LHR", continue looping. Because departure cannot be all three of them simultaneously, the loop never ends. I would suggest replacing your OR's (||) with AND's (&&)
As well, each execution of the loop you need to clear the value in departure, otherwise the previously entered lines persist and your comparison will fail even when a correct airport code is entered.
our main problem is that the string is being compared incorrectly. Let's say we type in "MAN".
The departure.comare("MAN") != 0 will be true if the string is not "MAN". Fine, we typed in "MAN", so it's false. Now we OR that with departure.compare("EMA") != 0 - which is true, because "MAN" is not equal to "EMA". So you need to combhine your condition with &&, not ||.
To fix for "owercase", there are two choices. Either convert the input string to uppercase, or compare with all different combinations of lower and upper case (Man, MaN, mAn, etc) - the latter gets very tedious very quickly.
Have a look at this one for some options of comparing strings in a case-insensitive way:
Case insensitive string comparison C++

If/else if not executing even though it appears conditions are met inside of a while loop

Pretty much what the title says. I'm to the last bit of this lottery numbers assignment, and I'm not sure why the second set of if/else if statements aren't displaying when I debug. I know that if/else statements are mutually exclusive - but shouldn't an if and then another if both test?
Here's the code.
count=0;
while(count<5)
{
if(lottery[count] == user[count])
{
lotto = lottery[count];
cout<<"So, you matched numbers with "<<lotto <<".\n";
tally++;
}
if(tally==5 && count == 5)
{
cout<<"Congratulations, you're the grand prize winner!";
}
else if(tally < 5 && count == 5)
{
cout<<"A total of "<<tally<<" of your lottery picks matched.";
}
else if(tally == 0 && count == 5)
{
cout<<"Caution. The following comment is an inside joke. Read at your own risk.";
cout<<"Bet you feel like a total loser huh? Nothing matched.";
}
count++;
}
I know I probably ought to have replaced the while loop with a for loop for simplicity's sake but i'm more comfortable with the while.
count will never be 5 when the if blocks are executed.
As soon as it becomes 5, the condition fails and the loop stops.
count will never be equal to 5 inside the while loop at the point where your if-else conditions require it. If you had incremented count before the if-else then it would have been possible to satisfy one of the conditions (depending on the value of tally).