对于简单的井字游戏板,2D阵列没有返回任何内容

时间:2014-04-08 03:13:05

标签: java arrays matrix

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语句检查获胜者的每一行和每列(我意识到它不检查水平线)。我的问题是没有被返回,即使它应该返回一个字符串。正如我所说,我 在这里使用2D数组。我们的目标是检查一个井字游戏并将其归为谁。

3 个答案:

答案 0 :(得分:0)

考虑替换此

的每个实例
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')  ||
   ...

这将使您的代码更简洁,更易于阅读并且更易于调试

答案 1 :(得分:0)

尝试做:

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

在主参数内(public static void main(String [] args)

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

答案 2 :(得分:0)

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

您的功能正常,我在本地进行了测试。我尝试了水平和垂直,同时使用x和o。它必须是你在调用者身上做的事情。

此代码的最大问题是清晰度。通过将逻辑分成几组,你可以做很多关于清晰度的事情。

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

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

我继续实施tic tac toe,只是为了解释清楚我的意思。

每当你开始拥有大量的if else链和阻止返回语句时,你可能需要花费额外的时间来解决这个问题。

这是完全可以兼容的,我认为如果您通过它调试几次,您会发现更容易发现错误。

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

但是,我们还有额外的构造器来调整游戏的大小,并为游戏玩家提供一些他们想要的东西的选项。

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

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

下面是一个完全可编辑的tic tac toe控制台应用程序。在目前的形式中,它可以随机播放100个3乘3的游戏并打印出所有100个游戏的结果。

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

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

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

因为我们创建了三个单独的方法来验证移动输入。我们对我们正在验证的内容以及我们为什么这样做有了更清晰的解释。

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

制作不需要大量注释的干净代码的技巧是尝试将任何复杂性分解为自己的单元。

您可能会在以后详细介绍工作单元,但请记住,方法越简单,它就越可重复使用。一个班级的目的越多,它就越好,越清晰。

我的例子:

<强> Mark.java
- 此枚举是玩家用来表示移动的标记的表示。

public enum Mark{
    X, Y;
}

<强> TicTacToe.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();
        }
    }
}