Tic-Tac-Toe AI: Как сделать дерево?

У меня есть огромный блок, который пытается понять "деревья" при создании бота Tic-Tac-Toe. Я понимаю эту концепцию, но я не могу ее реализовать.

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

Ответ 1

Представьте себе, что в любой точке платы tic-tac-toe каждый возможный ход - это ветвь. Текущее состояние платы - это корень. Один шаг - это ветка. Теперь притворяйтесь (по одному за раз), что каждая ветвь становится текущим состоянием. Каждый возможный ход становится новой ветвью. Лист дерева - это когда последний шаг сделан, и доска заполнена.

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

Сделайте дерево примерно таким:

class Node {
public:
   std::list< Node > m_branches;
   BoardState m_board;
   int m_winCount;
}

std::list< Node > tree;

Теперь вы перебираете список ветвей в дереве и для каждой ветки, итерации по ее ветвям. Это можно сделать с помощью рекурсивной функции:

int recursiveTreeWalk( std::list< Node >& partialTree)
{

   for each branch in tree
       if node has no branches
           calculate win 1/0;
       else
           recursiveTreeWalk( branch );

   partialTree.m_winCount = sum of branch wins;
}

// initial call
recursiveTreeWalk( tree )

Очень псевдокод.

Ответ 2

Я не думаю, что вам нужно сохранить дерево в памяти. Вам просто нужно реализовать рекурсивную функцию, которая работает примерно так:

Move getBestMove(Board state, boolean myTurn)

Затем вы просто рекурсируете, пока не достигнете выигрыша, проигрыша или ничьей.

Стек вызовов со временем будет выглядеть как дерево, если вы нарисуете его на бумаге. Вы должны вернуть ход, который ведет к node, при котором противник (определенно/скорее всего) теряет (даже если он также играет с использованием getBestMove)

Для пространства состояний всего лишь tic-tac-toe, вы можете просто сделать полный стол для просмотра с лучшими ходами!: -)

Ответ 3

Вы можете найти эту статью codeproject интересной:

Решите крестики-нолики с помощью алгоритма MiniMax

Это в С#, но адаптировать его в C++ не составит труда.

Эта статья была также хорошо прочитана для меня, когда я попытался реализовать свою первую игру Tic-Tac-Toe в C++:

Минимакс объяснил

Ответ 4

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

GenTree(State s):
  T <- empty tree  // T is a tree of States
  SetRoot(T, s)

  ForEach (s' in Successors(s)):
    AddChild(T, GenTree(s'))

  return T

// Call it
GenTree(currentMove)

где

Successors(s)  // returns a list of successor states of s
AddChild(p, n)  // adds n to the list of p children

Ответ 5

Реализация игры Tic Tac Toe, вероятно, является самой простой проблемой с точки зрения ИИ и пространства поиска.

Ключевым является подход к проблеме с минимаксом, итеративным углублением поиска в глубину и алгоритмами отсечения альфа-бета.

Вот моя реализация игры на Python, которая содержит всего ~ 200 строк кода и способна играть в игру " Human vs. Human, " Human vs. Computer и " Computer vs. Computer. Он также хранит статистику по глубинам и количеству достигнутых/сокращенных узлов, что приводит к лучшему перемещению.

Я настоятельно рекомендую edX.org курс по искусственному интеллекту, который дает фундаментальные знания по актуальным темам и решениям ИИ.