Алгоритм определения окончания игры в крестики-нолики

Я написал игру в крестики-нолики на Java, и мой текущий метод определения конца игры учитывает следующие возможные сценарии завершения игры:

  1. Доска заполнена, и победитель еще не объявлен: ничья.
  2. Крест выиграл.
  3. Кружок выиграл.

К сожалению, для этого он считывает заранее определенный набор этих сценариев из таблицы. Это не обязательно плохо, учитывая, что на доске всего 9 мест, и поэтому таблица несколько мала, но есть ли лучший алгоритмический способ определить, закончилась ли игра? Определение того, выиграл кто-то или нет, является мясом проблемы, поскольку проверка того, заполнены ли 9 мест, является тривиальной.

Метод с таблицей может быть решением, но если нет, то что тогда? Также, что если бы доска не была размером n=9? Что если бы она была намного больше, скажем, n=16, n=25 и т.д., в результате чего количество последовательно расположенных элементов для выигрыша было бы x=4, x=5 и т.д.? Общий алгоритм, который можно использовать для всех n = { 9, 16, 25, 36 ... }?

Решение

Вы знаете, что выигрышный ход может произойти только после того, как X или O сделали свой последний ход, поэтому вы можете искать только в строке/столбце с необязательной диаграммой, которая содержится в этом ходе, чтобы ограничить пространство поиска при попытке определить выигрышную доску. Также, поскольку в ничейной игре крестики-нолики есть фиксированное количество ходов, как только сделан последний ход, если он не был выигрышным, то по умолчанию игра считается ничейной.

edit: этот код для доски n на n с n подряд для победы (доска 3x3 требует 3 подряд и т.д.)

edit: добавлен код для проверки анти-диаграммы, я не смог найти незацикленный способ определить, было ли очко на анти-диаграмме, поэтому этот шаг отсутствует.

public class TripleT {

    enum State{Blank, X, O};

    int n = 3;
    State[][] board = new State[n][n];
    int moveCount;

    void Move(int x, int y, State s){
        if(board[x][y] == State.Blank){
            board[x][y] = s;
        }
        moveCount++;

        //check end conditions

        //check col
        for(int i = 0; i < n; i++){
            if(board[x][i] != s)
                break;
            if(i == n-1){
                //report win for s
            }
        }

        //check row
        for(int i = 0; i < n; i++){
            if(board[i][y] != s)
                break;
            if(i == n-1){
                //report win for s
            }
        }

        //check diag
        if(x == y){
            //we're on a diagonal
            for(int i = 0; i < n; i++){
                if(board[i][i] != s)
                    break;
                if(i == n-1){
                    //report win for s
                }
            }
        }

        //check anti diag (thanks rampion)
        if(x + y == n - 1){
            for(int i = 0; i < n; i++){
                if(board[i][(n-1)-i] != s)
                    break;
                if(i == n-1){
                    //report win for s
                }
            }
        }

        //check draw
        if(moveCount == (Math.pow(n, 2) - 1)){
            //report draw
        }
    }
}
Комментарии (18)

можно использовать магический квадрат http://mathworld.wolfram.com/MagicSquare.html если в любом ряду, столбце или диаграмме в сумме получается 15, то игрок выиграл.

Комментарии (8)

Это похоже на Усама ALASSIRY'ы answerно он торгует постоянно-пространства и линейного времени, линейного пространства и постоянной времени. То есть, там'не зацикливание после инициализации.

Инициализировать пара (0,0) для каждой строки, каждого столбца и двух диагоналей (диагональ & анти-диагонали). Эти пары представляют собой накопленный `(сумма,сумма) из штук в соответствующую строку, столбец или диагональ, где в <предварительно> Кусок из игроков имеет значение (1,0) Кусок от игрока B имеет значение (0,1) </пред>

Когда игрок кладет кусочек, обновить соответствующую пару строк, пару колонны, а диагональных пар (если по диагонали). Если любые вновь обновленную строку, столбец или диагональ пары равна либо (н,0) или `(0,п), то либо A или B выиграл, соответственно.

Асимптотический анализ:

в <предварительно> За O(1) времени (на ход) О(н) площадь (общая) </пред>

Для использования памяти, вы используете 4*(П+1) целых чисел.

в <предварительно> two_elementsn_rows + two_elementsn_columns + two_elementstwo_diagonals = 4П + 4 чисел = 4(П+1) целых чисел </пред>

Упражнения: вы можете увидеть, как тест на ничью в O(1) времени на ход? Если это так, вы можете закончить игру рано на ничью.

Комментарии (1)

