(Java) 使用二维数组的井字游戏

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39945881/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 21:34:49  来源:igfitidea点击:

(Java) Tic-Tac-Toe game using 2 dimensional Array

javaarrays

提问by Shepp

In class, our assignment is to create a two-dimensional array and create a tic-tac-toe game around it. I have everything done except displaying when the whole board is full and the game is a draw. I have tried a few things but I have not found the solution and I need some help... Here is my code:

在课堂上,我们的作业是创建一个二维数组并围绕它创建一个井字游戏。除了在整个棋盘已满并且游戏是平局时显示之外,我已经完成了所有工作。我已经尝试了一些东西,但我还没有找到解决方案,我需要一些帮助......这是我的代码:

import java.util.Scanner;

public class TicTacToe {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int row, column;
        char player = 'X';

        //create 2 dimensional array for tic tac toe board
        char[][] board = new char[3][3];
        char ch = '1';
        for (int i = 0; i < 3; i++){
            for (int j = 0; j < 3; j++) {
                board[i][j] = ch++;
            }
        }
        displayBoard(board);
        while(!winner(board) == true){

            //get input for row/column
            System.out.println("Enter a row and column (0, 1, or 2); for player " + player + ":");
            row = in.nextInt();
            column = in.nextInt();

            //occupied
            while (board[row][column] == 'X' || board[row][column] == 'O') {
                System.out.println("This spot is occupied. Please try again");
            }
            //place the X
            board[row][column] = player;
            displayBoard(board);

            if (winner(board)){
                System.out.println("Player " + player + " is the winner!");
            }

            //time to swap players after each go.
            if (player == 'O') {
                player = 'X';

            }
            else {
                player = 'O';
            }
            if (winner(board) == false) {
            System.out.println("The game is a draw. Please try again.");

        }

    }

    private static void displayBoard(char[][] board) {
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                if (j == board[i].length - 1) System.out.print(board[i][j]);
                else System.out.print( board[i][j] + " | ");
            }
            System.out.println();
        }


    }
    //method to determine whether there is an x or an o in the spot
    public static Boolean winner(char[][] board){
        for (int i = 0; i< board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (board[i][j] == 'O' || board[i][j] == 'X') {
                    return false;
                }
            }
        }

        return (board[0][0] == board [0][1] && board[0][0] == board [0][2]) ||
            (board[0][0] == board [1][1] && board[0][0] == board [2][2]) ||
            (board[0][0] == board [1][0] && board[0][0] == board [2][0]) ||
            (board[2][0] == board [2][1] && board[2][0] == board [2][2]) ||
            (board[2][0] == board [1][1] && board[0][0] == board [0][2]) ||
            (board[0][2] == board [1][2] && board[0][2] == board [2][2]) ||
            (board[0][1] == board [1][1] && board[0][1] == board [2][1]) ||
            (board[1][0] == board [1][1] && board[1][0] == board [1][2]);
    }
}

I want output saying that the board is full when it's full but I get nothing. This is the last line of my output and as you can see, my current strategy is not working as it continues to ask for input. -->

我想要输出说电路板已满时已满,但我什么也没得到。这是我输出的最后一行,正如您所看到的,我当前的策略不起作用,因为它继续要求输入。-->

Enter a row and column (0, 1, or 2); for player X: 2 0 X | O | X O | O | X X | X | O Enter a row and column (0, 1, or 2); for player O:

输入行和列(0、1 或 2);对于玩家 X:2 0 X | 哦| XO | 哦| X X | X | O 输入一行和一列(0、1 或 2);对于玩家 O:

采纳答案by Nordiii

First off:

首先:

 while (board[row][column] == 'X' || board[row][column] == 'O') {
            System.out.println("This spot is occupied. Please try again");
        }

This will create a infinite loop because rowand columnshouldn't change you should ask for new input!

这将创建一个无限循环,因为row并且column不应该改变你应该要求新的输入!

Also

