Java: крестики-нолики - как ИЗБЕЖАТЬ, что повторяющаяся ячейка не будет вводиться пользователем?

Как мне предотвратить ввод одной и той же координаты крестики-нолики пользователем?

Пользовательский ввод осуществляется в основном методе игрового класса.

Клетки крестики-нолики с координатами [x, y] в диапазоне от (0-2) могут быть:

0(_), 1 (X) or 2 (O)

Класс сетки с алгоритмом обрезки дерева поиска альфа-бета

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

class Grid {

  List<Cell> availableCells;
  int[][] board = new int[3][3];
  Scanner scan = new Scanner(System.in);
  // Set limit to search tree depth
  int treeDepth = 9;

  List<CellsAndScores> rootsChildrenScore = new ArrayList<>();

  public int score() {
    int score = 0;

    // Check all columns
    for (int j = 0; j < 3; ++j) {
      int X = 0;
      int O = 0;
      for (int i = 0; i < 3; ++i) {
        if (board[i][j] == 0) {
        } else if (board[i][j] == 1) {
          X++;
        } else {
          O++;
        }
      }
      score += changeInScore(X, O);
    }

    // Check all rows
    for (int i = 0; i < 3; ++i) {
      int X = 0;
      int O = 0;
      for (int j = 0; j < 3; ++j) {
        if (board[i][j] == 0) {
        } else if (board[i][j] == 1) {
          X++;
        } else {
          O++;
        }
      }
      score += changeInScore(X, O);
    }

    int X = 0;
    int O = 0;

    // Check diagonal (first)
    for (int i = 0, j = 0; i < 3; ++i, ++j) {
      if (board[i][j] == 1) {
        X++;
      } else if (board[i][j] == 2) {
        O++;
      } else {
      }
    }

    score += changeInScore(X, O);

    X = 0;
    O = 0;

    // Check Diagonal (Second)
    for (int i = 2, j = 0; i > -1; --i, ++j) {
      if (board[i][j] == 1) {
        X++;
      } else if (board[i][j] == 2) {
        O++;
      } else {
      }
    }

    score += changeInScore(X, O);

    return score;
  }

  private int changeInScore(int X, int O) {
    int change;
    if (X == 3) {
      change = 100;
    } else if (X == 2 && O == 0) {
      change = 10;
    } else if (X == 1 && O == 0) {
      change = 1;
    } else if (O == 3) {
      change = -100;
    } else if (O == 2 && X == 0) {
      change = -10;
    } else if (O == 1 && X == 0) {
      change = -1;
    } else {
      change = 0;
    }
    return change;
  }

  public int alphaBetaMinimax(int alpha, int beta, int depth, int turn) {

    if (beta <= alpha) {
      System.out.println("Pruning at tree depth = " + depth + " alpha: " + alpha + " beta: " + beta);
      if (turn == 1)
        return Integer.MAX_VALUE;
      else
        return Integer.MIN_VALUE;
    }

    if (depth == treeDepth || gameOver()) {
      return score();
    }

    List<Cell> cellsAvailable = getAvailableStates();

    if (cellsAvailable.isEmpty()) {
      return 0;
    }

    if (depth == 0) {
      rootsChildrenScore.clear();
    }

    int maxValue = Integer.MIN_VALUE, minValue = Integer.MAX_VALUE;

    for (int i = 0; i < cellsAvailable.size(); ++i) {
      Cell cell = cellsAvailable.get(i);

      int currentScore = 0;

      if (turn == 1) {
        placeAMove(cell, 1);
        currentScore = alphaBetaMinimax(alpha, beta, depth + 1, 2);
        maxValue = Math.max(maxValue, currentScore);

        // Set alpha
        alpha = Math.max(currentScore, alpha);

        if (depth == 0) {
          rootsChildrenScore.add(new CellsAndScores(currentScore, cell));
        }
      } else if (turn == 2) {
        placeAMove(cell, 2);
        currentScore = alphaBetaMinimax(alpha, beta, depth + 1, 1);
        minValue = Math.min(minValue, currentScore);

        // Set beta
        beta = Math.min(currentScore, beta);
      }
      // reset board
      board[cell.x][cell.y] = 0;

      // Do not evaluate the rest of the branches after search tree is pruned
      if (currentScore == Integer.MAX_VALUE || currentScore == Integer.MIN_VALUE)
        break;
    }
    return turn == 1 ? maxValue : minValue;
  }

  public boolean gameOver() {
    // Game is over is someone has won, or board is full (draw)
    return (hasXWon() || hasOWon() || getAvailableStates().isEmpty());
  }

