Крестики-нолики в процедурном стиле Java (Часть 2)

Крестики-нолики в процедурном стиле Java

Всем доброго времени. На связи Алексей Гулынин. В данной статье закончим разработку консольного варианта игры крестики-нолики в процедурном стиле на Java.

Метод "userShot" отвечает за ход игрока, где первым параметром указывается метка "X" или "O" (то, чем он ходит), а вторым номером указывается какой игрок ходит. Здесь также используется объект класса Scanner, который считывает данные из потока (в данном случае вводимые в консоль — System.in).

Скачать уже готовый проект полностью можно по ссылке.

Цикл "do — while" будет отрабатывать до тех пор, пока пользователь не сходит в свободную ячейку:

public static void userShot(String sign, int i)
{
  int x = -1;
  int y = -1;
  do
  {
    if (i == 0)
    {
      System.out.println("Введите координаты x y (1 - " + DIMENSION + "): ");
    }
    else
    {
      System.out.println("Игрок " + i + ". Введите координаты x y (1 - " + DIMENSION + "): ");
    }
    Scanner sc = new Scanner(System.in);
    x = sc.nextInt() - 1;
    y = sc.nextInt() - 1;
  }
  while (isCellBusy(x, y));
  field[x][y] = sign;
}

Самый интересный метод — это ход компьютера, в котором будут зачатки искусственного интеллекта.

В случае "aiLevel = 0" используется объект класса Random. В данном случае компьютер просто ходит случайным образом на любую незанятую ячейку.

В случае "aiLevel = 1" компьютер анализирует ситуацию, при которой игрок может выиграть на следующем ходу. Если такой выигрышный ход существует, то компьютер ходит на эту ячейку. Как это реализовано: в двойном цикле проверяются все ячейки, и компьютер в каждую незанятую ячейку ставит метку игрока ("field[i][j] = USER_SIGN"). Далее с помощью метода "checkWin()" проверяется является ли данный ход выигрышным, если да, то эти координаты запоминаются, и компьютер ставит на них свою метку (AI_SIGN). После каждого анализа ячейка обнуляется (("field[i][j] = NOT_SIGN").

В случае "aiLevel = 2" логика работы такая же, как и на первом уровне. Только в данном случае компьютер анализирует свой ход и, если он является выигрышным, ходит.

public static void aiShot()
{
  int x = -1;
  int y = -1;
  boolean ai_win = false;
  boolean user_win = false;
  // aiLevel = 2
  // Находим выигрышный ход
  if (aiLevel == 2)
  {
    for (int i = 0; i < DIMENSION; i++)
    {
      for (int j = 0; j < DIMENSION; j++)
      {
        if (!isCellBusy(i, j))
        {
          field[i][j] = AI_SIGN;
          if (checkWin(AI_SIGN))
          {
            x = i;
            y = j;
            ai_win = true;
          }
          field[i][j] = NOT_SIGN;
        }
      }
    }
  }
  // aiLevel = 1
  // Блокировка хода пользователя, если он побеждает на следующем ходу
  if (aiLevel > 0)
  {
    if (!ai_win)
    {
      for (int i = 0; i < DIMENSION; i++)
      {
        for (int j = 0; j < DIMENSION; j++)
        {
          if (!isCellBusy(i, j))
          {
            field[i][j] = USER_SIGN;
            if (checkWin(USER_SIGN))
            {
              x = i;
              y = j;
              user_win = true;
            }
            field[i][j] = NOT_SIGN;
          }
        }
      }
    }
  }
  // aiLevel = 0
  if (!ai_win && !user_win)
  {
    do
    {
      Random rnd = new Random();
      x = rnd.nextInt(DIMENSION);
      y = rnd.nextInt(DIMENSION);
    }
    while (isCellBusy(x, y));
  }
  field[x][y] = AI_SIGN;
}

Следующий метод проверяет ячейку на занятость:

public static boolean isCellBusy(int x, int y)
{
  if (x < 0 || y < 0 || x > DIMENSION - 1 || y > DIMENSION - 1)
  {
    return false;
  }
  return field[x][y] != NOT_SIGN;
}

Следующие 2 метода используются для определения победы в игре. Метод "checkLine()" проверяет одну линию (горизонталь, вертикаль, диагональ). В данном случае используется уравнение прямой из алгебры.

Метод "checkWin()" проверяет все линии, и если находит выигрышную, то возвращает "true", иначе "false".

Чтобы лучше хорошо понять логику работы данных методов рекомендую выполнить их на бумаге. Тогда сразу всё станет понятно.

public static boolean checkLine(int start_x, int start_y, int dx, int dy, String sign)
{
  for (int i = 0; i < DIMENSION; i++)
  {
    if (field[start_x + i * dx][start_y + i * dy] != sign)
      return false;
  }
  return true;
}

public static boolean checkWin(String sign)
{
  for (int i = 0; i < DIMENSION; i++)
  {
    // проверяем строки
    if (checkLine(i, 0, 0, 1, sign)) return true;
    // проверяем столбцы
    if (checkLine(0, i, 1, 0, sign)) return true;
  }
  // проверяем диагонали
  if (checkLine(0, 0, 1, 1, sign)) return true;
  if (checkLine(0, DIMENSION - 1, 1, -1, sign)) return true;
  return false;
}

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

На связи был Алексей Гулынин, оставляйте свои комментарии, увидимся в следующих статьях.


Комментарии:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *