Крестики - нолики AI: как сделать дерево?

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

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

4 ответов


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

причина, по которой вам нужно иметь дерево, заключается в том, что как только он построен, вам нужно выяснить, какая ветка имеет больше всего листьев, что это сценарии "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 )

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


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

Move getBestMove(Board state, boolean myTurn)

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

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

для пространства состояний, как мало, как крестики - нолики, однако, вы можете просто сделать полный поиск таблицы с лучшими ходами! :-)


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

решить крестики-нолики с алгоритмом минимакса

Это на C#, но это не будет проблемой, чтобы адаптировать его на C++.

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

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


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

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's children