  public boolean hasXWon() {
    if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 1)
        || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 1)) {
      // System.out.println("X Diagonal Win");
      return true;
    }
    for (int i = 0; i < 3; ++i) {
      if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
          || (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
        // System.out.println("X Row or Column win");
        return true;
      }
    }
    return false;
  }

  public boolean hasOWon() {
    if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 2)
        || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 2)) {
      // System.out.println("O Diagonal Win");
      return true;
    }
    for (int i = 0; i < 3; ++i) {
      if ((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
          || (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2)) {
        // System.out.println("O Row or Column win");
        return true;
      }
    }

    return false;
  }

  public List<Cell> getAvailableStates() {
    availableCells = new ArrayList<>();
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        if (board[i][j] == 0) {
          availableCells.add(new Cell(i, j));
        }
      }
    }
    return availableCells;
  }

  public void placeAMove(Cell Cell, int player) {
    board[Cell.x][Cell.y] = player; // player = 1 for X, 2 for O
  }

  public Cell returnBestMove() {
    int MAX = -100000;
    int best = -1;

    for (int i = 0; i < rootsChildrenScore.size(); ++i) {
      if (MAX < rootsChildrenScore.get(i).score) {
        MAX = rootsChildrenScore.get(i).score;
        best = i;
      }
    }

    return rootsChildrenScore.get(best).cell;
  }

  public void displayBoard() {
    System.out.println();
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        if (board[i][j] == 0)
          System.out.print("_" + " ");
        if (board[i][j] == 1)
          System.out.print("X" + " ");
        if (board[i][j] == 2)
          System.out.print("O" + " ");
      }
      System.out.println("");
    }
    System.out.println();
  }

  public void resetGrid() {
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        board[i][j] = 0;
      }
    }
  }
}

Класс ячейки

class Cell {

  int x, y;

  public Cell(int x, int y) {
    this.x = x;
    this.y = y;
  }

  public String toString() {
    return "[" + x + ", " + y + "]";
  }
}

class CellsAndScores {

  int score;
  Cell cell;

  CellsAndScores(int score, Cell cell) {
    this.score = score;
    this.cell = cell;
  }
}

Класс игры с основным методом - принимает пользовательский ввод

import java.util.Random;

public class Game {

  public static void main(String[] args) {
    Grid grid = new Grid();
    Random random = new Random();

    grid.displayBoard();

    System.out.print("Who moves first? [1]Computer(X) [2]User(O): ");
    int turn = grid.scan.nextInt();
    if (turn == 1) {
      Cell p = new Cell(random.nextInt(3), random.nextInt(3));
      grid.placeAMove(p, 1);
      grid.displayBoard();
    }

    while (!grid.gameOver()) {
      int x = 0, y = 0;

      System.out.print("Please enter an x coordinate [0-2]: ");
      x = grid.scan.nextInt();
      System.out.print("Please enter an y coordinate [0-2]: ");
      y = grid.scan.nextInt();

      Cell userMove = new Cell(y, x);

      grid.placeAMove(userMove, 2); // 2 for O and O is the user
      grid.displayBoard();
      if (grid.gameOver())
        break;

      grid.alphaBetaMinimax(Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 1);
      for (CellsAndScores pas : grid.rootsChildrenScore)
        System.out.println("Cell: " + pas.cell + " Score: " + pas.score);

      grid.placeAMove(grid.returnBestMove(), 1);
      grid.displayBoard();
    }
    if (grid.hasXWon()) {
      System.out.println("Unfortunately, you lost!");
      grid.resetGrid();
    } else if (grid.hasOWon()) {
      System.out.println("You win!");
      grid.resetGrid();
    } else {
      System.out.println("It's a draw!");
      grid.resetGrid();
    }
  }
}

person Tai    schedule 05.10.2016    source источник
comment
Ваша доска представляет собой int массив массивов. Итак, проверьте, есть ли там значение, введенное игроком. Например, установите для всех пустых пространств какое-то значение, чтобы вы могли знать, открыты ли пробелы. Кроме того, вы можете проверить, не является ли значение ячейки пустым значением, чтобы определить, была ли ячейка уже занята.   -  person Charles    schedule 06.10.2016
comment
Кроме того, вы как бы слишком усложняете работу с классом Cell. Вы, конечно, могли бы обойтись без него.   -  person Charles    schedule 06.10.2016


Ответы (1)


Моим ответом было бы добавить логический метод проверки в ваш класс Grid.java, а затем в ваш основной метод - вызвать этот логический метод проверки перед методом placeAMove ().

Например, в вашем классе Grid.java добавляем следующий метод:

/*
 * Return true if space is ok to use.
 */
public boolean isMoveOK(Cell cell) {
    return board[cell.x][cell.y] == 0;
}

Таким образом, используя ваши ранее существовавшие значения 0/1/2, которые отслеживают пустые значения пробелов / X / O, вы можете предоставить проверку, чтобы увидеть, равно ли значение пробела нулю или нет.

Это был бы один из способов использования его в вашем основном методе, чтобы ответить на ваш вопрос: «Как мне предотвратить ввод пользователем одной и той же координаты крестики-нолики?»

Cell userMove = new Cell(y, x);

if (grid.isMoveOK(userMove)) {
    grid.placeAMove(userMove, 2); // 2 for O and O is the user
} else {
    System.out.println("Please try a different space/cell");
    continue;
}

grid.displayBoard();
if (grid.gameOver())
    break;

Таким образом, я пропускаю оставшийся код цикла в вашем основном цикле метода, пока не появится допустимое открытое пространство. (Если есть, программа должна перейти к проверке выигрышных значений или продолжению игры)

Надеюсь, что это ответ на ваш вопрос! :)

Ваше здоровье

person Nick Bell    schedule 05.10.2016