Цитата (БиллиУбили @ 4.6.2020)
Странное всё-таки решение в этих крестиках-ноликах.
Те позиции, в которых определился исход, сходу получают EV узла = -10000 очков, а остальные позиции доигрываются 500 раз по рандому (с +3 очка за победу и -1 за поражение).
Не, я конечно всё понимаю...но что таким образом экономится?
Это что, заградительный барьер? Чтобы узел "-10000" с 1 посещением никогда не стал привлекательнее узла "-300" с 300 посещениями? С таким же успехом достаточно давать "-500" и 1 посещение, там же в знаменателе дроби число посещений узла.
Кто прав: я или автор кода?
ну давай гляну код , пяток минут потрачу
сразу бросается в глаза что код нифига не доделан :
вот вызов монтекарлы
50к максимально симуляций используется
и погнали попадает сначала сюда , а потом сюда
GetTopActions создает дерево , корень его является ТЕКУЩЕЕ СОСТОЯНИЕ +ХОД .... понимаешь? т.е. решается не сначала от текущего состояния ... но в китайском тебе надо все дерево будет держать ) чтобы решение иметь на любую ситуацию )
поэтому разные деревья для разных сдач ...
вообще рассматривай дерево монте карло как способ хранения информации ... а обход дерева это алгоритм решения задачи ....
но это возможно не верно , но в моем представлении оно так
короче дерево создали строчкой var root = new Node<TPlayer, TAction>(state)
root.buildtree(())
и возвращаем return root.Children
.OrderByDexcending()
вот и весь алгоритм)
теперь процесс создание дерева :
ну там ничего нового корень добавляется + неиспользованные действия (ходы):
на примере все ходы нолика доступные - покроют все выходы из этой ноды
далее создание дерева
выбор хода -чилдов :
выбираем если все ходы уже в дереве :
!node.UntriedActons.Any неиспользованных ходов нету
и ход не последний в игре , если сделали последний игра завершена выбирать нечего :
node.Actions.Any()
далее
условие пока у нас есть неиспользованные ходы мы добавляем ноды ...
node.UntriedActions.Any()
и доигрываем по стратегии случайной:
while (state.Actions.Any())
state.ApplyAction(state.Actions.RandomChoice());
а далее backpropagate
while (node != null)
{
node.NumRuns++;
node.NumWins += state.GetResult(this.Player);
node = node.Parent;
}
мы с последней ноды записываем результат в каждую ноду до корня , которую посетили
NumWins += state.getresult(this.player)
теперь самое интересное выбор хода
node.selectChild
ну выбор у них по UCT идет
т.е. по той формуле argmax UCTvalue
по этим формулам :
БиллиУбили, по ев ничем , справа евшка до сотых равная
для примера чтобы разобраться как это устроено и работает хватит