Как об этом псевдокоде:

После того, как игрок кладет кусок в позиции (Х,Y):

col=row=diag=rdiag=0
winner=false
for i=1 to n
  if cell[x,i]=player then col++
  if cell[i,y]=player then row++
  if cell[i,i]=player then diag++
  if cell[i,n-i+1]=player then rdiag++
if row=n or col=n or diag=n or rdiag=n then winner=true

Я'd использовать массив char [Н,Н], С О х и места для пустых.

  1. простой.
  2. Одной петле.
  3. Пять простых переменных: 4 целых и один логический.
  4. Масштабируется до любого размера Н.
  5. Проверяет только текущий кусок.
  6. Никакой магии. :)
Комментарии (3)

Вот мое решение, которое я писал для проекта Я'м работает на в JavaScript. Если вы Don'т ум расходов памяти несколько массивов это's наверное самый быстрый и простое решение, вы'll найти. Это предполагает, что вы знаете позицию последнего хода.

/*
 * Determines if the last move resulted in a win for either player
 * board: is an array representing the board
 * lastMove: is the boardIndex of the last (most recent) move
 *  these are the boardIndexes:
 *
 *   0 | 1 | 2
 *  ---+---+---
 *   3 | 4 | 5
 *  ---+---+---
 *   6 | 7 | 8
 * 
 * returns true if there was a win
 */
var winLines = [
    [[1, 2], [4, 8], [3, 6]],
    [[0, 2], [4, 7]],
    [[0, 1], [4, 6], [5, 8]],
    [[4, 5], [0, 6]],
    [[3, 5], [0, 8], [2, 6], [1, 7]],
    [[3, 4], [2, 8]],
    [[7, 8], [2, 4], [0, 3]],
    [[6, 8], [1, 4]],
    [[6, 7], [0, 4], [2, 5]]
];
function isWinningMove(board, lastMove) {
    var player = board[lastMove];
    for (var i = 0; i < winLines[lastMove].length; i++) {
        var line = winLines[lastMove][i];
        if(player === board[line[0]] && player === board[line[1]]) {
            return true;
        }
    }
    return false;
}
Комментарии (3)

Я просто написал это для моего класса C программирования.

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

Вы'll найти мой алгоритм, такой как она есть, в checkWinner()` функция. Это не'т использовать магию чисел или чего-нибудь особенного, чтобы проверить, не победитель, он просто использует четыре петли - код хорошо прокомментирован, поэтому я'МР Пусть говорят сами за себя я думаю.

// This program will work with any whole number sized rectangular gameBoard.
// It checks for N marks in straight lines (rows, columns, and diagonals).
// It is prettiest when ROWS and COLS are single digit numbers.
// Try altering the constants for ROWS, COLS, and N for great fun!    

// PPDs come first

    #include 
    #define ROWS 9              // The number of rows our gameBoard array will have
    #define COLS 9              // The number of columns of the same - Single digit numbers will be prettier!
    #define N 3                 // This is the number of contiguous marks a player must have to win
    #define INITCHAR ' '        // This changes the character displayed (a ' ' here probably looks the best)
    #define PLAYER1CHAR 'X'     // Some marks are more aesthetically pleasing than others
    #define PLAYER2CHAR 'O'     // Change these lines if you care to experiment with them

// Function prototypes are next

    int playGame    (char gameBoard[ROWS][COLS]);               // This function allows the game to be replayed easily, as desired
    void initBoard  (char gameBoard[ROWS][COLS]);               // Fills the ROWSxCOLS character array with the INITCHAR character
    void printBoard (char gameBoard[ROWS][COLS]);               // Prints out the current board, now with pretty formatting and #s!
    void makeMove   (char gameBoard[ROWS][COLS], int player);   // Prompts for (and validates!) a move and stores it into the array
    int checkWinner (char gameBoard[ROWS][COLS], int player);   // Checks the current state of the board to see if anyone has won

// The starting line
int main (void)
{
    // Inits
    char gameBoard[ROWS][COLS];     // Our gameBoard is declared as a character array, ROWS x COLS in size
    int winner = 0;
    char replay;

    //Code
    do                              // This loop plays through the game until the user elects not to
    {
        winner = playGame(gameBoard);
        printf("\nWould you like to play again? Y for yes, anything else exits: ");

        scanf("%c",&replay);      // I have to use both a scanf() and a getchar() in
        replay = getchar();         // order to clear the input buffer of a newline char
                                    // (http://cboard.cprogramming.com/c-programming/121190-problem-do-while-loop-char.html)

    } while ( replay == 'y' || replay == 'Y' );

    // Housekeeping
    printf("\n");
    return winner;
}

