【问题标题】:C++ - Checking for 3 in a rowC++ - 连续检查 3 个
【发布时间】:2010-02-20 05:06:00
【问题描述】:

我有这个 3 x 3 的 char 数组,它应该代表一个井字棋盘,在此之前,我会使用一堆“if”语句来查看是否有 3 个连续。

... if ((board[0][0] == board[0][1]) && (board[0][1] == board[0][2])) { ... } ...

我意识到这是很多打字,而且很容易出错,那么有没有更好的方法来做到这一点?

【问题讨论】:

标签: c++ search


【解决方案1】:

您可以将其更改为仅从上次移动的位置开始检查。

//lr = lastMoveRow
//lc = lastMoveCol

// no need to check blank with last move known
if (board[0][lc] == board[1][lc] && board[0][lc] == board[2][lc] ||
    board[lr][0] == board[lr][1] && board[lr][0] == board[lr][2]){
      // Game over
}

// Check diagonals
if (board[1][1] != blank &&
   (board[0][0] == board[1][1] && board[0][0] == board[2][2] ||
    board[2][0] == board[1][1] && board[2][0] == board[0][2])){
    // Game over
}

或者 - 检查所有状态:

//Check horizontals and verticals at once
for (int i = 0; i < 3; ++i){
    // Check column and row at once
    if (board[0][i] != blank && board[0][i] == board[1][i] && board[0][i] == board[2][i] ||
        board[i][0] != blank && board[i][0] == board[i][1] && board[i][0] == board[i][2]){
      // Game over
    }
}

// Check diagonals
if (board[1][1] != blank &&
   (board[0][0] == board[1][1] && board[0][0] == board[2][2] ||
    board[2][0] == board[1][1] && board[2][0] == board[0][2])){
    // Game over
}

或者,如果您确实将其变成了一点点系统 - 保留单独的 X 和 O 板以便于更新。那么你只需要 9 位来表示 x,9 位来表示 O,而且你的获胜棋盘匹配要简单得多。 (在这种情况下,要查找空位,只需按位或 x 和 o 板)

// winning 9 bit boards
// int winningBoards[8]
000000111
000111000
111000000
001001001
010010010
100100100
100010001
001010100

//xBoard and yBoard can be ints
for (int i = 0; i < 8; ++i){
  if (xBoard & winningBoards[i] == winningBoards[i]){
    //Game over
  }
}

【讨论】:

  • 这个答案的唯一问题是它不处理正方形为空的情况。如果一行完全是空的,它会报告成功,即使游戏还没有结束。否则,+1。
【解决方案2】:

您可以删除括号,因为&amp;&amp; 的优先级低于==

if (board[0][0] == board[0][1] && board[0][1] == board[0][2])

您还可以定义一个内联函数(或)来分解相等性

inline bool are_equal(int a, int b, int c) {
  return a == b && b == c;
}
...
if (are_equal(board[0][0], board[0][1], board[0][2]))

请注意,a==b==c 不会返回您需要的内容。例如,0==0==1 在许多 C 派生语言中都是正确的。

