我有一个非常长的if语句来检查tic-tac-toe board的获胜条件:
if
((tttPositions[i][j][0] && tttPositions[i][j][1] && tttPositions[i][j][2] == 1)
|| (tttPositions[i][j][3] && tttPositions[i][j][4] && tttPositions[i][j][5] == 1)
|| (tttPositions[i][j][6] && tttPositions[i][j][7] && tttPositions[i][j][8] == 1)
|| (tttPositions[i][j][0] && tttPositions[i][j][3] && tttPositions[i][j][6] == 1)
|| (tttPositions[i][j][1] && tttPositions[i][j][4] && tttPositions[i][j][7] == 1)
|| (tttPositions[i][j][2] && tttPositions[i][j][5] && tttPositions[i][j][8] == 1)
|| (tttPositions[i][j][0] && tttPositions[i][j][4] && tttPositions[i][j][8] == 1)
|| (tttPositions[i][j][2] && tttPositions[i][j][4] && tttPositions[i][j][6] == 1))
{
if (j = 0)
cout << "x wins" << endl;
if (j = 1)
cout << "o wins" << endl;
}
这看起来真的很丑..是否有办法分别跟踪获胜条件,以大大减少if语句的长度?
我把多个tic tac toe board放到一个3D矢量中,每个盒子是2x9,第一行代表'x'位置,第二行代表'o'位置,所以像:
110000011
001110000
表示:
x x o
o o _
_ x x
答案 0 :(得分:4)
您可以创建一个接受3个整数的lambda,并检查它们是否在数组中设置。您还可以将tttPositions[i][j]
绑定到引用,只是为了使其输入不那么麻烦。
auto& pos = tttPositions[i][j];
auto check = [&pos](int a, int b, int c) {
return (pos[a] == 1) && (pos[b] == 1) && (pos[c] == 1);
};
// these three lines don't shorten the code, but
// they do make the if statememt more readable, imo
bool horizontal = check(0, 1, 2) || check(3, 4, 5) || check(6, 7, 8);
bool vertical = check(0, 3, 6) || check(1, 4, 7) || check(2, 5, 8);
bool diagonal = check(0, 4, 8) || check(2, 4, 6);
这样,您的if
变得更加简单(并且由于变量名称而自我记录):
if (horizontal || vertical || diagonal)
进一步的简化(同样,不是代码大小,而是可读性)将把所有这些都考虑在一个函数中。我不确定tttPositions[i][j]
是什么类型,但假设它是一个9个整数的数组,你可以这样做:
bool check_win_condition(int const (&pos)[9]) {
// all the stuff I did before, except instead of an if statement, just return
return horizontal || vertical || diagonal;
}
然后在你的其他功能中,if
就变成了这样:
if (check_win_condition(tttPositions[i][j]))
答案 1 :(得分:1)
您可能需要考虑在编译时创建数据结构以存储获胜行,然后在运行时遍历该数据结构以检查玩家位置是否包含获胜行而不是大量if
s。
我注意到你的情况有很多重复检查。例如,您正在检查tttPositions[i][j][4]
四次。成本可能微不足道,但减少它会很好。
您可以用来存储获胜线的一种数据结构是一种树。在树的顶部将是一个精心挑选的位置选择,包括在所有获胜线中,并且对于每个父母位置,作为孩子,您拥有包括那些位置的获胜线。
这棵树只需要有两个级别,我已经无法想象地称为Parent
和Child
并存储在一个平面阵列中。 Parent
存储了它的孩子的开始和结束索引。 Child
将其他两个位置存储在获胜行中:
struct Parent {
int pos;
int children_start;
int children_end;
};
using Child = std::pair<int, int>;
using Parents = std::array<Parent, 4>;
using Children = std::array<Child, 8>;
// Position indexes:
// 0 | 1 | 2
// ----------
// 3 | 4 | 5
// ----------
// 6 | 7 | 8
// centre top-left bottom-right
constexpr Parents parents = {{ {4,0,4}, {0,4,6}, {8,6,8} }};
// | | |
// +-----+--+--+-----+ +--+--+ +--+--+
// | | | | | | | |
constexpr Children children = {{ {0,8},{2,6},{1,7},{3,5},{1,2},{3,6},{2,5},{7,6} }};
添加几个begin
和end
函数,以便我们可以在Parent
上执行基于范围的for循环:
Children::const_iterator begin(const Parent& p) {
return children.begin() + p.children_start;
}
Children::const_iterator end(const Parent& p) {
return children.begin() + p.children_end;
}
有了这个,我们就可以编写一个函数来检查一个位置是否胜利:
using PlayerPositions = std::array<int, 9>;
bool isWin(const PlayerPositions& pos) {
for (auto& parent : parents) {
if (pos[parent.pos]) {
for (auto& child : parent) {
if (pos[child.first] && pos[child.second])
return true;
}
}
}
return false;
}
在这种情况下,这是否会使事情变得更简单是有争议的,但是更容易推广到更复杂的游戏规则。您甚至可以在运行时加载您的获胜位置。