【问题标题】:Nothing returned from 2D arrays for a simple tic-tac-toe board对于简单的井字游戏板,二维数组没有返回任何内容
【发布时间】:2014-04-08 03:13:05
【问题描述】:
public String tictactoe(String game)
{
    game = game.toUpperCase();
    char[][] board = new char[3][3];
    int loc = 0;
    for( char r = 0; r < board.length; r++ )
    {
        for( char  c = 0; c < board[r].length;  c++)
        {   board[r][c] = game.charAt(loc);
            loc++;
        }
    }
    if ((board[0][0] =='X' && board[0][1] =='X' && board[0][2] =='X') ||
        (board[1][0] =='X' && board[1][1] =='X' && board[1][2] =='X') ||
        (board[2][0] =='X' && board[2][1] =='X' && board[2][2] =='X'))
        return("Player 1 wins horizontally!");
    else if ((board[0][0] =='O' && board[0][1] =='O' && board[0][2] =='O') ||
             (board[1][0] =='O' && board[1][1] =='O' && board[1][2] =='O') ||
             (board[2][0] =='O' && board[2][1] =='O' && board[2][2] =='O'))
        return("Player 2 wins horizontally!");
    else if ((board[0][0] =='X' && board[1][0] =='X' && board[2][0] =='X') ||
             (board[0][1] =='X' && board[1][1] =='X' && board[2][1] =='X') ||
             (board[0][2] =='X' && board[1][2] =='X' && board[2][2] =='X'))
        return("PLayer 2 wins vertically!");
    else if ((board[0][0] =='O' && board[1][0] =='O' && board[2][0] =='O') ||
             (board[0][1] =='O' && board[1][1] =='O' && board[2][1] =='O') ||
             (board[0][2] =='O' && board[1][2] =='O' && board[2][2] =='O'))
        return("Player 2 wins vertically!");
    return "Tie!";
}

以上是我的这个方法的代码。它为井字游戏读取 9 个字母的字符串,然后将其一一放入 2D 数组中。不幸的是,我不得不使用这种方法,因为不幸的是,这是我们正在学习的内容,而且我一直不得不为此打扰我的老师......

if 语句检查每一行和每一列的获胜者(我意识到它不检查水平线)。我的问题是没有返回任何内容,即使它应该返回一个字符串。正如我所说,我在这里使用二维数组。目标是检查井字游戏并返回获胜者。

【问题讨论】:

  • 你有没有通过这个调试过?
  • 该方法确实return 一些东西。也许您将return ...System.out.println(...) 混淆了,并且错误地期望此方法可以将某些内容打印到控制台?
  • 这可能是逻辑语句中的错误。为了清楚起见,应该分解成更小的陈述。
  • @tana - 示例中没有返回任何类型“无”的代码路径...
  • 我用 game = "XOXOXOXOXX" 试过这个代码 -> 它返回 Tie!。我怎样才能重现您的问题!

标签: java arrays matrix


【解决方案1】:

考虑替换 this 的每个实例

if(board[0][0] =='X' && board[0][1] =='X' && board[0][2] =='X')  ||
   ...

使用此功能:

public boolean isRowFilledWith(int row_index, char x_or_o)  {
   return  (board[row_idx][0] == x_or_o  &&
            board[row_idx][1] == x_or_o  &&
            board[row_idx][2] == x_or_o);
}

现在,要查看最顶行是否包含所有 x-s,请使用以下命令调用它

