Определение победителя в игре Крестики Нолики

Пытаюсь написать программу Крестики Нолики, но есть проблема с финальным этапом - определением победителя.

Вместо того чтобы прервать игру после определения победителя, моя программа даёт пользователю возможность ввести лишний нолик или крестик. В чем моя ошибка?

В инпуте цифры от 1 включительно до 3 включительно через пробел. Пример: 2 3, 1 1.

Вот код:

package tictactoe;

import java.util.Arrays;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class UserTurn {
    static String coordinates1;
    static String coordinates2;
    static int coordRow;
    static int coordCol;
    static int countX;
    static int countO;
    static char symbolX = 'X';
    static char symbolO = 'O';
    static String str2;
    static String grid;
    static int[][] combosToWin = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8},  //Записываем в массив все возможные комбинации для выигрыша
            {0, 4, 8}, {2, 4, 6},
            {0, 3, 6}, {1, 4, 7}, {2, 5, 8}};

    public static void main(String[] args) {
        printStartGrid(); // вывод пустой сетки
        Character[][] ticTacToeGrid = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}};   // заполняющийся массив
        boolean has3X = false;
        boolean has3O = false;
        //boolean draw = false;
        while (!has3X || !has3O) {             //пока не будет 3 крестика либо 3 нолика в выигрышной комбинации, не выходить из цикла
            userTurn(ticTacToeGrid);           // первый ход
            for (int i = 0; i < 8; i++) {
                 // определение победителя
                if (grid.charAt(combosToWin[i][0]) == symbolX && grid.charAt(combosToWin[i][1]) == symbolX && grid.charAt(combosToWin[i][2]) == symbolX) {
                     System.out.println("X wins");
                     has3X = true;
                     break;
                 } else if (grid.charAt(combosToWin[i][0]) == symbolO && grid.charAt(combosToWin[i][1]) == symbolO && grid.charAt(combosToWin[i][2]) == symbolO) {
                    System.out.println("O wins");
                     has3O = true;
                    break;
                }
            }
            secondUserTurn(ticTacToeGrid); //второй ход
            }
        }
        
    public static void printStartGrid() {                      //пустая сетка
        System.out.println("---------\n" +
                "| " + " " + " " + " " + " " + " " + " |\n" +
                "| " + " " + " " + " " + " " + " " + " |\n" +
                "| " + " " + " " + " " + " " + " " + " |\n" +
                "---------");
    }

    public static void printCurrentGrid(Character[][] ticTacToeGrid) {            // сетка, которая обновляется после каждого хода
        System.out.println("---------");
        for (Character[] characters : ticTacToeGrid) {
            System.out.print("| ");
            for (Character character : characters) {
                System.out.print(character + " ");
            }
            System.out.println("|");
        }
        System.out.println("---------");
    }

    public static void userTurn(Character[][] ticTacToeGrid) {          //первый ход
        Scanner scn = new Scanner(System.in);
        Pattern p1 = Pattern.compile("(.*[0-4]+.*)");                   // проверка на тип int, на правильность координаты ячейки и на "свободна ли ячейка"
        Matcher m1;
        boolean areNumbers1 = false;
        boolean inRange1 = false;
        boolean isOccupiedForUser = false;
        while (!areNumbers1) {
            while (!inRange1) {
                while (!isOccupiedForUser) {
                    System.out.print("User 1 Enter the coordinates: ");
                    coordinates1 = scn.nextLine();
                    m1 = p1.matcher(coordinates1);
                    if (!m1.matches()) {
                        System.out.println("You should enter numbers!");
                        areNumbers1 = false;
                    } else {
                        areNumbers1 = true;
                        coordRow = Integer.parseInt(String.valueOf(coordinates1.charAt(0)));
                        coordCol = Integer.parseInt(String.valueOf(coordinates1.charAt(2)));
                        if (coordRow < 1 || coordRow > 3 || coordCol < 1 || coordCol > 3) {
                            System.out.println("Coordinates should be from 1 to 3!");
                            inRange1 = false;
                        } else {
                            inRange1 = true;
                            coordRow -= 1;
                            coordCol -= 1;
                            if (ticTacToeGrid[coordRow][coordCol] == symbolX) {
                                isOccupiedForUser = false;
                                System.out.println("This cell is occupied! Choose another one!");
                            } else if (ticTacToeGrid[coordRow][coordCol] == symbolO) {
                                isOccupiedForUser = false;
                                System.out.println("This cell is occupied! Choose another one!");
                            } else {
                                isOccupiedForUser = true;
                                ticTacToeGrid[coordRow][coordCol] = symbolX;
                                printCurrentGrid(ticTacToeGrid);
                                convert(ticTacToeGrid);
                                countX++;
                                System.out.println(countX);
                            }
                        }
                    }
                }
            }
        }

    }

    public static void secondUserTurn(Character[][] ticTacToeGrid) {
        Scanner scn2 = new Scanner(System.in);
        Pattern p2 = Pattern.compile("(.*[0-4]+.*)");
        Matcher m2;
        boolean areNumbers2 = false;
        boolean inRange2 = false;
        boolean isOccupiedForUser2 = false;
        while (!areNumbers2) {
            while (!inRange2) {
                while (!isOccupiedForUser2) {
                    System.out.print("User 2 Enter the coordinates: ");
                    coordinates2 = scn2.nextLine();
                    m2 = p2.matcher(coordinates2);
                    if (!m2.matches()) {
                        System.out.println("You should enter numbers!");
                        areNumbers2 = false;
                    } else {
                        areNumbers2 = true;
                        coordRow = Integer.parseInt(String.valueOf(coordinates2.charAt(0)));
                        coordCol = Integer.parseInt(String.valueOf(coordinates2.charAt(2)));
                        if (coordRow < 1 || coordRow > 3 || coordCol < 1 || coordCol > 3) {
                            System.out.println("Coordinates should be from 1 to 3!");
                            inRange2 = false;
                        } else {
                            inRange2 = true;
                            coordRow -= 1;
                            coordCol -= 1;
                            if (ticTacToeGrid[coordRow][coordCol] == symbolX) {
                                isOccupiedForUser2 = false;
                                System.out.println("This cell is occupied! Choose another one!");
                            } else if (ticTacToeGrid[coordRow][coordCol] == symbolO) {
                                isOccupiedForUser2 = false;
                                System.out.println("This cell is occupied! Choose another one!");
                            } else {
                                isOccupiedForUser2 = true;
                                ticTacToeGrid[coordRow][coordCol] = symbolO;
                                printCurrentGrid(ticTacToeGrid);
                                convert(ticTacToeGrid);
                                countO++;
                                System.out.println(countO);
                            }
                        }
                    }
                }
            }
        }
    }
    
    public static void convert(Character[][] ticTacToeGrid) {
        str2 = Arrays.deepToString(ticTacToeGrid);                                             //Конвертирование массива в строку
        grid = str2.substring(2, 3) + str2.substring(5, 6) + str2.substring(8, 9) +            // Новая строка с введенными X и O. Сделал это для того,
                str2.substring(13, 14) + str2.substring(16, 17) + str2.substring(19, 20) +     // чтобы было легче определить победителя
                str2.substring(24, 25) + str2.substring(27, 28) + str2.substring(30, 31);
        System.out.println("Tic-Tac-Toe line grid: " + grid);
    }
}

Ответы (1 шт):

Автор решения: Alexander Chernin

Вы делаете break в случае либо крестики, либо нолики победили, после чего происходит просто выход из цикла for и переход к методу secondUserTurn.

for (int i = 0; i < 8; i++) {
    // определение победителя
    if (grid.charAt(combosToWin[i][0]) == symbolX && grid.charAt(combosToWin[i][1]) == symbolX && grid.charAt(combosToWin[i][2]) == symbolX) {
        has3X = true;
        break;
    } else if (grid.charAt(combosToWin[i][0]) == symbolO && grid.charAt(combosToWin[i][1]) == symbolO && grid.charAt(combosToWin[i][2]) == symbolO) {
        has3O = true;
        break;
    }
}

secondUserTurn(ticTacToeGrid); //второй ход

Надо просто делать перед этим вызовом проверку, что уже есть победитель. Рекомендую следующую модификацию:

while(true) {
    for (int i = 0; i < 8; i++) {
        ...
    }
    if (!has3O || !has3X) {
        secondUserTurn(ticTacToeGrid); //второй ход
    } else {
        // У нас есть победитель - игра закончена
        break; // выходим из while
    }
}
→ Ссылка