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

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

В нашей последней версии →V0.4 ← мы создали алгоритм, но он еще не закончен. Сейчас наш miniMax() возвращает console.log, давайте изменим его, заставив возвращать число, и посмотрим, что мы можем делать с этим.

Первое, что нужно сделать, это понять логику нашего алгоритма, поэтому мы возвращаемся к нашему псевдокоду.

Итак, мы создаем все возможные состояния с помощью нашей функции рекурсии, а затем всякий раз, когда мы находим конечное состояние, мы присваиваем ему значение. Скажем, например, у нас есть 4 состояния, 2 состояния возвращают 10, 1 состояние возвращает 0, а последнее состояние возвращает -10. . Когда наступит очередь компьютера, из всех 4 состояний он выберет то, которое возвращает -10. Таким образом, компьютер каждый раз выбирает, где ставить «О».

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

Совет: Инкапсуляция (каждый объект отвечает за определенные задачи).

Пришло время использовать некоторую инкапсуляцию и создать функцию, которая увеличивает .oMovesCount каждый раз, когда мы создаем состояние из предыдущего состояния. Мы помещаем его в переменную globals.

Поскольку теперь мы создаем новый State() каждый раз, когда мы вызываем .applyTo, нам нужен способ обновления нашего GameManager .currentState(), чтобы мы могли отслеживать текущее состояние, в котором мы находимся.

Итак, теперь каждый раз, когда мы вызываем .advanceTo, он будет брать любое состояние State(), которое у нас есть, и назначать его this.currentState.

Давайте посмотрим, как это работает в нашей функции takeABlindMove().

Мы возвращаемся к нашей функции рекурсии и обновляем .map и базовый вариант.

Объяснение: всякий раз, когда мы достигаем конечного состояния доски, мы возвращаем число. Итак, теперь у нас есть функция, которая создает все возможные перестановки доски и возвращает число, указывающее значение каждого состояния, и все это зависит от .oMovesCount.

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

Впервые у нас есть готовая версия алгоритма. Первое, что мы объясним, это то, почему мы имеем return stateScore внутри нашей рекурсивной функции. До сих пор мы видели это в нашей В базовом случае мы возвращаем число, так почему возвращает stateScore? Объяснение простое: мы используем наш базовый вариант для создания числа nextScore, которое мы используем для сравнения с исходным StateScore,

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

Совет: помните, что miniMax создает все возможные состояния нашей доски, начиная с уже заполненной доски (состояния), поэтому именно state.turn определяет сортировку между stateScore и nextScore.

Пример: мы вызываем miniMax(_state) в состоянии, в котором ему осталось пройти только 1 глубину, пока оно не заполнит доску (осталось 2 состояния). что одно состояние возвращает 10 — _state.oMovesCount = -20, а другое возвращает 20. Если наш начальный miniMax был вызван в состоянии с поворотом «X», он вернет -20 (поскольку для достижения этого состояния потребовалось 30 .oMovesCount). государство).

Теперь сортировка, мы создаем 2 статические функции и присоединяем их к AIAction():

Теперь, когда у нас есть все части, пришло время построить наш takeAMasterMove().

Используем console.log для лучшего понимания, а затем вызываем функцию

Если мы посмотрим в консоль, то увидим, что в первый раз она возвращает массив из 8 объектов, каждый из которых имеет значение minMaxValue. Теперь, прошу вас, скажите мне, какой штат мы выберем? Если вы действительно понимаете алгоритм, вы будете знать ответ.

ВНИМАНИЕ, СПОЙЛЕР

Теперь мы обновляем наш MasterMove()

Последнее, что мы хотим сделать, это создать переключатель в уведомлении и дать игроку возможность играть против компьютера-новичка или эксперта.

Мы создаем 2 элемента кнопки (мы используем bootstrap :)

Мы обновляем наш ИИ()

Мы также обновляем наше стартовое событие.

Теперь нам просто нужно присвоить класс difficultyLevel элементу, на который нажали.

Полное кодовое перо: → V0.5

Дополнительная функция: причина, по которой мы создаем .DESCENDING и .ASCENDING, заключается в том, чтобы компьютер мог воспроизводить символы «X» или «O». Я не буду показывать, как им пользоваться, попробуйте сделать это сами, кроме того, Free Code Camp попрошу вас сыграть за X или O, примите это как окончательный выпускной. экзамен!

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