if(isRowFilledWith(0, 'X')  ||
   ...

这将使您的代码更简洁、更易于阅读和更易于调试

【讨论】:

    【解决方案2】:

    尝试做:

    System.out.println(fooObject.tictactoe("XXXXXXOOO");
    

    在你的主要论点中(public static void main(String[] args)

    我怀疑你混淆了returnSystem.out.println。如果返回,控制台将不会打印任何内容。相反,返回是调用函数后向计算机抛出的内容。尝试打印调用函数的结果...这是返回到计算机的结果,并且只有在打印时才可见。您的代码中可能没有任何错误。

    【讨论】:

      【解决方案3】:

      首先,直接回答你的问题:

      您的功能运行良好,我在本地对其进行了测试。我尝试了水平和垂直,x 和 o。一定是你对调用者做的事情。

      这段代码最大的问题是清晰度。通过将逻辑分成组,您可以在清晰度方面做很多事情。

      可以通过制作特定于应用程序的对象来实现清晰度。

      如果您有一个对象来表示移动、游戏和标记...您可以大大简化您的代码,最重要的是使其易于阅读和调试。

      我继续实施井字游戏只是为了解释清楚我的意思。

      任何时候你开始有大量的 if else 链和块返回语句,你可能需要花费额外的时间来解决问题。

      这是完全可编译的,我认为如果你调试几次你会发现发现错误要容易得多。

      调整游戏也很容易...现在,我们有 2 名玩家和 3 x 3 游戏。

      但是,我们有额外的构造函数来调整游戏的大小,并为游戏玩家提供一些他们想要的井字游戏外观的选项。

      这样做的主要原因是为了清晰和调试。

      如果您有特定于问题的方法和特定于问题的对象,总是更容易找到问题。

      下面是一个完全可编译的井字游戏控制台应用程序。在目前的形式下,它会玩 100 场随机 3 x 3 游戏并打印所有 100 场游戏的结果。

      该应用程序还能够将您的字符串输入作为控制台输入并处理移动。

      请注意,我们仍在使用二维数组,但我们通过将数组封装在 Board 对象中来隐藏使用它们的复杂性。

      我们还提供了一个移动对象,以便我们可以存储移动以供以后播放。这在创建循环以玩许多游戏时很有用,因为有关移动的所有事实都包含在单个对象中。

      因为我们创建了三个单独的方法来验证移动输入。我们对验证的内容和验证的原因有了更清晰的解释。

      大多数调试器也会显示对象 toString 方法,因此通过将游戏显示嵌入到 toString() 中,我们可以在调试时查看板的外观。

      编写不需要大量 cmets 的干净代码的诀窍是尝试将任何复杂性分解到自己的单元中。

      稍后您可能会介绍更多有关工作单元的内容,但请记住,方法越简单,它的可重用性就越高。一个类的目的越单一,它就越好越清晰。

      我的例子:

      Mark.java
      -这个枚举是玩家用来表示他们移动的标记的表示。

      public enum Mark{
          X, Y;
      }
      

      井字游戏.java

      -这个类是你的主类 它负责收集用户输入、清理、处理并将输出显示给用户

      请注意,除了主要方法之外,我没有在其他任何地方进行游戏显示。这使所有显示问题都集中在一个位置。将代码分解为特定对象的好处是知道在出现问题时在哪里修复。

      package com.clinkworks.example;
      import java.util.ArrayList;
      import java.util.Collections;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      import com.clinkworks.example.Move;
      import com.clinkworks.example.Board;
      import com.clinkworks.example.Mark;
      
      public class TicTacToe {
      
          //this variable is simply used for output. Since we cant save CAT in the mark enum, we
          // use it to hold cats games.
          private static final String catsGameIdentity = "CAT";
      
          //this map contains the counts of wins for each symbol
          //it also keeps track of ties and stores it in the map as "CAT"
          //soo.. gamesToWinsMap.get("CAT") will produce the amount of ties.
          private static final Map<String, Integer> gamesToWinsMap = new HashMap<String, Integer>();
      
          public static void main(String[] args){
              String game = args[0].toUpperCase(); //better ways to do this and no validation here.. but it will work
              Board board = new Board();
      
              //holds our current place in the string passed in from teh console
              int currentStringLocation = 0;
      
              for(int row = 0; row < 3; row++){ //loop through the rows
                  for(int column = 0; column < 3; column++){ //for each row loop through the columns
                      if(gameOver(board)){ // we don't care about the rest of the input if the game was won
                          break;
                      }
      
                      //convert the symbol to a string for use in teh ValueOf function in the enum class
      
                      String symbol = "" + game.charAt(currentStringLocation); //better than String.valueOf imho
      
                      //allow spaces to represent un marked places
                      if(" ".equals(symbol)){
                          currentStringLocation++; //this accounts for spaces in our input
                          break; 
                      }
                      //convert the string to a Mark enum... over use of strings is a very very bad practice
                      Mark nextMarkToPlace = Mark.valueOf(symbol);
                      Move move = new Move(row, column, nextMarkToPlace); //we create a move object that encapsulates the complexity of placing the mark on the game board
                      board.play(move); //the game already knows how to play itself, just let it
                      currentStringLocation++; //increment the posision.
                  }
              }
      
              //since you may not have won the game, or gave a complete string for a cats game, 
              // lets at least display the board for debugging reasons.
              if(board.movesLeft() > 0){
                  System.out.println("Board isn't finished, but here is what it looks like basd on your input: ");
                  System.out.println(board);
              }
      
          }
      
          //call me main if you want to see what I do
          public static void main2(String[] args) {
      
              //lets play 100 games and see the wins and ties
              playGames(100);
      
              System.out.println("Number wins by X: " + gamesToWinsMap.get(Mark.X.name()));
              System.out.println("Number wins by O: " + gamesToWinsMap.get(Mark.O.name()));
              System.out.println("Number of ties: " + gamesToWinsMap.get(catsGameIdentity));
          }
      
          public static void playGames(int count) {
              //play a new game each iteration, in our example, count = 100;
              for (int i = 0; i < count; i++) {
                  playGame();
              }
          }
      
          public static void playGame() {
              //create a new game board. this initalizes our 2d array and lets the complexity of handling that
              // array be deligated to the board object.
              Board board = new Board();
      
              //we are going to generate a random list of moves. Heres where we are goign to store it
              List<Move> moves = new ArrayList<Move>();
      
              //we are creating moves for each space on the board.
              for (int row = 0; row < 3; row++) {
                  for (int col = 0; col < 3; col++) {
                      moves.add(new Move(row, col));
                  }
              }
      
              //randomize the move list
              Collections.shuffle(moves);
      
              //do each move
              for (Move move : moves) {
                  board.play(move);
      
                  if(gameOver(board)){
                      break;
                  }
              }
          }
      
          public static boolean gameOver(Board board){
              if (board.whoWon() != null) {
                  System.out.println("Player with the mark: " + board.whoWon() + " won the game!");
                  System.out.println(board);
      
                  Integer winCount = gamesToWinsMap.get(board.whoWon().name());
                  winCount = winCount == null ? 1 : winCount + 1;
      
                  gamesToWinsMap.put(board.whoWon().name(), winCount);
      
                  return true;
      
              } else if (board.movesLeft() == 0) {
                  System.out.println("It was a cats game!!");
                  System.out.println(board);
      
                  Integer catCount = gamesToWinsMap.get(catsGameIdentity);
                  catCount = catCount == null ? 1 : catCount + 1;
                  gamesToWinsMap.put(catsGameIdentity, catCount);
      
                  return true;
              }
      
              return false;
          }
      

      Move.java

      -这个类负责确保只有整数被传递给游戏板 并告诉游戏板谁在移动(可选)

          package com.clinkworks.example;
      
          import com.clinkworks.example.Mark;        
      
          public class Move {
              private int row;
              private int column;
              private Mark forcedMark;
      
              public Move(int row, int column) {
                  this.row = row;
                  this.column = column;
              }
      
              //the board already knows who should be next. only use this constructor to override
              // what symbol to put on the game board
              public Move(int row, int column, Mark markToPlace){
                  this.row = row;
                  this.column = column;
                  forcedMark = markToPlace;
              }
      
              public int getRow() {
                  return row;
              }
      
              public int getColumn() {
                  return column;
              }
      
              public Mark getMark(){
                  return forcedMark;
              }
          }
      

      Board.java

      -这是管理所有棋盘状态的地方,谁是下一个玩家,谁赢了,以及 有没有什么动作可以玩。它还确保只玩有效的动作。

          package com.clinkworks.example;
      
          import com.clinkworks.example.Mark;
          import com.clinkworks.example.Move;
      
          public class Board {
      
              private final int rowSize;
              private final int columnSize;
              private final Mark[][] gameBoard;
              private Mark currentMark;
              private Mark winningMark;
      
              /**
              * This constructor defaults the starting player to X with a 3 by 3
              * game.
              */
              public Board() {
                  gameBoard = new Mark[3][3];
                  currentMark = Mark.X; // X always goes first ;P
                  winningMark = null;
                  this.rowSize = 3;
                  this.columnSize = 3;
              }
      
              /**
              * This constructor defaults the starting player to X, and lets the
              * board size be adjusted
              */
              public Board(int rowSize, int columnSize) {
                  gameBoard = new Mark[rowSize][columnSize];
                  currentMark = Mark.X; // X always goes first ;P
                  winningMark = null;
                  this.rowSize = getRowSize();
                  this.columnSize = columnSize;
              }
      
              /**
              * this constructor allows the players to choose who goes first on a 3
              * by 3 board.
              * 
              * @param firstPlayer
              */
              public Board(Mark firstPlayer) {
                  gameBoard = new Mark[3][3];
                  currentMark = firstPlayer; // Let the player choose
                  winningMark = null;
                  rowSize = 3;
                  columnSize = 3;
              }
      
              /**
              * this constructor allows the players to choose who goes first and to
              * choose the size of the board.
              * 
              * @param firstPlayer
              */
              public Board(Mark firstPlayer, int rowSize, int columnSize) {
                  gameBoard = new Mark[getRowSize()][columnSize];
                  currentMark = firstPlayer; // Let the player choose
                  winningMark = null;
                  this.rowSize = rowSize;
                  this.columnSize = columnSize;
              }
      
              /**
              * 
              * @return the amount of empty spaces remaining on the game board, or if theres a winning player, zero.
              */
              public int movesLeft() {
      
                  if(whoWon() != null){
                      return 0;
                  }
      
                  int moveCount = 0;
                  for (int x = 0; x < getRowSize(); x++) {
                      for (int y = 0; y < getColumnSize(); y++) {
                          moveCount += getMarkAt(x, y) == null ? 1 : 0;
                      }
                  }
                  return moveCount;
              }
      
              /**
              * If someone won, this will return the winning player.
              * 
              * @return the winning player
              */
              public Mark whoWon() {
                  return winningMark;
              }
      
              /**
              * This move allows the next player to choose where to place their mark.
              * if a move is played without a player given, it will use the current player variable
              * 
              * as a side affect, the next player in rotation is chosen.
              * 
              * @param Move
              * @return if the game is over, play will return true, otherwise false.
              */
              public boolean play(Move move) {
      
                  if (!validMove(move)) {
                      // always fail early
                      throw new IllegalStateException("Cannot play " + currentMark + " at " + move.getRow() + ", " + move.getColumn() + "\n" + toString());
                  }
      
                  doMove(move);
      
                  boolean playerWon = isWinningMove(move);
      
                  if (playerWon) {
                      winningMark = currentMark;
                      return true;
                  }
      
                  togglePlayer();
      
                  boolean outOfMoves = movesLeft() <= 0;
      
                  return outOfMoves;
              }
      
              public Mark lastMarkPlayed() {
                  return currentMark;
              }
      
              public int getRowSize() {
                  return rowSize;
              }
      
              public int getColumnSize() {
                  return columnSize;
              }
      
              public Mark getCurrentPlayer() {
                  return currentMark;
              }
      
              public Mark getMarkAt(int row, int column) {
                  return gameBoard[row][column];
              }
      
              private void doMove(Move move) {
                  if(move.getMark() != null){
                      currentMark = move.getMark();
                  }
                  gameBoard[move.getRow()][move.getColumn()] = getCurrentPlayer();
              }
      
              private void togglePlayer() {
                  if (currentMark == Mark.X) {
                      currentMark = Mark.O;
                  } else {
                      currentMark = Mark.X;
                  }
              }
      
              /**
               * A valid move is a move where the row and the column are within boundries
               * and no move has been made that the location specified by the move.
               */
              private boolean validMove(Move move) {
                  boolean noMarkAtIndex = false;
                  boolean indexesAreOk = move.getRow() >= 0 || move.getRow() < getRowSize();
                  indexesAreOk = indexesAreOk && move.getColumn() >= 0 || move.getColumn() < getColumnSize();
                  if (indexesAreOk) {
                      noMarkAtIndex = getMarkAt(move.getRow(), move.getColumn()) == null;
                  }
                  return indexesAreOk && noMarkAtIndex;
              }
      
              private boolean isWinningMove(Move move) {
                  // since we check to see if the player won on each move
                  // we are safe to simply check the last move
                  return winsDown(move) || winsAcross(move) || winsDiagnally(move);
              }
      
              private boolean winsDown(Move move) {
                  boolean matchesColumn = true;
      
                  for (int i = 0; i < getColumnSize(); i++) {
                      Mark markOnCol = getMarkAt(move.getRow(), i);
                      if (markOnCol != getCurrentPlayer()) {
                          matchesColumn = false;
                          break;
                      }
                  }
      
                  return matchesColumn;
              }
      
              private boolean winsAcross(Move move) {
                  boolean matchesRow = true;
                  for (int i = 0; i < getRowSize(); i++) {
                      Mark markOnRow = getMarkAt(i, move.getColumn());
                      if (markOnRow != getCurrentPlayer()) {
                          matchesRow = false;
                          break;
                      }
                  }
                  return matchesRow;
              }
      
              private boolean winsDiagnally(Move move) {
                  // diagnals we only care about x and y being teh same...
                  // only perfect squares can have diagnals
                  // so we check (0,0)(1,1)(2,2) .. etc
                  boolean matchesDiagnal = false;
                  if (isOnDiagnal(move.getRow(), move.getColumn())) {
                      matchesDiagnal = true;
                      for (int i = 0; i < getRowSize(); i++) {
                          Mark markOnDiagnal = getMarkAt(i, i);
                          if (markOnDiagnal != getCurrentPlayer()) {
                              matchesDiagnal = false;
                              break;
                          }
                      }
                  }
      
                  return matchesDiagnal;
              }
      
              private boolean isOnDiagnal(int x, int y) {
                  if (boardIsAMagicSquare()) {
                      return x == y;
                  } else {
                      return false;
                  }
              }
      
              private boolean boardIsAMagicSquare() {
                  return getRowSize() == getColumnSize();
              }
      
              //prints out the board in a nice to view display
              public String toString() {
                  StringBuffer stringBuffer = new StringBuffer();
                  for(int y = 0; y < getColumnSize(); y++) {
                      for(int x = 0; x < getRowSize(); x++) {
                          Mark mark = getMarkAt(x, y);
                              String markToPrint = "";
                          if (mark == null) {
                              markToPrint = " ";
                          } else {
                              markToPrint = mark.name();
                          }
                          stringBuffer.append("|").append(markToPrint);
                      }
                      stringBuffer.append("|\n");
                  }
                  return stringBuffer.toString();
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-15
        相关资源
        最近更新 更多