Крестики-Нолики на Java, МиниМакс с Alpha-Beta обрезкой.

Всем привет! Учусь языку Java, в курсе есть практикум где необходимо написать игру Крестики-Нолики в процедурном стиле, а затем переделать её в ООП, с тех пор я задался задачей создать непобидимый ИИ в процедурном стиле, у меня это получилось, во всяком случае я ниразу не выйграл спустя ~ 100 партий, используя разные обманные ходы, возможно у вас это получится. Затем я решил написать игру в ООП стиле с применением Алгоритма МиниМакс, а также с Алгоритмом МиниМакс с Альфа-Бета обрезкой. Я только начинаю изучать Java код возможно не идеальный, но на сайте GeekBrains я не нашел темы обсуждения Алгоритма МиниМакс и МиниМакс с Альфа-Бета обрезкой на языке Java, и решил, возможно кому-то будет интересна данная реализация игры в разных вариациях.
1) Реализация в процедурном стиле.
Когда я переделывал код некоторые пункты в блоке настроек не удалил, оставил как есть.
```
import java.util.*;
public class TicTacToe {
/**
* Блок настроек игры
/
private static final char DOT_EMPTY = ''; // Символ пустых ячеек
private static final char DOT_X = 'X'; // Символ
private static final char DOT_O = 'O'; // Символ
private static char[][] map; // Матрица карты
private static char[][] mapComputer; //Матрица игрового поля для компьютера.
private static final int SIZE = 3; // Размер поля
private static final int BLOCK = 2;
private static Scanner scanner = new Scanner(System.in);
private static Random random = new Random();
public static void main(String[] args) {
initMap();
printMap(map);
while (true) {
humanTurn(); // Ход человека
if (isEndGame(DOT_X)) {
break;
}
computerTurn(); // Ход компьютера
if (isEndGame(DOT_O)) {
break;
}
}
System.out.println("Игра закончена!");
}
/**
* Метод создания игрового поля
*/
private static void initMap() {
map = new char[SIZE][SIZE];
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
map[i][j] = DOT_EMPTY;
}
}
}
/**
* Метод вывода игрового поля на экран с текущими значениями
*/
private static void printMap(char[][] arr) {
for (int i = 0; i <= SIZE; i++) {
System.out.print(i + " ");
}
System.out.println();
for (int i = 0; i < SIZE; i++) {
System.out.print((i + 1) + " ");
for (int j = 0; j < SIZE; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
/**
* Метод хода человека
*/
private static void humanTurn() {
int x, y;
do {
System.out.println("Ваш ход, введите координаты клетки через пробел.");
y = scanner.nextInt() - 1;
x = scanner.nextInt() - 1;
}
while (!isCellValid(x, y, DOT_X));
map[y][x] = DOT_X;
}
/**
* Метод хода компьтера
*/
private static void computerTurn() {
System.out.println("Компьютер сделал ход, теперь Ваш ход.");
// Находим выйгрышный ход компьютера
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (map[i][j] == DOT_EMPTY) {
map[i][j] = DOT_O;
if (checkWin(DOT_O, map))
return;
if (!checkWin(DOT_O, map)) {
map[i][j] = DOT_EMPTY;
}
}
}
}
//Блокируем выигрышный ход человека
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (map[i][j] == DOT_EMPTY) {
map[i][j] = DOT_X;
if (checkWin(DOT_X, map)) {
map[i][j] = DOT_O;
return;
}
if (!checkWin(DOT_X, map)) {
map[i][j] = DOT_EMPTY;
}
}
}
}
//Если центральная клетка свободна мы её занимаем
int center = (((SIZE + 1) / 2) - 1);
if (map[center][center] == DOT_EMPTY) {
map[center][center] = DOT_O;
return;
}
//Занимает правую нижнюю клетку
if(map[1][2] == DOT_X && map [2][1] == DOT_X && map[2][2] == DOT_EMPTY || map[1][2] == DOT_X && map[2][0] == DOT_X) {
map[2][2] = DOT_O;
System.out.println("Правая нижняя клетка");
return;
}
//Занимаем левую нижнюю клетку
if(map[1][0] == DOT_X && map[2][1] == DOT_X && map[2][0] == DOT_EMPTY || map[1][0] == DOT_X && map[2][2] == DOT_X && map[2][0] == DOT_EMPTY){
map[2][0] = DOT_O;
System.out.println("Левая нижняя клетка");
return;
}
// Ход по диагоналям, если занята центральная клетка
if(map[1][1] == DOT_X){
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (map[i][j] == DOT_EMPTY) {
map[i][j] = DOT_X;
if (checkDiagonalComputerTurn(DOT_X)) {
System.out.println("Диагонали X " + checkDiagonalComputerTurn(DOT_X));
map[i][j] = DOT_O;
return;
}
if (!checkDiagonalComputerTurn(DOT_X)) {
map[i][j] = DOT_EMPTY;
}
}
}
}
}
// Проверка на 2 шага вперёд по СТОЛБЦАМ, чтобы в столбце были: 1 клетка со знаком компьютера и 2 свободные
if (map[0][2] == DOT_X || map[2][0] == DOT_X || map[0][0] == DOT_X || map[2][2] == DOT_X) {
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (map[i][j] == DOT_EMPTY) {
map[i][j] = DOT_O;
if (checkVerticalComputerTurn(DOT_O)) {
System.out.println("Столбцы O " + checkVerticalComputerTurn(DOT_O));
return;
}
if (!checkVerticalComputerTurn(DOT_O)) {
map[i][j] = DOT_EMPTY;
}
}
}
}
}
/*// Проверка на 2 шага вперед по ДИАГОНАЛЯМ, чтобы в диагонали были: 1 клетка со знаком компьютера и 2 свободных
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (map[i][j] == DOT_EMPTY) {
map[i][j] = DOT_O;
if (checkDiagonalComputerTurn(DOT_O)) {
System.out.println("Диагонали O " + checkDiagonalComputerTurn(DOT_O));
map[i][j] = DOT_O;
return;
}
if (!checkDiagonalComputerTurn(DOT_O)) {
map[i][j] = DOT_EMPTY;
}
}
}
}*/
if(map[1][1] == DOT_O && map[0][0] != DOT_X && map[0][2] != DOT_X && map[2][0] != DOT_X && map[2][2] != DOT_X){
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (map[i][j] == DOT_EMPTY) {
map[i][j] = DOT_O;
if (checkDiagonalComputerTurn(DOT_O)) {
System.out.println("Диагонали O " + checkDiagonalComputerTurn(DOT_O));
return;
}
if (!checkDiagonalComputerTurn(DOT_O)) {
map[i][j] = DOT_EMPTY;
}
}
}
}
}
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (map[i][j] == DOT_EMPTY) {
map[i][j] = DOT_O;
if (checkLineComputerTurn(DOT_O)) {
System.out.println("Строки O " + checkLineComputerTurn(DOT_O));
return;
}
if (!checkLineComputerTurn(DOT_O)) {
map[i][j] = DOT_EMPTY;
}
}
}
}
// рандомный ход
int x; int y;
do {
x = random.nextInt(SIZE);
y = random.nextInt(SIZE);
} while (!isCellValid(x, y, DOT_O));
map[y][x] = DOT_O;
}
/**
* Проверка на валидность хода
* @param x
* @param y
* @return признак валидности хода
*/
private static boolean isCellValid(int x, int y, char playerSymbol) {
if (x < 0 || x >= SIZE || y < 0 || y >= SIZE) {
if(playerSymbol != DOT_O)System.out.println("Вы ввели некорректные координаты клетки!");
return false;
}
if (map[y][x] == DOT_EMPTY) return true;
if(playerSymbol != DOT_O) System.out.println("Вы ввели координаты занятой клетки!");
return false;
}
/**
* Метод проверки игры на завершение
* @param playerSymbol сомвол, которым играет текущий игрок
* @return boolean - признак завершения игры
*/
private static boolean isEndGame(char playerSymbol) {
boolean result = false;
printMap(map);
// Проверяем необходимость следующего хода
if (checkWin(playerSymbol, map)) {
System.out.println("Победили " + playerSymbol + "!");
result = true;
}
if (isMapFull() && !checkWin(playerSymbol, map)) {
System.out.println("Ничья!");
result = true;
}
return result;
}
/**
* Проверка на 100%-ю заполненность поля
* @return boolean признак наличия свободных ячеек
*/
private static boolean isMapFull() {
boolean result = true;
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (map[i][j] == DOT_EMPTY)
result = false;
}
}
return result;
}
/**
* Проверка выйгрышных комбинаций компьтера, или человека
* @param playerSymbol символ игрока, или компьютера для проверки
* @return true or false
*/
private static boolean checkWin(char playerSymbol, char[][] map){
boolean checkLine, checkColumns, checkDiagonalA, checkDiagonalB;
for (int i = 0; i < map.length; i++){
checkLine = true;
checkColumns = true;
checkDiagonalA = true;
checkDiagonalB = true;
for (int j = 0; j < map.length; j++){
checkLine &= (map[i][j] == playerSymbol);
checkColumns &= (map[j][i] == playerSymbol);
checkDiagonalA &= (map[j][j] == playerSymbol);
checkDiagonalB &= (map[map.length - 1 - j][j] == playerSymbol);
}
if (checkLine || checkColumns || checkDiagonalA || checkDiagonalB) return true;
}
return false;
}
/**
* Делаем проверку ближайших символов по вертикали для хода компьтера
* @param playerSymbol Символ игрока.
* @return результат проверки.
*/
private static boolean checkVerticalComputerTurn(char playerSymbol) {
int emptyCellVertical, singCellVertical;
for (int x = 0; x < SIZE; x++) {
emptyCellVertical = 0;
singCellVertical = 0;
for (int y = 0; y < SIZE; y++) {
if (map[y][x] == playerSymbol)
singCellVertical++;
else if (map[y][x] == DOT_EMPTY)
emptyCellVertical++;
if ((singCellVertical == SIZE - 1) && (emptyCellVertical == SIZE - 2))
return true;
}
}
return false;
}
/**
* Делаем проверку ближайших символов по диагонали для хода компьтера
* @param playerSymbol Символ игрока.
* @return результат проверки.
*/
private static boolean checkDiagonalComputerTurn(char playerSymbol) {
int emptyCellDiagonalA = 0, singCellDiagonalA = 0, emptyCellDiagonalB = 0, singCellDiagonalB = 0;
for (int x = 0; x < SIZE; x++) {
if (map[x][x] == playerSymbol)
singCellDiagonalA++;
else if (map[x][x] == DOT_EMPTY)
emptyCellDiagonalA++;
if ((singCellDiagonalA == SIZE - 1) && (emptyCellDiagonalA == SIZE - 2))
return true;
if (map[x][SIZE - 1 - x] == playerSymbol)
singCellDiagonalB++;
else if (map[x][SIZE - 1 - x] == DOT_EMPTY)
emptyCellDiagonalB++;
if ((singCellDiagonalB == SIZE - 1) && (emptyCellDiagonalB == SIZE - 2))
return true;
}
return false;
}
private static boolean checkLineComputerTurn(char playerSymbol){
int emptyCellLine, singCellLine;
for (int x = 0; x < SIZE; x++) {
emptyCellLine = 0;
singCellLine = 0;
for (int y = 0; y < SIZE; y++) {
if (map[x][y] == playerSymbol)
singCellLine++;
else if (map[x][y] == DOT_EMPTY)
emptyCellLine++;
if ((singCellLine == SIZE - 1) && (emptyCellLine == SIZE - 2))
return true;
}
}
return false;
}
}
**2) Реализация игры в ООП с применением Алгоритма МиниМакс**
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Random;
class Point {
int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return "[" + x + ", " + y + "]";
}
}
class PointsAndScores {
int score;
Point point;
PointsAndScores(int score, Point point){
this.score = score;
this.point = point;
}
}
class Board {
List<Point> availablePoints; // Создаём переменную для хранения доступных точек
Scanner scan = new Scanner(System.in);
int [][] board = new int[3][3];
public Board(){
}
/**
* Проверка окончания игры
* @return true or false
*/
public boolean isGameOver(){
return (checkWin(1) || checkWin(2) || getAvailableStates().isEmpty());
}
/**
* Проверка валидности вводимых координат клетки.
* @param x координаты клетки X
* @param y координаты клетки Y
* @return true or false
*/
public boolean isCellValid(int x, int y){
if (x < 0 || x > board.length -1 || y < 0 || y > board.length -1) {
System.out.println("Вы ввели некорректные координаты клетки!");
return false;
}
if (board[x][y] == 0)
return true;
else System.out.println("Вы ввели координаты занятой клетки!");
return false;
}
/**
* Метод хода человека с проверкой на валидность вводимых координат.
* @return Point (x, y)
*/
public Point userMove(){
int x, y;
do{
System.out.println("Ваш ход! Введите координаты клетки через пробел от 1 до 3.");
x = scan.nextInt() - 1;
y = scan.nextInt() - 1;
}while (!isCellValid(x, y));
return new Point(x, y);
}
/**
* Вывод состояния игровой доски на экран
*/
public void displayBoard(){
System.out.println();
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}
/**
* Проверка выйгрышных комбинаций компьтера, или человека
* @param playerSymbol символ игрока, или компьютера для проверки
* @return true or false
*/
public boolean checkWin(int playerSymbol){
boolean checkLine, checkColumns, checkDiagonalA, checkDiagonalB;
for (int i = 0; i < board.length; i++){
checkLine = true;
checkColumns = true;
checkDiagonalA = true;
checkDiagonalB = true;
for (int j = 0; j < board.length; j++){
checkLine &= (board[i][j] == playerSymbol);
checkColumns &= (board[j][i] == playerSymbol);
checkDiagonalA &= (board[j][j] == playerSymbol);
checkDiagonalB &= (board[board.length - 1 - j][j] == playerSymbol);
}
if (checkLine || checkColumns || checkDiagonalA || checkDiagonalB) return true;
}
return false;
}
/**
* Получить доступные состояния ходов
* @return Возврат доступных точек для хода
*/
public List<Point> getAvailableStates(){
availablePoints = new ArrayList<>();
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
if(board[i][j] == 0) {
availablePoints.add(new Point(i, j));
}
}
}
return availablePoints;
}
/**
* Сделать ход
* @param point координаты хода
* @param playerSymbol символ игрока, или компьютера
*/
public void placeAMove(Point point, int playerSymbol){
board[point.x][point.y] = playerSymbol;
}
/**
* Выбрать лучший ход
* @return Координаты хода
*/
public Point returnBestMove(){
int MAX = - 100000;
int best = -1;
for (int i = 0; i < rootsChildrenScores.size(); ++i){
if (MAX < rootsChildrenScores.get(i).score){
MAX = rootsChildrenScores.get(i).score;
best = i;
}
}
return rootsChildrenScores.get(best).point;
}
/**
* Возврат минимального значения
* @param List
* @return индекс минимального значения
*/
public int returnMin(List<Integer> List){
int min = Integer.MAX_VALUE;
int index = -1;
for (int i = 0; i < List.size(); ++i){
if(List.get(i) < min){
min = List.get(i);
index = i;
}
}
return List.get(index);
}
/**
* Возврат максимального значения
* @param List
* @return индекс максимального значения
*/
public int returnMax(List<Integer> List){
int max = Integer.MIN_VALUE;
int index = -1;
for (int i = 0; i < List.size(); ++i){
if(List.get(i) > max){
max = List.get(i);
index = i;
}
}
return List.get(index);
}
/**
* Создаём переменную для хранения координат и очков доступных клеток
*/
List<PointsAndScores> rootsChildrenScores;
/**
* Вызов метода МиниМакс
* @param depth глубина хода
* @param turn очередь ходов игроков
*/
public void callMiniMax(int depth, int turn){
rootsChildrenScores = new ArrayList<>();
miniMax(depth, turn);
}
/**
* Метод МиниМакс
* @param depth глубина хода
* @param turn поочередность ходов
* @return Возвращаем максимальное значение, если ходит компьютер, или минимальное значение, если ходит человек
*/
public int miniMax(int depth, int turn){
if (checkWin(1)) return +1;
if (checkWin(2)) return -1;
List<Point> pointsAvailable = getAvailableStates(); // Переменная, которая получает список доступных точек
if (pointsAvailable.isEmpty()) return 0;
List<Integer> scores = new ArrayList<>(); // Переменная хранения счета ходов
for(int i = 0; i < pointsAvailable.size(); ++i){
Point point = pointsAvailable.get(i);
if (turn == 1) { // Ход компьютера
placeAMove(point, 1); // Совершение хода компьютера в доступную точку поля
int currentScore = miniMax(+1, 2); // Переменная текущего счета, которая вызывает метод miniMAx и передает новые параметры методу меняя очередь хода игрока
scores.add(currentScore); // Передаём результаты хода переменной подсчёта очков хода
if (depth == 0)
rootsChildrenScores.add(new PointsAndScores(currentScore, point)); // Добавление в переменную список индексов клеток и очков заработанных с этой клетки
}else if (turn == 2){ // Совершение хода в доступную точку за человека
placeAMove(point, 2);
scores.add(miniMax(+1, 1)); // Передаем результат хода в переменную подсчёта очков хода, вызываем метод miniMax с новыми параметрами
}
board[point.x][point.y] = 0; // Очистка клетки поля
}
return turn == 1 ? returnMax(scores) : returnMin(scores); // Возвращаем максимальное значение, если ходит компьютер, или минимальное значение, если ходит человек
}
}
public class TicTacToeOOPMiniMax {
public static void main (String[] args){
Board b = new Board();
Random rand = new Random();
b.displayBoard();
System.out.println("Кто будет ходить первым? (1)Компьютер, (2)Человек.");
int choice = b.scan.nextInt();
if(choice == 1){
Point p = new Point(rand.nextInt(3), rand.nextInt(3));
b.placeAMove(p,1);
b.displayBoard();
}
while (!b.isGameOver()){
//System.out.println("Ваш ход!");
//Point userMove = new Point(b.scan.nextInt(), b.scan.nextInt());
b.placeAMove(b.userMove(),2);
b.displayBoard();
if (b.isGameOver()) {
break;
}
b.callMiniMax(0,1);
for (PointsAndScores pas : b.rootsChildrenScores) {
System.out.println("Point: " + pas.point + " Score: " + pas.score);
}
b.placeAMove(b.returnBestMove(),1);
b.displayBoard();
}
if(b.checkWin(1)) {
System.out.println("Победил Компьютер!");
} else if (b.checkWin(2)) {
System.out.println("Вы победили!");
} else System.out.println("Ничья!");
}
}
**3) Реализация игры в ООП МиниМакс с Alpha-Beta обрезкой**
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Random;
class Point {
int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return "[" + x + ", " + y + "]";
}
}
class Board {
List<Point> availablePoints;
Scanner scan = new Scanner(System.in);
int board[][] = new int[3][3];
public Board(){
}
/**
* Проверка игры на завершение
* @return true or false
*/
public boolean isGameOver(){
return (checkWin(1) || checkWin(2) || getAvailableStates().isEmpty());
}
/**
* Проверка победных кобинаций
* @param playerSymbol символ игрока, который проверяем на наличие победных комбинаций
* @return true or false
*/
public boolean checkWin(int playerSymbol){
boolean checkLine, checkColumns, checkDiagonalA, checkDiagonalB;
for (int i = 0; i < board.length; ++i){
checkLine = true;
checkColumns = true;
checkDiagonalA = true;
checkDiagonalB = true;
for (int j = 0; j < board.length; ++j){
checkLine &= (board[i][j] == playerSymbol);
checkColumns &= (board[j][i] == playerSymbol);
checkDiagonalA &= (board[j][j] == playerSymbol);
checkDiagonalB &= (board[board.length - 1 - j][j] == playerSymbol);
}
if (checkLine || checkColumns || checkDiagonalA || checkDiagonalB) return true;
}
return false;
}
/**
* Проверка валидности хода, корректности вводимых координат
* @param x координаты: x
* @param y координаты: y
* @return true or false
*/
public boolean isCellValid(int x, int y){
if (x < 0 || x > board.length - 1 || y < 0 || y > board.length - 1){
System.out.println("Вы ввели некорректные координаты клетки!");
return false;
}
if (board[x][y] == 0)
return true;
else System.out.println("Вы ввели координаты уже занятой клетки!");
return false;
}
/**
* Метод хода человека с проверкой на валидность
* @return true or false
*/
public Point userMove(){
int x, y;
do {
System.out.println("Ваш ход! Введите координаты клетки поля через пробел от 1 до 3.");
x = scan.nextInt() -1;
y = scan.nextInt() -1;
}while (!isCellValid(x, y));
return new Point(x, y);
}
/**
* Получение списка доступных для хода клеток
* @return ArrayList<>() availablePoints
*/
public List<Point> getAvailableStates(){
availablePoints = new ArrayList<>();
for(int i = 0; i < board.length; ++i){
for(int j = 0; j < board.length; ++j){
if (board[i][j] == 0) {
availablePoints.add(new Point(i, j));
}
}
}
return availablePoints;
}
/**
* Совершить ход в клетку поля
* @param point координаты клетки
* @param playerSymbol символ, который совершает ход
*/
public void placeAMove(Point point, int playerSymbol){
board[point.x][point.y] = playerSymbol;
}
/**
* Вывод игровой доски на экран
*/
public void displayBoard(){
System.out.println();
for (int i = 0; i < board.length; ++i){
for (int j = 0; j < board.length; ++j){
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}
/**
* Объявляем переменную в которой будут храниться координаты для совершения хода компьютера
*/
Point computersMove;
/**
* Метод МиниМакс с Alpha - Beta обрезкой
* @param depth параметр счёта глубины хода
* @param turn параметр очереди ходов (компьютер / человек)
* @return возвращает счёт с клетки, которую оценивает метод
*/
public int miniMaxAlphaBeta(int depth, int turn) {
if (checkWin(1)) return +1;
if (checkWin(2)) return -1;
List<Point> pointsAvailable = getAvailableStates();
if (pointsAvailable.isEmpty()) return 0;
int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;
for (int i = 0; i < pointsAvailable.size(); ++i) {
Point point = pointsAvailable.get(i);
if (turn == 1) {
placeAMove(point, 1);
int currentScore = miniMaxAlphaBeta(+1, 2);
max = Math.max(currentScore, max);
if (depth == 0) System.out.println("Счёт клетки: "+(i + 1)+" = "+currentScore);
if (currentScore >= 0) {
if (depth == 0) computersMove = point;
}
if (currentScore == 1) {
board[point.x][point.y] = 0;
break;
}
if (i == pointsAvailable.size() - 1 && max < 0) {
if (depth == 0) computersMove = point;
}
} else if (turn == 2) {
placeAMove(point, 2);
int currentScore = miniMaxAlphaBeta(+1, 1);
min = Math.min(currentScore, min);
if (min == -1) {
board[point.x][point.y] = 0;
break;
}
}
board[point.x][point.y] = 0;
}
return turn == 1 ? max : min;
}
}
public class TicTacToeGame {
public static void main(String[] args) {
Board b = new Board();
Random rand = new Random();
b.displayBoard();
System.out.println("Выберите кто будет ходить первым: (1)Компьютер, (2)Игрок");
int choice = b.scan.nextInt();
if (choice == 1){
Point p = new Point(rand.nextInt(3), rand.nextInt(3));
b.placeAMove(p,1);
b.displayBoard();
}
while (!b.isGameOver()){
//System.out.println("Ваш ход! Введите координаты клетки поля через пробел от 1 до 3.");
//Point userMove = new Point(b.scan.nextInt(), b.scan.nextInt());
//b.placeAMove(userMove, 2);
b.placeAMove(b.userMove(),2);
b.displayBoard();
if(b.isGameOver()) break;
b.miniMaxAlphaBeta(0,1);
b.placeAMove(b.computersMove,1);
b.displayBoard();
}
if (b.checkWin(1))
System.out.println("Вы проиграли!");
else if (b.checkWin(2))
System.out.println("Вы выйграли!");
else
System.out.println("Ничья!");
}
}
```