【讨论】:

    【解决方案3】:

    你可以循环播放。例如,检查您可能执行的所有行:

    for(int i = 0; i < 3; i++){
        if((board[i][0]==board[i][1]) && (board[i][1]==board[i][2])){
            ....
        }
    }
    

    并为列做类似的事情。然后你只需要分别检查对角线。

    【讨论】:

    • 查看对 Tom 的回答的评论——我不完全确定链接 operator== 是允许的,就像链接 operator= 一样。
    • 但是 +1 for 建议循环。
    • 是的,我也不确定,但只是希望对方的那部分是正确的。我会把它改成我认为正确的。
    【解决方案4】:

    我不知道“更好”,但你可以把事情连续分解:

    //Set empty to whatever value you're using for an empty square.
    #define empty '\0'
    
    bool thereIsALine(char matrix[3][3])
    {
        char c;
        //Check all columns:
        for(int i = 0; i < 3; i++)
        {
            c = matrix[i][0];
            if (c == empty)
                break;
            if (c == matrix[i][1] && c == matrix[i][2])
                return true;
        }
        //Check all rows:
        for(int i = 0; i < 3; i++)
        {
            c = matrix[0][i];
            if (c == empty)
                break;
            if (c == matrix[1][i] && c == matrix[2][i])
                return true;
        }
        //Check diagonals
        c = matrix[1][1];
        if (c == empty) return false;
        if (c == matrix[0][2] && c == matrix[2][0] )
            return true;
        if (c == matrix[0][0] && c == matrix[2][2] )
            return true;
        return false;
    }
    

    【讨论】:

      【解决方案5】:

      改编自上周的Code Golf 比赛。请注意,沿棋盘矩阵的线性模式从给定索引开始,并沿相等的间隔前进。

      如果你用 1 表示玩家 1,用 2 表示玩家 2,那么它们是独立的位,你可以用按位与连续测试 3。

      char check_1row( char *board, int base, int stride ) {
          return board[ base ] & board[ base + stride ] & board[ base + 2 * stride ];
      }
      
      char check_win( char (&board)[3][3] ) {
          char winner = 0;
          winner |= check1row( board, 0, 4 ); // check NW/SE diagonal
          for ( int i = 0; i < 3; i ++ ) {
              winner |= check1row( board, i, 3 ); // check verticals
          }
          winner |= check1row( board, 2, 2 ); // check NE/SW diagonal
          for ( int i = 0; i < 9; i += 3 ) {
              winner |= check1row( board, i, 1 ); // check horizontals
          }
          return winner;
      }
      

      【讨论】:

      • 相当不错!但是,问题询问的是二维数组,而不是一维数组。这种方法如何推广?也许首先将二维数组解析为一维数组?
      • @Morlock:保证二维和一维数组是等效的。只需投射,无需解析。编辑答案以更好地适应。
      【解决方案6】:

      是的,你可以这样做

      if (board[0][0]==board[0][1]==board[0][2]) {...}
      

      甚至可以写一个函数

      inline boolean row_win(int row_num){
         return (board[row_num][0]==board[row_num][1]==board[row_num][2]);
      }
      

      虽然它有一个隐藏的一面,但如果 board[0][0],board[0][0],board[0][1] 等于 0,它将无法工作。

      另一种方法是编写一个 for 循环,但我想那是更多的输入。

      【讨论】:

      • 我希望能够减少打字。我仍然需要为每个有效的三元组做一个“如果”。
      • 我不完全确定这是否有效。假设第一个 == 首先被评估并评估为真,然后你会得到类似 1 == board[0][2] 的东西,这不是 OP 想要的。如果情况相反,那么你会得到board[0][0]==1,这也不是 OP 想要的。
      • 你是对的,用 int a,b,c = 50,50,50 进行了测试。 a==b 计算结果为 1,并且 1 50。
      【解决方案7】:

      您可以存储构成获胜行的索引,并使用单个循环:

      int win_row[][3] = {{0, 0, 0}, {1, 1, 1}, {2, 2, 2}, {0, 1, 2}, {0, 1, 2}, {0, 1, 2}, {0, 1, 2}, {0, 1, 2}};
      int win_col[][3] = {{0, 1, 2}, {0, 1, 2}, {0, 1, 2}, {0, 0, 0}, {1, 1, 1}, {2, 2, 2}, {0, 1, 2}, {2, 1, 0}};
      
      bool has_winner(char board[][3])
      {
          //'\0' means unoccupied
          for (int i = 0; i != 8; ++i) {
              char c = board[win_row[i][0]][win_col[i][0]];
              if (c && c == board[win_row[i][1]][win_col[i][1]] && c == board[win_row[i][2]][win_col[i][2]]) {
                  return true;
              }
          }
          return false;
      }
      

      我也支持 Jeff 将玩家的移动存储在单独的值中并使用按位运算的建议。

      【讨论】:

        【解决方案8】:

        这是一个完整的解决方案,以检查函数的形式来验证玩家(1 或 2,代表 X 和 O)是否获胜:

        // tic tac toe win checker
        
        #include<iostream>
        
        using namespace std;
        
        const int DIM = 3;
        
        int check (int t[DIM][DIM]) {
            // 0 is empty, 1 is X, 2 is O
            // return 1 or 2 if there is a win from either
            for (int row=0; row<DIM; row++) {
                if (t[row][0] == t[row][1] && t[row][1] == t[row][2]) {
                    if (t[row][0] != 0) return t[row][0];
                }
            }
            for (int col=0; col<DIM; col++) {
                if (t[0][col] == t[1][col] && t[0][col] == t[2][col]) {
                    if (t[0][col] != 0) return t[0][col];
                }
            }
            if (t[0][0] == t[1][1] && t[1][1] == t[2][2]) {
                if (t[0][0] != 0) return t[0][0];
            }
            if (t[0][2] == t[1][1] && t[1][1] == t[2][0] != 0) {
                if (t[0][2] != 0) return t[0][2];
            }
            return 1;
        }
        
        int main() {
            int ttt[DIM][DIM];
            ttt[1][0] = 2; // Initialyzing row no. 2 to values "2" to test
            ttt[1][1] = 2;
            ttt[1][2] = 2;
            if (check(ttt) != 0) {
                cout << "Player " << check(ttt) << " wins\n";
            }
            else {
                cout << "No winner yet\n";
            }
        }
        

        编辑:我更喜欢这种方法(返回获胜玩家的号码),而不是简单地验证是否有获胜者,因为它似乎更实用。

        希望对你有帮助!

        【讨论】:

        • 如果您只是要在最后对对角线检查进行硬编码,为什么还要定义一个DIM 宏呢?此外,这与我在您之前 39 分钟发布的答案基本相同。
        • 相当正确,只是此解决方案返回获胜者,而不是在有获胜者时返回“真”。我毫不掩饰这是最好的答案,自从我上周开始学习 C++ 以来更是如此。这可能就是为什么我没有发现您的解决方案方法的相似性,但它们确实彼此相似。干杯
        • 你为什么要把ttt设为全局?
        • 确实,我应该将 ttt 作为 main 的一部分。我更改了代码。谢谢
        猜你喜欢
        • 2011-06-23
        • 2020-04-08
        • 1970-01-01
        • 2013-05-10
        • 2016-08-26
        • 1970-01-01
        • 1970-01-01
        • 2016-08-18
        • 2023-02-04
        相关资源
        最近更新 更多