int playGame(char gameBoard[ROWS][COLS])
{
    int turn = 0, player = 0, winner = 0, i = 0;

    initBoard(gameBoard);

    do
    {
        turn++;                                 // Every time this loop executes, a unique turn is about to be made
        player = (turn+1)%2+1;                  // This mod function alternates the player variable between 1 & 2 each turn
        makeMove(gameBoard,player);
        printBoard(gameBoard);
        winner = checkWinner(gameBoard,player);

        if (winner != 0)
        {
            printBoard(gameBoard);

            for (i=0;i 0+(N-2); col--)            // Start from the last column and go until N columns from the first
    {                                                   // The math seems strange here but the numbers work out when you trace them
        for ( row = 0; row < (ROWS-(N-1)); row++)        // Start from the first row and go until N rows from the last
        {
            while (gameBoard[row][col] == currentChar)  // If the current player's character is there
            {
                row++;                                  // Go down a row
                col--;                                  // And back a column
                i++;                                    // The i variable tracks how many consecutive marks have been found
                if (i == N)                             // Once i == N
                {
                    return player;                      // Return the current player number to the
                }                                       // winnner variable in the playGame function
            }                                           // If it breaks out of the while loop, there weren't N consecutive marks
            i = 0;                                      // So make i = 0 again
        }                                               // And go back into the for loop, incrementing the row to check from
    }

    return 0;                                           // If we got to here, no winner has been detected,
}                                                       // so we pop back up into the playGame function

// The end!

// Well, almost.

// Eventually I hope to get this thing going
// with a dynamically sized array. I'll make
// the CONSTANTS into variables in an initGame
// function and allow the user to define them.
Комментарии (1)

Если доска n × n, то на ней n строк, n столбцов и 2 диагонали. Проверьте каждую из них на все-X или все-O, чтобы определить победителя.

Если для победы требуется только x < n последовательных квадратов, то это немного сложнее. Наиболее очевидным решением является проверка каждого x × x квадрата на наличие победителя. Вот код, который демонстрирует это.

(Я не тестировал это *cough*, но он скомпилировался с первой попытки, ура!)


public class TicTacToe
{
    public enum Square { X, O, NONE }

    /**
     * Returns the winning player, or NONE if the game has
     * finished without a winner, or null if the game is unfinished.
     */
    public Square findWinner(Square[][] board, int lengthToWin) {
        // Check each lengthToWin x lengthToWin board for a winner.    
        for (int top = 0; top 
Комментарии (3)

Я Дон'т знаете Java, но я не знаю C, поэтому я попытался АДК'магический квадрат idea (вместе с Hardwareguy'ы поиск restriction).

// tic-tac-toe.c
// to compile:
//  % gcc -o tic-tac-toe tic-tac-toe.c
// to run:
//  % ./tic-tac-toe
#include 

// the two types of marks available
typedef enum { Empty=2, X=0, O=1, NumMarks=2 } Mark;
char const MarkToChar[] = "XO ";

// a structure to hold the sums of each kind of mark
typedef struct { unsigned char of[NumMarks]; } Sum;

// a cell in the board, which has a particular value
#define MAGIC_NUMBER 15
typedef struct {
  Mark mark;
  unsigned char const value;
  size_t const num_sums;
  Sum * const sums[4];
} Cell;

#define NUM_ROWS 3
#define NUM_COLS 3

// create a sum for each possible tic-tac-toe
Sum row[NUM_ROWS] = {0};
Sum col[NUM_COLS] = {0};
Sum nw_diag = {0};
Sum ne_diag = {0};

// initialize the board values so any row, column, or diagonal adds to
// MAGIC_NUMBER, and so they each record their sums in the proper rows, columns,
// and diagonals
Cell board[NUM_ROWS][NUM_COLS] = { 
  { 
    { Empty, 8, 3, { &row[0], &col[0], &nw_diag } },
    { Empty, 1, 2, { &row[0], &col[1] } },
    { Empty, 6, 3, { &row[0], &col[2], &ne_diag } },
  },
  { 
    { Empty, 3, 2, { &row[1], &col[0] } },
    { Empty, 5, 4, { &row[1], &col[1], &nw_diag, &ne_diag } },
    { Empty, 7, 2, { &row[1], &col[2] } },
  },
  { 
    { Empty, 4, 3, { &row[2], &col[0], &ne_diag } },
    { Empty, 9, 2, { &row[2], &col[1] } },
    { Empty, 2, 3, { &row[2], &col[2], &nw_diag } },
  }
};

// print the board
void show_board(void)
{
  size_t r, c;
  for (r = 0; r < NUM_ROWS; r++) 
  {
    if (r > 0) { printf("---+---+---\n"); }
    for (c = 0; c < NUM_COLS; c++) 
    {
      if (c > 0) { printf("|"); }
      printf(" %c ", MarkToChar[board[r][c].mark]);
    }
    printf("\n");
  }
}

// run the game, asking the player for inputs for each side
int main(int argc, char * argv[])
{
  size_t m;
  show_board();
  printf("Enter moves as \" \" (no quotes, zero indexed)\n");
  for( m = 0; m < NUM_ROWS * NUM_COLS; m++ )
  {
    Mark const mark = (Mark) (m % NumMarks);
    size_t c, r;

    // read the player's move
    do
    {
      printf("%c's move: ", MarkToChar[mark]);
      fflush(stdout);
      scanf("%d %d", &r, &c);
      if (r >= NUM_ROWS || c >= NUM_COLS)
      {
        printf("illegal move (off the board), try again\n");
      }
      else if (board[r][c].mark != Empty)
      {
        printf("illegal move (already taken), try again\n");
      }
      else
      {
        break;
      }
    }
    while (1);

    {
      Cell * const cell = &(board[r][c]);
      size_t s;

      // update the board state
      cell->mark = mark;
      show_board();

      // check for tic-tac-toe
      for (s = 0; s < cell->num_sums; s++)
      {
        cell->sums[s]->of[mark] += cell->value;
        if (cell->sums[s]->of[mark] == MAGIC_NUMBER)
        {
          printf("tic-tac-toe! %c wins!\n", MarkToChar[mark]);
          goto done;
        }
      }
    }
  }
  printf("stalemate... nobody wins :(\n");
done:
  return 0;
}

Он компилируется и тесты хорошо. в <предварительно> % Оук -о крестики-нолики крестики-нолики.с % ./крестики-нолики | | ---+---+--- | | ---+---+--- | | Введите движется как " в<строка> <кол> наша" (без кавычек, ноль проиндексированных) Х's движение: 1 2 | | ---+---+--- | | Х ---+---+--- | | О's движение: 1 2 запрещенный прием (уже забрали), попробуйте снова О's движение: 3 3 незаконные перемещение (с доски), попробуйте еще раз О's движение: 2 2 | | ---+---+--- | | Х ---+---+--- | | О Х's движение: 1 0 | | ---+---+--- Х | | Х ---+---+--- | | О О's движение: 1 1 | | ---+---+--- Х | О | Х ---+---+--- | | О Х's движение: 0 0 Х | | ---+---+--- Х | О | Х ---+---+--- | | О О's движение: 2 0 Х | | ---+---+--- Х | О | Х ---+---+--- О | | О Х's движение: 2 1 Х | | ---+---+--- Х | О | Х ---+---+--- О | Х | О О's движение: 0 2 Х | | О ---+---+--- Х | О | Х ---+---+--- О | Х | О крестики-нолики! О'побед! % ./крестики-нолики | | ---+---+--- | | ---+---+--- | | Введите движется как " в<строка> <кол> наша" (без кавычек, ноль проиндексированных) Х's движение: 0 0 Х | | ---+---+--- | | ---+---+--- | | О's движение: 0 1 Х | О | ---+---+--- | | ---+---+--- | | Х's движение: 0 2 Х | О | Х ---+---+--- | | ---+---+--- | | О's движение: 1 0 Х | О | Х ---+---+--- О | | ---+---+--- | | Х's движение: 1 1 Х | О | Х ---+---+--- О | Х | ---+---+--- | | О's движение: 2 0 Х | О | Х ---+---+--- О | Х | ---+---+--- О | | Х's движение: 2 1 Х | О | Х ---+---+--- О | Х | ---+---+--- О | Х | О's движение: 2 2 Х | О | Х ---+---+--- О | Х | ---+---+--- О | Х | О Х's движение: 1 2 Х | О | Х ---+---+--- О | Х | Х ---+---+--- О | Х | О патовая ситуация... никто не выигрывает :( % </пред> Это было весело, спасибо!

На самом деле, думая об этом, вы не'т нужна магический квадрат, только в каждой строке/столбце/диагонали. Это немного легче, чем обобщающие магический квадрат Н &ампер;раз; - Н-матрицы, так как вы просто должны рассчитывать на "N".

Комментарии (0)

Я задала тот же вопрос в одном из своих интервью. Мои мысли: Инициализировать матрицы с 0. Держать 3 массивы 1)sum_row (размер Н) 2) sum_column (размера n) 3) диагонали (размер 2)

На каждый ход (х) уменьшить значение на 1, а на каждый шаг (0) увеличивает его на 1. В любой момент, если в строке/столбце/диагонали, который был изменен в текущее движение имеет сумму или -3 или +3 означает, что кто-то выиграл. На ничью мы можем использовать данный подход, чтобы сохранить moveCount переменной.

Вы думаете, что я что-то пропустил ?

Редактировать: так же можно использовать для матрицы n х N. Сумма должна быть даже +3 или -3.

Комментарии (0)

не петли способ определить, если речь шла о борьбе диаг:

`if (x + y == n - 1)`
Комментарии (0)

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

Возьмите этот магический квадрат:

4 9 2
3 5 7
8 1 6

Во-первых, создал массив баллы, который увеличивается каждый раз, когда движение осуществляется. Смотрите этот ответ для деталей. Теперь, если мы нелегально поиграть в X два раза подряд в [0,0] и [0,1], то массив баллы выглядит так:

[7, 0, 0, 4, 3, 0, 4, 0];

И совет выглядит так:

X . .
X . .
. . .

Затем, все, что нам нужно для того, чтобы получить ссылку на какой площади, чтобы выиграть/блок на это:

get_winning_move = function() {
  for (var i = 0, i < scores.length; i++) {
    // keep track of the number of times pieces were added to the row
    // subtract when the opposite team adds a piece
    if (scores[i].inc === 2) {
      return 15 - state[i].val; // 8
    }
  }
}

В реальности, реализация требует несколько дополнительных уловок, как обработка клавиш с цифрами (в JavaScript), но я нашел его довольно проста и заняться рекреационной математикой.

Комментарии (0)

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

Вот Java-код для этого.

    int gameState(int values[][], int boardSz) {

    boolean colCheckNotRequired[] = new boolean[boardSz];//default is false
    boolean diag1CheckNotRequired = false;
    boolean diag2CheckNotRequired = false;
    boolean allFilled = true;

    int x_count = 0;
    int o_count = 0;
    /* Check rows */
    for (int i = 0; i < boardSz; i++) {
        x_count = o_count = 0;
        for (int j = 0; j < boardSz; j++) {
            if(values[i][j] == x_val)x_count++;
            if(values[i][j] == o_val)o_count++;
            if(values[i][j] == 0)
            {
                colCheckNotRequired[j] = true;
                if(i==j)diag1CheckNotRequired = true;
                if(i + j == boardSz - 1)diag2CheckNotRequired = true;
                allFilled = false;
                //No need check further
                break;
            }
        }
        if(x_count == boardSz)return X_WIN;
        if(o_count == boardSz)return O_WIN;         
    }

    /* check cols */
    for (int i = 0; i < boardSz; i++) {
        x_count = o_count = 0;
        if(colCheckNotRequired[i] == false)
        {
            for (int j = 0; j < boardSz; j++) {
                if(values[j][i] == x_val)x_count++;
                if(values[j][i] == o_val)o_count++;
                //No need check further
                if(values[i][j] == 0)break;
            }
            if(x_count == boardSz)return X_WIN;
            if(o_count == boardSz)return O_WIN;
        }
    }

    x_count = o_count = 0;
    /* check diagonal 1 */
    if(diag1CheckNotRequired == false)
    {
        for (int i = 0; i < boardSz; i++) {
            if(values[i][i] == x_val)x_count++;
            if(values[i][i] == o_val)o_count++;
            if(values[i][i] == 0)break;
        }
        if(x_count == boardSz)return X_WIN;
        if(o_count == boardSz)return O_WIN;
    }

    x_count = o_count = 0;
    /* check diagonal 2 */
    if( diag2CheckNotRequired == false)
    {
        for (int i = boardSz - 1,j = 0; i >= 0 && j < boardSz; i--,j++) {
            if(values[j][i] == x_val)x_count++;
            if(values[j][i] == o_val)o_count++;
            if(values[j][i] == 0)break;
        }
        if(x_count == boardSz)return X_WIN;
        if(o_count == boardSz)return O_WIN;
        x_count = o_count = 0;
    }

    if( allFilled == true)
    {
        for (int i = 0; i < boardSz; i++) {
            for (int j = 0; j < boardSz; j++) {
                if (values[i][j] == 0) {
                    allFilled = false;
                    break;
                }
            }

            if (allFilled == false) {
                break;
            }
        }
    }

    if (allFilled)
        return DRAW;

    return INPROGRESS;
}
Комментарии (0)

Мне нравится этот алгоритм, как он использует 1x9 представление против 3х3 совета.

private int[] board = new int[9];
private static final int[] START = new int[] { 0, 3, 6, 0, 1, 2, 0, 2 };
private static final int[] INCR  = new int[] { 1, 1, 1, 3, 3, 3, 4, 2 };
private static int SIZE = 3;
/**
 * Determines if there is a winner in tic-tac-toe board.
 * @return {@code 0} for draw, {@code 1} for 'X', {@code -1} for 'Y'
 */
public int hasWinner() {
    for (int i = 0; i < START.length; i++) {
        int sum = 0;
        for (int j = 0; j < SIZE; j++) {
            sum += board[START[i] + j * INCR[i]];
        }
        if (Math.abs(sum) == SIZE) {
            return sum / SIZE;
        }
    }
    return 0;
}
Комментарии (2)

Вот мое решение с помощью 2-мерного массива:

private static final int dimension = 3;
private static final int[][] board = new int[dimension][dimension];
private static final int xwins = dimension * 1;
private static final int owins = dimension * -1;

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    int count = 0;
    boolean keepPlaying = true;
    boolean xsTurn = true;
    while (keepPlaying) {
        xsTurn = (count % 2 == 0);
        System.out.print("Enter i-j in the format:");
        if (xsTurn) {
            System.out.println(" X plays: ");
        } else {
            System.out.println(" O plays: ");
        }
        String result = null;
        while (result == null) {
            result = parseInput(scanner, xsTurn);
        }
        String[] xy = result.split(",");
        int x = Integer.parseInt(xy[0]);
        int y = Integer.parseInt(xy[1]);
        keepPlaying = makeMove(xsTurn, x, y);
        count++;
    }
    if (xsTurn) {
        System.out.print("X");
    } else {
        System.out.print("O");
    }
    System.out.println(" WON");
    printArrayBoard(board);
}

private static String parseInput(Scanner scanner, boolean xsTurn) {
    String line = scanner.nextLine();
    String[] values = line.split("-");
    int x = Integer.parseInt(values[0]);
    int y = Integer.parseInt(values[1]);
    boolean alreadyPlayed = alreadyPlayed(x, y);
    String result = null;
    if (alreadyPlayed) {
        System.out.println("Already played in this x-y. Retry");
    } else {
        result = "" + x + "," + y;
    }
    return result;
}

private static boolean alreadyPlayed(int x, int y) {
    System.out.println("x-y: " + x + "-" + y + " board[x][y]: " + board[x][y]);
    if (board[x][y] != 0) {
        return true;
    }
    return false;
}

private static void printArrayBoard(int[][] board) {
    for (int i = 0; i < dimension; i++) {
        int[] height = board[i];
        for (int j = 0; j < dimension; j++) {
            System.out.print(height[j] + " ");
        }
        System.out.println();
    }
}

private static boolean makeMove(boolean xo, int x, int y) {
    if (xo) {
        board[x][y] = 1;
    } else {
        board[x][y] = -1;
    }
    boolean didWin = checkBoard();
    if (didWin) {
        System.out.println("keep playing");
    }
    return didWin;
}

private static boolean checkBoard() {
    //check horizontal
    int[] horizontalTotal = new int[dimension];
    for (int i = 0; i < dimension; i++) {
        int[] height = board[i];
        int total = 0;
        for (int j = 0; j < dimension; j++) {
            total += height[j];
        }
        horizontalTotal[i] = total;
    }
    for (int a = 0; a < horizontalTotal.length; a++) {
        if (horizontalTotal[a] == xwins || horizontalTotal[a] == owins) {
            System.out.println("horizontal");
            return false;
        }
    }
    //check vertical
    int[] verticalTotal = new int[dimension];

    for (int j = 0; j < dimension; j++) {
        int total = 0;
        for (int i = 0; i < dimension; i++) {
            total += board[i][j];
        }
        verticalTotal[j] = total;
    }
    for (int a = 0; a < verticalTotal.length; a++) {
        if (verticalTotal[a] == xwins || verticalTotal[a] == owins) {
            System.out.println("vertical");
            return false;
        }
    }
    //check diagonal
    int total1 = 0;
    int total2 = 0;
    for (int i = 0; i < dimension; i++) {
        for (int j = 0; j < dimension; j++) {
            if (i == j) {
                total1 += board[i][j];
            }
            if (i == (dimension - 1 - j)) {
                total2 += board[i][j];
            }
        }
    }
    if (total1 == xwins || total1 == owins) {
        System.out.println("diagonal 1");
        return false;
    }
    if (total2 == xwins || total2 == owins) {
        System.out.println("diagonal 2");
        return false;
    }
    return true;
}
Комментарии (0)

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

def spin(g): return set([g, turn(g), turn(turn(g)), turn(turn(turn(g)))])
def turn(g): return tuple(tuple(g[y][x] for y in (0,1,2)) for x in (2,1,0))

X,s = 'X.'
XXX = X, X, X
sss = s, s, s

ways_to_win = (  spin((XXX, sss, sss))
               | spin((sss, XXX, sss))
               | spin(((X,s,s),
                       (s,X,s),
                       (s,s,X))))

Эти симметрии могут иметь еще используются в ваш игровой код: если вы попадете на доске вы've уже видел повернутую версию, вы можете просто взять кэшированное значение или кэшированные лучше двигаться от одной (и исправить разворот обратно). Это обычно намного быстрее, чем оценки игры поддерева.

(Листать влево и вправо, может помочь таким же образом; это было'т нужна здесь, потому что набор вращений выигрышные узоры зеркально-симметричной.)

Комментарии (0)

Вот решение я придумал, это хранит символы в качестве символов и использует тип char'ы int значение, чтобы выяснить, если X или O выиграл (Посмотреть карточку's код)

public class TicTacToe {
    public static final char BLANK = '\u0000';
    private final char[][] board;
    private int moveCount;
    private Referee referee;

    public TicTacToe(int gridSize) {
        if (gridSize < 3)
            throw new IllegalArgumentException("TicTacToe board size has to be minimum 3x3 grid");
        board = new char[gridSize][gridSize];
        referee = new Referee(gridSize);
    }

    public char[][] displayBoard() {
        return board.clone();
    }

    public String move(int x, int y) {
        if (board[x][y] != BLANK)
            return "(" + x + "," + y + ") is already occupied";
        board[x][y] = whoseTurn();
        return referee.isGameOver(x, y, board[x][y], ++moveCount);
    }

    private char whoseTurn() {
        return moveCount % 2 == 0 ? 'X' : 'O';
    }

    private class Referee {
        private static final int NO_OF_DIAGONALS = 2;
        private static final int MINOR = 1;
        private static final int PRINCIPAL = 0;
        private final int gridSize;
        private final int[] rowTotal;
        private final int[] colTotal;
        private final int[] diagonalTotal;

        private Referee(int size) {
            gridSize = size;
            rowTotal = new int[size];
            colTotal = new int[size];
            diagonalTotal = new int[NO_OF_DIAGONALS];
        }

        private String isGameOver(int x, int y, char symbol, int moveCount) {
            if (isWinningMove(x, y, symbol))
                return symbol + " won the game!";
            if (isBoardCompletelyFilled(moveCount))
                return "Its a Draw!";
            return "continue";
        }

        private boolean isBoardCompletelyFilled(int moveCount) {
            return moveCount == gridSize * gridSize;
        }

        private boolean isWinningMove(int x, int y, char symbol) {
            if (isPrincipalDiagonal(x, y) && allSymbolsMatch(symbol, diagonalTotal, PRINCIPAL))
                return true;
            if (isMinorDiagonal(x, y) && allSymbolsMatch(symbol, diagonalTotal, MINOR))
                return true;
            return allSymbolsMatch(symbol, rowTotal, x) || allSymbolsMatch(symbol, colTotal, y);
        }

        private boolean allSymbolsMatch(char symbol, int[] total, int index) {
            total[index] += symbol;
            return total[index] / gridSize == symbol;
        }

        private boolean isPrincipalDiagonal(int x, int y) {
            return x == y;
        }

        private boolean isMinorDiagonal(int x, int y) {
            return x + y == gridSize - 1;
        }
    }
}

Также Вот мой модульные тесты, чтобы на самом деле проверить это работает

import static com.agilefaqs.tdd.demo.TicTacToe.BLANK;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class TicTacToeTest {
    private TicTacToe game = new TicTacToe(3);

    @Test
    public void allCellsAreEmptyInANewGame() {
        assertBoardIs(new char[][] { { BLANK, BLANK, BLANK },
                { BLANK, BLANK, BLANK },
                { BLANK, BLANK, BLANK } });
    }

    @Test(expected = IllegalArgumentException.class)
    public void boardHasToBeMinimum3x3Grid() {
        new TicTacToe(2);
    }

    @Test
    public void firstPlayersMoveMarks_X_OnTheBoard() {
        assertEquals("continue", game.move(1, 1));
        assertBoardIs(new char[][] { { BLANK, BLANK, BLANK },
                { BLANK, 'X', BLANK },
                { BLANK, BLANK, BLANK } });
    }

    @Test
    public void secondPlayersMoveMarks_O_OnTheBoard() {
        game.move(1, 1);
        assertEquals("continue", game.move(2, 2));
        assertBoardIs(new char[][] { { BLANK, BLANK, BLANK },
                { BLANK, 'X', BLANK },
                { BLANK, BLANK, 'O' } });
    }

    @Test
    public void playerCanOnlyMoveToAnEmptyCell() {
        game.move(1, 1);
        assertEquals("(1,1) is already occupied", game.move(1, 1));
    }

    @Test
    public void firstPlayerWithAllSymbolsInOneRowWins() {
        game.move(0, 0);
        game.move(1, 0);
        game.move(0, 1);
        game.move(2, 1);
        assertEquals("X won the game!", game.move(0, 2));
    }

    @Test
    public void firstPlayerWithAllSymbolsInOneColumnWins() {
        game.move(1, 1);
        game.move(0, 0);
        game.move(2, 1);
        game.move(1, 0);
        game.move(2, 2);
        assertEquals("O won the game!", game.move(2, 0));
    }

    @Test
    public void firstPlayerWithAllSymbolsInPrincipalDiagonalWins() {
        game.move(0, 0);
        game.move(1, 0);
        game.move(1, 1);
        game.move(2, 1);
        assertEquals("X won the game!", game.move(2, 2));
    }

    @Test
    public void firstPlayerWithAllSymbolsInMinorDiagonalWins() {
        game.move(0, 2);
        game.move(1, 0);
        game.move(1, 1);
        game.move(2, 1);
        assertEquals("X won the game!", game.move(2, 0));
    }

    @Test
    public void whenAllCellsAreFilledTheGameIsADraw() {
        game.move(0, 2);
        game.move(1, 1);
        game.move(1, 0);
        game.move(2, 1);
        game.move(2, 2);
        game.move(0, 0);
        game.move(0, 1);
        game.move(1, 2);
        assertEquals("Its a Draw!", game.move(2, 0));
    }

    private void assertBoardIs(char[][] expectedBoard) {
        assertArrayEquals(expectedBoard, game.displayBoard());
    }
}

Полное решение: https://github.com/nashjain/tictactoe/tree/master/java

Комментарии (0)

Как насчет следующего подхода по 9 слотов? Объявить 9 целочисленные переменные для матрицы 3х3 (А1,А2....А9), где А1,А2,А3 представляют собой строки-1 и А1,А4,А7 будет колонна-1 Форма (вы получаете идею). Использовать '1' чтобы указать игрока-1 и '2', чтобы указать игроку-2.

Существует 8 возможных комбинаций выигрыша: Победа-1: А1+А2+А3 (ответ может быть 3 или 6, на основе которых игрок выиграл) Победа-2: А4+А5+А6 Победа-3: А7+А8+А9 Победа-4: А1+А4+А7 .... Победы-7: А1+А5+А9 Победа-8: А3+А5+А7

Теперь мы знаем, что если игрок переходит А1, то мы должны пересмотреть сумму 3 переменных: Победа-1, победа-4 и Win-7. Какой Бы 'Победа-?' переменные достигает 3 или 6 первый выигрывает игру. Если победит-1 переменная достигает 6 первого, то игрок 2 выигрывает.

Я понимаю, что это решение не легко масштабируется.

Комментарии (0)

Это очень простой способ проверить.


    public class Game() { 

    Game player1 = new Game('x');
    Game player2 = new Game('o');

    char piece;

    Game(char piece) {
       this.piece = piece;
    }

public void checkWin(Game player) {

    // check horizontal win
    for (int i = 0; i 
Комментарии (0)

Если у вас есть сноуборд поле 5*5 например, я использовал следующий метод проверки:


public static boolean checkWin(char symb) {
  int SIZE = 5;

        for (int i = 0; i < SIZE-1; i++) {
            for (int j = 0; j 
Комментарии (0)

Постоянное время O(8), в среднем 4 коротких и'ов. Плеер = короткий номер. Требует дополнительной проверки для убедившись, что движение является действительным.


// O(8)
boolean isWinner(short X) {
    for (int i = 0; i < 8; i++)
        if ((X & winCombinations[i]) == winCombinations[i])
            return true;
    return false;
}

short[] winCombinations = new short[]{
  7, 7 
Комментарии (1)