Алгоритм определения окончания игры в крестики-нолики
Я написал игру в крестики-нолики на Java, и мой текущий метод определения конца игры учитывает следующие возможные сценарии завершения игры:
- Доска заполнена, и победитель еще не объявлен: ничья.
- Крест выиграл.
- Кружок выиграл.
К сожалению, для этого он считывает заранее определенный набор этих сценариев из таблицы. Это не обязательно плохо, учитывая, что на доске всего 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: добавлен код для проверки анти-диаграммы, я не смог найти незацикленный способ определить, было ли очко на анти-диаграмме, поэтому этот шаг отсутствует.
можно использовать магический квадрат http://mathworld.wolfram.com/MagicSquare.html если в любом ряду, столбце или диаграмме в сумме получается 15, то игрок выиграл.
Это похоже на Усама 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) времени на ход? Если это так, вы можете закончить игру рано на ничью.
Как об этом псевдокоде:
После того, как игрок кладет кусок в позиции (Х,Y):
Я'd использовать массив char [Н,Н], С О х и места для пустых.
Вот мое решение, которое я писал для проекта Я'м работает на в JavaScript. Если вы Don'т ум расходов памяти несколько массивов это's наверное самый быстрый и простое решение, вы'll найти. Это предполагает, что вы знаете позицию последнего хода.
Я просто написал это для моего класса C программирования.
Я отправляю это, потому что ни один из других примеров здесь будет работать с любого размера прямоугольный сетки, и любое количество Н-в-ряд последовательных знаков, чтобы выиграть.
Вы'll найти мой алгоритм, такой как она есть, в checkWinner()` функция. Это не'т использовать магию чисел или чего-нибудь особенного, чтобы проверить, не победитель, он просто использует четыре петли - код хорошо прокомментирован, поэтому я'МР Пусть говорят сами за себя я думаю.
Если доска n × n, то на ней n строк, n столбцов и 2 диагонали. Проверьте каждую из них на все-X или все-O, чтобы определить победителя.
Если для победы требуется только x < n последовательных квадратов, то это немного сложнее. Наиболее очевидным решением является проверка каждого x × x квадрата на наличие победителя. Вот код, который демонстрирует это.
(Я не тестировал это *cough*, но он скомпилировался с первой попытки, ура!)
Я Дон'т знаете Java, но я не знаю C, поэтому я попытался АДК'магический квадрат idea (вместе с Hardwareguy'ы поиск restriction).
Он компилируется и тесты хорошо. в <предварительно> % Оук -о крестики-нолики крестики-нолики.с % ./крестики-нолики | | ---+---+--- | | ---+---+--- | | Введите движется как " в<строка> <кол> наша" (без кавычек, ноль проиндексированных) Х'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. Держать 3 массивы 1)sum_row (размер Н) 2) sum_column (размера n) 3) диагонали (размер 2)
На каждый ход (х) уменьшить значение на 1, а на каждый шаг (0) увеличивает его на 1. В любой момент, если в строке/столбце/диагонали, который был изменен в текущее движение имеет сумму или -3 или +3 означает, что кто-то выиграл. На ничью мы можем использовать данный подход, чтобы сохранить moveCount переменной.
Вы думаете, что я что-то пропустил ?
Редактировать: так же можно использовать для матрицы n х N. Сумма должна быть даже +3 или -3.
не петли способ определить, если речь шла о борьбе диаг:
Я опаздываю на вечеринку, но я хотел обратить внимание на одно преимущество, которое я нашел, чтобы использовать магический квадрат, а именно, что он может быть использован, чтобы получить ссылку на площадь, что бы вызвать выигрыша или проигрыша на следующий ход, а не просто используется, чтобы вычислить, когда игра окончена.
Возьмите этот магический квадрат:
Во-первых, создал массив
баллы
, который увеличивается каждый раз, когда движение осуществляется. Смотрите этот ответ для деталей. Теперь, если мы нелегально поиграть в X два раза подряд в [0,0] и [0,1], то массивбаллы
выглядит так:И совет выглядит так:
Затем, все, что нам нужно для того, чтобы получить ссылку на какой площади, чтобы выиграть/блок на это:
В реальности, реализация требует несколько дополнительных уловок, как обработка клавиш с цифрами (в JavaScript), но я нашел его довольно проста и заняться рекреационной математикой.
Я сделал некоторые оптимизации в строке, коль, проверяет диагонали. Ее в основном решили в первый вложенный цикл, если нужно проверить конкретный столбец или диагональ. Таким образом, мы избежим проверки столбцов или диагоналей экономия времени. Это делает большое влияние, когда размер платы больше и значительное количество клеток не заполнены.
Вот Java-код для этого.
Мне нравится этот алгоритм, как он использует 1x9 представление против 3х3 совета.
Вот мое решение с помощью 2-мерного массива:
Другой вариант: создаем таблицу с кодом. До симметрии, есть только три пути к победе: пограничный ряд, средний ряд или по диагонали. Взять те три и вращать их вокруг всеми возможными способами:
Эти симметрии могут иметь еще используются в ваш игровой код: если вы попадете на доске вы've уже видел повернутую версию, вы можете просто взять кэшированное значение или кэшированные лучше двигаться от одной (и исправить разворот обратно). Это обычно намного быстрее, чем оценки игры поддерева.
(Листать влево и вправо, может помочь таким же образом; это было'т нужна здесь, потому что набор вращений выигрышные узоры зеркально-симметричной.)
Вот решение я придумал, это хранит символы в качестве символов и использует тип char'ы int значение, чтобы выяснить, если X или O выиграл (Посмотреть карточку's код)
Также Вот мой модульные тесты, чтобы на самом деле проверить это работает
Полное решение: https://github.com/nashjain/tictactoe/tree/master/java
Как насчет следующего подхода по 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 выигрывает.
Я понимаю, что это решение не легко масштабируется.
Это очень простой способ проверить.
Если у вас есть сноуборд поле 5*5 например, я использовал следующий метод проверки:
Постоянное время O(8), в среднем 4 коротких и'ов. Плеер = короткий номер. Требует дополнительной проверки для убедившись, что движение является действительным.