2

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:

1
  • 2
    There is an infinite loop when you check if the cell is occupied . Commented Oct 9, 2016 at 16:38

5 Answers 5

4

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 row and column shouldn't change you should ask for new input!

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)

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)

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

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

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

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.

EDIT Had 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()

 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!

Sign up to request clarification or add additional context in comments.

3 Comments

Hey there, I did what you said and altered my code based off of your advice, however it doesn't end the game when there are 3 in a row, and even when the board is full it continues to ask for input.
@Shepp huups i did a mistake will edit it right away!
That still didn't work for me, but I appreciate your help anyways!
1

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.

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.

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() {
        ...
    }
}

Comments

0

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.

3 Comments

That looks like a great solution. I will try it out, and I'll let you know how it goes!
How do I call the boardFull method from my main method?
I think you can do it the same way you have called your previous methods; place it below main and then from the main program just call it normally like boardFull()
0

Solution

The code that's not working is your winner() method. It is always returning false if there is at least one cell occupied. You could proceed based on the last part of 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(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 continue statement will exit the current iteration of your other 'while' loop and start a new iteration, thus asking for new input.

Exceptions

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

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 try statement, and if someone inputs something incorrect, the exception gets 'caught' and a new iteration is created. Genius!

2 Comments

Wow thanks for the detailed response. I am still confused however... So my next step should be to alter my "winner" method? I am a little confused by your solution. Do I use what you typed to help me solve this? Or do I use what Nordiii advised...
You would use what @Nordiii advised about deciding the winner and replace all the code I listed as an extra problem with the code I supplied.
0

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);

    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.