public static Boolean winner(char[][] board){
    for (int i = 0; i< board.length; i++) {
        for (int j = 0; j < board[0].length; j++) {
            if (board[i][j] == 'O' || board[i][j] == 'X') {
                return false;
            }
        }
    }

As soon you hit 'O' or 'X' you will exit the Method with a false (no winner)

一旦你点击“O”或“X”,你就会以假(没有赢家)退出方法

What you probably want to check is if every spot is occupied

您可能想要检查的是是否每个位置都被占用

public static Boolean winner(char[][] board){
   //Boolean which is true until there is a empty spot
   boolean occupied = true;
   //loop and check if there is empty space or if its a draw
    for (int i = 0; i< board.length; i++) {
        for (int j = 0; j < board[0].length; j++) {
            //Check if spot is not 'O' or not 'X' => empty 
            if (board[i][j] != 'O' || board[i][j] != 'X') {
                occupied = false;
            }
        }
    }
    if(occupied)
        return false;
   //Check if someone won
    return (board[0][0] == board [0][1] && board[0][0] == board [0][2]) ||
        (board[0][0] == board [1][1] && board[0][0] == board [2][2]) ||
        (board[0][0] == board [1][0] && board[0][0] == board [2][0]) ||
        (board[2][0] == board [2][1] && board[2][0] == board [2][2]) ||
        (board[2][0] == board [1][1] && board[0][0] == board [0][2]) ||
        (board[0][2] == board [1][2] && board[0][2] == board [2][2]) ||
        (board[0][1] == board [1][1] && board[0][1] == board [2][1]) ||
        (board[1][0] == board [1][1] && board[1][0] == board [1][2]);
}

This would now check if there is a winner or its a tie

现在这将检​​查是否有赢家或平局

Occupied == true == tie == return false

Winner == return true

But you have three states:

但是你有三个状态:

  • Win
  • Tie
  • NotFinished
  • 领带
  • 没做完

With the changed Method you will NOT finish the game until you win.

使用更改后的方法,您将无法完成游戏,直到您获胜。

Reason:

原因:

 while(!winner(board) == true)

This makes the game run as long as there is NO winner (winner() will be false because everything is occupied or there is no winner)

这使得游戏只要没有赢家就会运行(winner() 将是假的,因为一切都被占用了或者没有赢家)

while(!false==true) => while(true) 

You could write a method similar to winner but it only checks if the board has empty spots:

你可以写一个类似于winner的方法,但它只检查棋盘上是否有空位:

public static Boolean hasEmptySpot(char[][] board){
   //loop and check if there is empty space 
    for (int i = 0; i< board.length; i++) {
        for (int j = 0; j < board[0].length; j++) {
            if (board[i][j] != 'O' && board[i][j] != 'X') {
                return true;
            }
        }
    }
    return false;
}

//New code 
while(hasEmptySpot(board) || !winner(board)){
          //Your code for the game here
     ....
    }

this would end the game when there is no empty spot left After you finished the game you can call winner(board) and it will return if you tied or won!

这将在没有空位时结束游戏 完成游戏后,您可以呼叫获胜者(棋盘),如果您平局或获胜,它将返回!

By creating hasEmptySpot()you could change your winner method to

通过创建,hasEmptySpot()您可以将获胜者方法更改为

public static Boolean winner(char[][] board){
    return (board[0][0] == board [0][1] && board[0][0] == board [0][2]) ||
        (board[0][0] == board [1][1] && board[0][0] == board [2][2]) ||
        (board[0][0] == board [1][0] && board[0][0] == board [2][0]) ||
        (board[2][0] == board [2][1] && board[2][0] == board [2][2]) ||
        (board[2][0] == board [1][1] && board[0][0] == board [0][2]) ||
        (board[0][2] == board [1][2] && board[0][2] == board [2][2]) ||
        (board[0][1] == board [1][1] && board[0][1] == board [2][1]) ||
        (board[1][0] == board [1][1] && board[1][0] == board [1][2]);
}

Why? Because you finished the game and you know there are only two possible outcomes Win or Tie.

为什么?因为您完成了游戏并且您知道只有赢或平两种可能的结果。

I hope this helped you a little bit.

我希望这对你有所帮助。

EDITHad a logic error myself!

编辑自己有一个逻辑错误!

First mistake:you still need to check if there is a winner while the game is running forgot that point!

第一个错误:您仍然需要在游戏运行时检查是否有获胜者忘记了这一点!

while(hasEmptySpot(board) || !winner(board)){
}

Now this will quit the game loop when there is a winner or no empty spots is left

现在,当有获胜者或没有空位时,这将退出游戏循环

Second mistake:In hasEmptySpot()

第二个错误:在 hasEmptySpot()

 if (board[i][j] != 'O' && board[i][j] != 'X') {
                return true;

not

不是

 if (board[i][j] != 'O' || board[i][j] != 'X') {
                return true;

Fixed it in the upper examples.

在上面的例子中修复了它。

I'm sorry for the inconvenience!

对不起给您带来不便!

回答by ccless1

You could try to incorporate a new method such as the following:

您可以尝试合并一种新方法,例如:

public Boolean boardFull()
{
    short count = 0;
    for(short i = 0; i < 3; i++){
        for(short j = 0; j < 3; j++){
            if(board[i][j] == ‘O' || board[i][j] == 'X'){
                count++;
            } else {
                continue;
            }
        }
    }

    if(count == 9){
        return true;
    } else {
        return false;
    }
}

You could use an if statement to see if it returns true and then print something out if it does.

您可以使用 if 语句来查看它是否返回 true,如果返回 true,则将其打印出来。

回答by XavCo7

Solution

解决方案

The code that's not working is your winner()method. It is always returning falseif there is at least one cell occupied. You could proceed based on the last part of Nordiii's answer.

不起作用的代码是您的winner()方法。false如果至少有一个单元格被占用,它总是返回。您可以根据Nordiii's answer的最后一部分继续。

Extra problems

额外问题

Cell-checking loop

细胞检查循环

Your code to check if a cell is occupied is going infinitely. You need to use an 'if' statement instead of a 'while' loop:

您检查单元格是否被占用的代码将无限运行。您需要使用“if”语句而不是“while”循环:

if(board[row][column] == 'X' || board[row][column] == 'O'){
    System.out.println("This spot is occupied. Please try again");
    continue;
}

Your old code got stuck always checking if 1 cell was occupied and it always returned true, which kept the loop alive and flooded your console. The continuestatement will exit the current iteration of your other 'while' loop and start a new iteration, thus asking for new input.

你的旧代码总是卡住,检查是否有 1 个单元被占用,它总是返回true,这使循环保持活动并淹没你的控制台。该continue语句将退出您的另一个“while”循环的当前迭代并开始新的迭代,从而要求新的输入。

Exceptions

例外

Man, that's a lot of uncaught exceptions! If I mess up my input, pow! The whole thing fails. Just put a tryblock for your input-checking code:

伙计,这是很多未捕获的异常!如果我搞砸了我的输入,pow!整件事都失败了。只需try为您的输入检查代码放置一个块:

try {
    row = in.nextInt();
    column = in.nextInt();

    // Attempt to place player (an ArrayOutOfBoundsException could be thrown)
    if(board[row][column] == 'X' || board[row][column] == 'O'){
        System.out.println("This spot is occupied. Please try again");
        continue;
    }

    board[row][column] = player;
} catch(Exception e){
    System.out.println("I'm sorry, I didn't get that.");
    continue;
}

This attempts to execute the code inside the trystatement, and if someone inputs something incorrect, the exception gets 'caught' and a new iteration is created. Genius!

这会尝试执行try语句中的代码,如果有人输入了不正确的内容,则会“捕获”异常并创建新的迭代。天才!

回答by Pablo Napolitano

The most efficient way to do this is to keep a running count of how many spaces have been filled previously and increment that count each time a space is occupied. The board can be considered full when that count reaches 9.

执行此操作的最有效方法是记录先前已填充的空间数量,并在每次占用空间时增加该计数。当该计数达到 9 时,可以认为该板已满。

If you're familiar with object-oriented programming, I think you'll find this easier to implement if you wrap your 2D array in a Board class.

如果您熟悉面向对象编程,我想如果您将二维数组包装在 Board 类中,您会发现这更容易实现。

Example:

例子:

public static class Board {
    private char[][] spaces = new char[3][3];
    private int numMoves = 0;

    public void makeMove(int row, int col, char player) {
        if (spaces[row][col] == 'X' || spaces[row][col] == 'O') {
            System.out.println("This spot is occupied. Please try again");
        } else {
            spaces[row][col] = player;
            numMoves++;
        }
    }

    public boolean isFull() {
        return numMoves == 9;
    }

    public boolean hasWinner() {
        ...
    }

    public void display() {
        ...
    }
}

回答by D.B.

Although there are already some great answers I'd like to post another solution that is more generic in its logic to determine the winner. Currently you've hard-coded your some of the possible winning scenarios when you could write more generic logic for this.

尽管已经有一些很好的答案,但我想发布另一个解决方案,该解决方案的逻辑更通用,以确定获胜者。目前,当您可以为此编写更通用的逻辑时,您已经对一些可能的获胜方案进行了硬编码。

As other answers have pointed out you want a method to check for unoccupied spaces in the board and this will tell you if there is a tie. I have implemented such a method in the code below along with the more generic winner logic.

正如其他答案所指出的那样,您需要一种方法来检查棋盘中是否有未占用的空间,这将告诉您是否存在平局。我在下面的代码中实现了这样的方法以及更通用的获胜者逻辑。

Note that some methods are public to make it easier to test, they do not necessarily have to remain public.

请注意,某些方法是公开的,以便于测试,它们不一定必须保持公开状态。

import java.util.Scanner;

public class TicTacToe {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int row, column;
        char player = 'X';

        //create 2 dimensional array for tic tac toe board
        char[][] board = new char[3][3];
        char ch = '1';
        for (int i = 0; i < 3; i++){
            for (int j = 0; j < 3; j++) {
                board[i][j] = ch++;
            }
        }
        displayBoard(board);
        while(!winner(board) == true){

            //get input for row/column
            System.out.println("Enter a row and column (0, 1, or 2); for player " + player + ":");
            row = in.nextInt();
            column = in.nextInt();

            //occupied
            while (board[row][column] == 'X' || board[row][column] == 'O') {
                System.out.println("This spot is occupied. Please try again");
            }
            //place the X
            board[row][column] = player;
            displayBoard(board);

            if (winner(board)){
                System.out.println("Player " + player + " is the winner!");
            }

            //time to swap players after each go.
            if (player == 'O') {
                player = 'X';

            }
            else {
                player = 'O';
            }
            if (winner(board) == false && !hasFreeSpace(board)) {
                System.out.println("The game is a draw. Please try again.");
            }
        }

        //Don't forget to close the scanner.
        in.close();

    }

    public static void displayBoard(char[][] board) {
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                if (j == board[i].length - 1) System.out.print(board[i][j]);
                else System.out.print( board[i][j] + " | ");
            }
            System.out.println();
        }
    }

    /**
     * Determines whether the board is completely occupied by X and O characters
     * @param board the board to search through
     * @return true if entire board is populated by X or O, false otherwise.
     */
    public static boolean hasFreeSpace(char[][] board){
        for (int i = 0; i< board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (board[i][j] != 'O' && board[i][j] != 'X') {
                    return true;
                }
            }
        }
        return false;
    }

    //method to determine whether there is a winner
    public static boolean winner(char[][] board){
        return isHorizontalWin(board) || isVerticalWin(board) || isDiagonalWin(board);
    }

    /**
     * Determines if there is a winner by checking each row for consecutive
     * matching tokens.
     * @return true if there is a winner horizontally, false otherwise.
     */
    private static boolean isHorizontalWin(char[][] board) {
        for(int row = 0; row < board.length; row++){
            if(isWin(board[row]))
                return true;
        }
        return false;
    }

    /**
     * Determines whether all of the buttons in the specified array have the 
     * same text and that the text is not empty string.
     * @param lineToProcess an array of buttons representing a line in the grid
     * @return true if all buttons in the array have the same non-empty text, false otherwise.
     */
    private static boolean isWin(char[] lineToProcess) {
        boolean foundWin = true;
        char prevChar = '-';
        for(char character: lineToProcess) {
            if(prevChar == '-')
                prevChar = character;
            if ('O' != character && 'X' != character) {
                foundWin = false;
                break;
            } else if (prevChar != character) {
                foundWin = false;
                break;
            }
        }
        return foundWin;
    }

    /**
     * Determines whether there is a winner by checking column for consecutive
     * matching tokens.
     * @return true if there is a vertical winner, false otherwise.
     */
    private static boolean isVerticalWin(char[][] board) {
        char[] column = null;
      //assuming all rows have same legnth (same number of cols in each row), use first row
        for(int col = 0; col < board[0].length; col++){
            column = new char[board[0].length]; 
            for(int row = 0; row < column.length; row++){
                column[row] = board[row][col];
            }
            if(isWin(column))
                return true;
        }
        return false;
    }

    /**
     * Determines if there is a winner by checking each diagonal for consecutive
     * matching tokens.
     * @return true if a diagonal winner exists, false otherwise.
     */
    private static boolean isDiagonalWin(char[][] board) {

        int row = 0, col = 0;
        int cols = board.length;
        int rows = board[0].length; //assuming all rows are equal length so just use the first one

        //Create a one-dimensional array to represent the diagonal. Use the lesser
        // of the rows or columns to set its size. If the grid is rectangular then
        // a diagonal will always be the size of the lesser of its two dimensions.
        int size = rows < cols ? rows : cols;
        char[] diagonal = new char[size];

        //Since we know the grid is a square we really could just check one of
        // these - either row or col, but I left both in here anyway.
        while (row < rows && col < cols) {
            diagonal[col] = board[row][col];

            row++;
            col++;
        }
        if (isWin(diagonal)) {
            return true;
        }


        row = rows - 1;
        col = 0;
        diagonal = new char[size];
        while (row >=0 && col < cols) {
            diagonal[col] = board[row][col];
            row--;
            col++;
        }
        return isWin(diagonal);

    }
}