在 While 循环中使用 != 的正确方法?...或者 ..?
The right way to use != in a While loop ?...Or ..?
我只是 C++ 的初学者,在编写创建 TicTacToe 游戏的代码时,我遇到了 while 语句,该语句将推动游戏继续进行,直到满足获胜条件:
while(((table[0][0]!='X')&&(table[1][1]!='X')&&(table[2][2]!='X')))
这只是对角线条件(把所有条件都放上去会碍眼...)。
问题是,即使满足条件,这也不起作用(我确定,因为我最后使用了 cout),但是当我用 || 更改 && 时条件有效!
我想可能是因为 != 影响了一切 ??
编辑:最小示例(我删除了浮点数!):
#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;
}
这里的问题是你的测试条件。如果您输入 2、3、4、6、7 或 8,您的循环就会重复。只要您输入 1、5 或 9,循环就会退出。如果您输入 1、5 或 9,则其中一个对角线值设置为 'X'
。 while
在条件为真时循环。一旦条件评估为假,它就会退出。输入 1、5 或 9 时,条件为假。
想象一下,table[0][0]
是 'X'
,table[1][1]
是 'N'
,table[2][2]
是 'N'
。换句话说,棋盘是这样的:
X | N | N
--+---+---
N | N | N
--+---+---
N | N | N
那么你的测试条件是:
table[0][0] != 'X' && table[1][1] != 'X' && table[2][2] != 'X'
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
false true true
如果你逻辑上将它们放在一起(就像你使用 &&
一样),这将计算为 false
(这是有道理的:false AND true
应该计算为 false
;只有 true AND true
应该评估为 true
).
那么你的测试条件应该是什么?
您真正想要做的是仅当用户没有连续 3 个时才循环。换句话说,检查用户是否连续有 3 个;如果他连续没有3个,那么继续。
我们可以将逻辑语句构造为:
// 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'
因此,您的循环条件应该是以下之一(它们是等价的;选择对您更有意义的一个):
!(table[0][0] == 'X' && table[1][1] == 'X' && table[2][2] == 'X')
这将检查用户是否连续没有 3
table[0][0] != 'X' || table[1][1] != 'X' || table[2][2] != 'X'
这将检查用户是否没有 'X'
任何一个所需的位置。从逻辑上讲,如果用户在这些位置之一缺少 'X'
,则用户不能连续 3 个。这只是De Morgan's laws对前面逻辑语句的应用。
学习编程的一个关键部分是学习避免在源代码中反复做类似的事情。您需要抽象出相似的行为,以便它们可以共享一块代码。所以计算机会做所有这些工作,但是在编写源代码时,你不会做所有这些工作。
你试图建立一个巨大的游戏状态布尔表达式是一个不编程的极端例子。这是一个常见的初学者错误(并且远不是代码中初学者错误的唯一示例)。修复那个巨大的布尔表达式是可能的,但在你学习编程的过程中,这会适得其反。相反,您应该以此为例来学习如何组合和抽象工作:
首先了解游戏概念:游戏状态是wonX、wonY、draw、inProgress之一。您可以为这些可能性定义一个枚举。棋盘上的八行中的每一行都有相同的四种可能状态,其中如果任何行具有该状态,则游戏状态为 wonX 或 wonY,并且游戏状态为 inProgress 是没有行 wonX 或 wonY 并且某些行正在进行中。
因为 draw 以与 wonX 或 wonY 相反的方式从单个 rline 组合到棋盘级别,组合操作在较高级别会很棘手,而在同样确定线状态的代码中更容易。
所以我建议写一个函数,将一行的三个值作为输入,也将游戏状态累加器作为输入,returns更新的游戏状态。在每一轮中,您将开始计算游戏状态为平局,然后为 8 行中的每一行调用函数来更新它。如果这条线是 X 或 Y 的胜利,那么状态将无条件地更改为那个。如果线是 inProgress,只有在绘制状态时,状态才会更改为该状态。如果线是平局,则不会改变状态(处于平局状态的一条线不会说明游戏状态)。
良好的设计会进一步抽象和组合代码的更多方面,但由于未能抽象和组合而导致的大问题是,正如我指出的那样,查看一行并计算其影响关于整板的状态。
一般来说,您会发现最强大的抽象和组合工作的工具是将那部分工作转移到一个函数中(如上所述)。试图做太多而不拆分成单独的函数是初学者的一个主要错误。
我只是 C++ 的初学者,在编写创建 TicTacToe 游戏的代码时,我遇到了 while 语句,该语句将推动游戏继续进行,直到满足获胜条件:
while(((table[0][0]!='X')&&(table[1][1]!='X')&&(table[2][2]!='X')))
这只是对角线条件(把所有条件都放上去会碍眼...)。 问题是,即使满足条件,这也不起作用(我确定,因为我最后使用了 cout),但是当我用 || 更改 && 时条件有效! 我想可能是因为 != 影响了一切 ??
编辑:最小示例(我删除了浮点数!):
#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;
}
这里的问题是你的测试条件。如果您输入 2、3、4、6、7 或 8,您的循环就会重复。只要您输入 1、5 或 9,循环就会退出。如果您输入 1、5 或 9,则其中一个对角线值设置为 'X'
。 while
在条件为真时循环。一旦条件评估为假,它就会退出。输入 1、5 或 9 时,条件为假。
想象一下,table[0][0]
是 'X'
,table[1][1]
是 'N'
,table[2][2]
是 'N'
。换句话说,棋盘是这样的:
X | N | N
--+---+---
N | N | N
--+---+---
N | N | N
那么你的测试条件是:
table[0][0] != 'X' && table[1][1] != 'X' && table[2][2] != 'X'
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
false true true
如果你逻辑上将它们放在一起(就像你使用 &&
一样),这将计算为 false
(这是有道理的:false AND true
应该计算为 false
;只有 true AND true
应该评估为 true
).
那么你的测试条件应该是什么?
您真正想要做的是仅当用户没有连续 3 个时才循环。换句话说,检查用户是否连续有 3 个;如果他连续没有3个,那么继续。
我们可以将逻辑语句构造为:
// 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'
因此,您的循环条件应该是以下之一(它们是等价的;选择对您更有意义的一个):
!(table[0][0] == 'X' && table[1][1] == 'X' && table[2][2] == 'X')
这将检查用户是否连续没有 3table[0][0] != 'X' || table[1][1] != 'X' || table[2][2] != 'X'
这将检查用户是否没有'X'
任何一个所需的位置。从逻辑上讲,如果用户在这些位置之一缺少'X'
,则用户不能连续 3 个。这只是De Morgan's laws对前面逻辑语句的应用。
学习编程的一个关键部分是学习避免在源代码中反复做类似的事情。您需要抽象出相似的行为,以便它们可以共享一块代码。所以计算机会做所有这些工作,但是在编写源代码时,你不会做所有这些工作。
你试图建立一个巨大的游戏状态布尔表达式是一个不编程的极端例子。这是一个常见的初学者错误(并且远不是代码中初学者错误的唯一示例)。修复那个巨大的布尔表达式是可能的,但在你学习编程的过程中,这会适得其反。相反,您应该以此为例来学习如何组合和抽象工作:
首先了解游戏概念:游戏状态是wonX、wonY、draw、inProgress之一。您可以为这些可能性定义一个枚举。棋盘上的八行中的每一行都有相同的四种可能状态,其中如果任何行具有该状态,则游戏状态为 wonX 或 wonY,并且游戏状态为 inProgress 是没有行 wonX 或 wonY 并且某些行正在进行中。
因为 draw 以与 wonX 或 wonY 相反的方式从单个 rline 组合到棋盘级别,组合操作在较高级别会很棘手,而在同样确定线状态的代码中更容易。
所以我建议写一个函数,将一行的三个值作为输入,也将游戏状态累加器作为输入,returns更新的游戏状态。在每一轮中,您将开始计算游戏状态为平局,然后为 8 行中的每一行调用函数来更新它。如果这条线是 X 或 Y 的胜利,那么状态将无条件地更改为那个。如果线是 inProgress,只有在绘制状态时,状态才会更改为该状态。如果线是平局,则不会改变状态(处于平局状态的一条线不会说明游戏状态)。
良好的设计会进一步抽象和组合代码的更多方面,但由于未能抽象和组合而导致的大问题是,正如我指出的那样,查看一行并计算其影响关于整板的状态。
一般来说,您会发现最强大的抽象和组合工作的工具是将那部分工作转移到一个函数中(如上所述)。试图做太多而不拆分成单独的函数是初学者的一个主要错误。