【问题标题】:Why won't the loop stop iterating?为什么循环不会停止迭代?
【发布时间】:2014-03-25 21:13:34
【问题描述】:

我是一名 Java 初学者,正在为我的班级编写一个 gui tic-tac-toe 程序。 (没有玩家,只有电脑生成)。

我的程序中的一切都按预期工作,除了一件事;似乎我对checkWinner 的方法调用的放置位置不正确,因为 X 和 O 的分配总是完成。为什么一有赢家就不会结束循环?

它将根据方法调用返回正确的获胜者,但 for 循环将继续迭代并填充其余部分(因此有时看起来 x 和 o 都赢了,或者一个赢了两次)。我一直在发疯,认为这可能是我的 checkWinner 方法调用和 if 语句的位置。当我设置winner = true; 时不应该取消循环吗?我试过把它放在每个 for 循环的内部和外部,但没有运气:(

我已经在代码右侧标记了我认为是问题的区域//这里有什么问题?//。感谢您的任何意见! :)

  public void actionPerformed(ActionEvent e)
  {
    int total = 0, i = 0;
    boolean winner = false;


    //stop current game if a winner is found
    do{

      // Generate random # 0-1 for the labels and assign 
      // X for a 0 value and O for a 1 value

      for (int row = 0; row < gameboard.length; row++) //rows
      {
        for (int col = 0; col < gameboard[row].length; col++) //columns
        { 

          //Generate random number
          gameboard[row][col] = (int)(Math.random() * 2);  

          //Assign proper values
          if(gameboard[row][col] == 0)
          {
            labels[i].setText("X");
            gameboard[row][col] = 10; //this will help check for the winner
          }

          else if(gameboard[row][col] == 1)
          {
            labels[i].setText("O");   
            gameboard[row][col] = 100; //this will help check for winner
          }             


          /**Send the array a the method to find a winner
            The x's are counted as 10s
            The 0s are counted as 100s
            if any row, column or diag = 30, X wins
            if any row, column or diag = 300, Y wins
            else it will be a tie
            */

          total = checkWinner(gameboard);      **//Is this okay here??//**
          if(total == 30 || total == 300)        //
            winner = true;                //Shouldn't this cancel the do-while?


          i++; //next label

        }
      }//end for
    }while(!winner);//end while



    //DISPLAY WINNER
    if(total == 30)
      JOptionPane.showMessageDialog(null, "X is the Winner!");
    else if(total == 300)
      JOptionPane.showMessageDialog(null, "0 is the Winner!");
    else
      JOptionPane.showMessageDialog(null, "It was a tie!");
  }

【问题讨论】:

  • Sidenode:如果没有找到获胜者会怎样?游戏将重新启动(但未中止),因此您的“领带”选项永远不可能。
  • 尝试在将winner 标志设置为true 时向System.out 打印一条消息;确保它确实在发生。
  • 您不需要将total 初始化为零,因为它会被checkWinner 结果覆盖。但是,您需要将 i 初始化为零,并且您应该在 do 循环中在 for(row) 之前执行此操作。
  • @JasonC 我做了你所说的使用 JOption 并且发生的事情是一旦有人获胜它就会弹出,但是,在我按下确定后,相同的消息会继续弹出直到结束循环。
  • @dognose 如果他们是平手,它确实有效。如果我可以编辑我的代码帖子,如果有人想查看它,我将添加 checkWinner 方法。但是,我认为这与我当前的问题无关。

标签: java loops do-while tic-tac-toe


【解决方案1】:

最简单的方法是一次中断所有循环。 (即使有些人不喜欢这样)

outerwhile: while(true){

  // Generate random # 0-1 for the labels and assign 
  // X for a 0 value and O for a 1 value

  for (int row = 0; row < gameboard.length; row++) //rows
  {
    for (int col = 0; col < gameboard[row].length; col++) //columns
    { 

      total = checkWinner(gameboard);     
      if(total == 30 || total == 300)        
        break outerwhile;  //leave outer while, implicit canceling all inner fors.


      i++; //next label
    }
  }//end for
}//end while

但是,这将不允许“平局”选项,因为如果没有找到获胜者,则基本上会重新开始游戏。要让平局,你根本不需要外边,当找到赢家时,可以同时离开两个边:

  Boolean winner = false;
  outerfor: for (int row = 0; row < gameboard.length; row++) //rows
  {
    for (int col = 0; col < gameboard[row].length; col++) //columns
    { 

      total = checkWinner(gameboard);     
      if(total == 30 || total == 300){        
        winner = true;     
        break outerfor;  //leave outer for, implicit canceling inner for.

      }

      i++; //next label
    }
  }//end for

  if (winner){
    //winner
  }else{
     //tie.
  }

【讨论】:

  • 如果您发现标签 + break 不受欢迎,另一种方法是执行 while (!winner) 并将 &amp;&amp; !winner 添加到内部 for 循环条件。
  • 最好首先考虑一下这段代码在做什么,而不是让所有这些中断。原始代码看起来只是简单地更新标签和替换游戏板中的数字,这是一个不应该影响“获胜者”状态的动作。循环并更新标签会更有意义,替换 0->10 和 1->100,然后简单地检查一次获胜者。
  • 这两个我都试过了,但是我的输出永远不会通过前三个标签。不管我按了多少次新游戏按钮。
【解决方案2】:

首先,您的代码遍历一个板并生成 X 和 O 的随机标记。这会导致一些非常奇怪的板状态,总是逐行填充,并且 X 和 O 标记的数量可能不平衡.

恕我直言,您应该以相反的方式组织代码以填充类似于真正游戏的棋盘。我的意思是一系列 9 标记“XOXOXOXOX”分布在黑板上。

Labels labels 是一个九字符数组,初始化为 9 个空格。

public int doGame( Labels labels)
{
    labels = "         ";
    int itisXmove = true;              // player X or O turn
    for( int movesLeft = 9; movesLeft > 0; movesLeft --)
    {
        int position =          // 0 .. movesLeft-1
                (int) Math.floor(Math.random() * movesLeft);

        for( int pos = 0; pos < 9; pos ++)        // find position
            if( labels[ pos] == " ")              // unused pos?
                if( position-- == 0)              // countdown
                {
                    if( itisXmove)                // use the pos
                        labels[ pos] = "X";       // for current player
                    else
                        labels[ pos] = "O";
                    break;
                }

        int result = checkWinner( labels);        // who wins (non-zero)?
        if( result != 0)
            return result;

        itisXmove = ! itisXmove;                  // next turn
    }
    return 0;                                     // a tie
}

然后

public void actionPerformed(ActionEvent e)
{
    Labels labels;

    int result = doGame( labels);

    if( result == valueForX)
        JOptionPane.showMessageDialog(null, "X is the Winner!");
    else if( result == valueForO)
        JOptionPane.showMessageDialog(null, "O is the Winner!");
    else
        JOptionPane.showMessageDialog(null, "It's a tie!");

    for( int rowpos = 0; rowpos < 9; rowpos += 3)
    {
        for( int colpos = 0; colpos < 3; colpos ++)
            /* output (char)label[ rowpos + colpos] */;

        /* output (char)newline */;
    }
}

【讨论】:

  • 一些改进:一个玩家直到他/她走3步才能获胜,所以当movesLeft小于5时我们可以有条件地调用checkWinner。另外我们不需要检查整个棋盘——获胜的三个必须在当前移动完成的连续或列中或对角线上。当然,获胜者只能是做出当前动作的玩家。所以checkWinner 例程应该获取labelspos 参数,从labels[pos] 获取标签并将其与两、三或四对其他位置进行比较,具体取决于pos 值。然后返回该标签或零。
【解决方案3】:

我认为您应该更改循环条件并再添加一个布尔值。

您有一个“平局”条件,但目前您只检查获胜者。没有 checkWinner 代码的唯一解释是您每次都遇到平局。

所以...

boolean tie;
boolean winner;

do {
//your stuff
}
while(!(tie || winner))

编辑:我没有意识到您将 while 循环放在 for 循环之外,您需要跳出 for 循环才能检查 while 条件。

//stop current game if a winner is found
    do{

      for (int row = 0; row < gameboard.length; row++) //rows
      {
        for (int col = 0; col < gameboard[row].length; col++) //columns
        { 
            if(winner || tie)
                break;
        }//end for

        if(winner || tie)
            break;
      }//end for
    }while(!(winner || tie));//end while
//the rest of your stuff here

【讨论】:

    【解决方案4】:

    在两个 for 循环完成之前,您不会检查 winner 的值。在设置winner = true 后立即添加break,并添加一个

    if (winner)
    {
        break;
    }
    

    到您的外部 for 循环的开头或结尾。

    【讨论】:

    • 他正在检查,它在内部循环中。只是输出在一段时间之后。
    • @JasonC 它肯定会阻止for 循环在有赢家时终止。 winner 的值将在内部 for 循环内设置为 true,但在两个 for 循环完成之前不会检查 while 条件。
    • @dognose 他正在内部for 循环内设置winner 的值,但直到while 循环完成迭代后他才检查该值。正如所写,为了让while 循环完成一次迭代,两个for 循环都必须运行完成。
    • 如果函数checkWinner() 被正确实现,它应该在满足获胜条件后总是返回true。因此,while 应该在 fors 完成所有剩余字段之后退出 - 这不应该将游戏从“有赢家”变成“没有赢家”。
    • @dognose 是的,这就是问题所在,对吧?如果您阅读 OP,您会看到他希望 for 循环在有获胜者时立即终止。
    【解决方案5】:

    您的问题是您的 do/while 语句围绕 for 语句。因此,for 语句最终会在到达while 语句之前运行整个周期。解决此问题的解决方案是检查 for 语句中的获胜者并打破:

    //stop current game if a winner is found
    do {
    
        for (int row = 0; row < gameboard.length; row++) //rows
        {
            for (int col = 0; col < gameboard[row].length; col++) //columns
            { 
    
                // ... your other code ...
    
                total = checkWinner(gameboard);
                if(total == 30 || total == 300) {
                    winner = true;
                    break; // end current for-loop
                }
    
                i++; //next label
            }
    
            if (winner) break; // we have a winner so we want to kill the for-loop
        } //end for
    
    } while(!winner); //end while
    

    因此,您应该能够遍历两个 for 语句并在获胜者处中断。您的代码似乎也没有处理绑定案例,但我猜您已经知道了。

    【讨论】:

    • @dognose 这不是我们想要的行为。运营商希望游戏在有赢家后立即结束,而不是在所有字段都填满后结束。
    • @MikeB,不,我只是将第二个 break 语句放在错误的位置,因此它无法正常工作。 Dognose 是正确的,但我编辑了我的帖子。
    • 1) 您可以将!winner 添加到for 延续条件:for (int col = 0; col &lt; gameboard[row].length &amp;&amp; !winner; col++) 2) 您可以在第一个 for 和 break 前面添加标签:@987654330 @ 和 break WINNER; 3) 既然您知道如何中断标签,您甚至可以在不使用布尔值的情况下使 while 循环和 break 无限循环。
    • 我试过这个,但是我的输出永远不会超过井字游戏网格的第一行。
    猜你喜欢
    • 1970-01-01
    • 2019-01-10
    • 1970-01-01
    • 2010-11-23
    • 2022-07-14
    • 2021-09-07
    • 2013-10-17
    • 2016-01-19
    • 1970-01-01
    相关资源
    最近更新 更多