Этот вопрос больше относится к вопросу о семантической алгоритмической структуре данных, чем к синтаксическому вопросу F #. У меня есть алгоритм Minimax. Алгоритм минимакса должен возвращать лучший следующий ход из начальной позиции. Для этого он вычисляет все следующие ходы, а затем следующие-следующие ходы до определенной глубины или до тех пор, пока больше ходов не останется. Он строит такое дерево:
P
/ \
a b
/ \
c d
У меня есть следующая структура данных для обработки дерева:
type TreeOfPosition =
| LeafP of Position * int
| BranchP of Position * TreeOfPosition list
В приведенном выше примере дерева P
и a
- это ветви, а b
, c
и d
- листья. Код ниже - мой алгоритм минимакса:
let evaluateTree ( tree : TreeOfPosition, player : int) =
let rec loop minOrmax node =
match node with
| LeafP(position, 0) ->
LeafP(position, evaluateLeaf(position))
| BranchP(position, children) ->
minimax.[minOrmax](List.map (loop (1 - minOrmax)) children)
loop player tree
Этот код возвращает мне лист, например, c
. Когда я изменил вызов рекурсии на
| BranchP(position, children) ->
LeafP(position,
getStaticEvalFromNode(minimax.[minOrmax](
List.map (loop (1 - minOrmax)) children)))
И эта модификация увеличивает статическую ценность хорошего листа. Мне нужно вернуть лучший узел второго уровня. Надеюсь, кто-нибудь сможет помочь! Педро Дуссо
ИЗМЕНИТЬ 1
Спасибо за все ответы, ребята, они мне очень помогают. Извините за то, что не особо указал на вещи. Пойдем по частям:
1) Я сопоставляю свой LeafP как LeafP(position, 0)
, потому что, когда я создаю свое дерево, я устанавливаю для листьев значение по умолчанию 0 в качестве его статического значения. Когда я поднимаю свои статические значения, удаляя лист и создавая (перед ветвями) листы с (минимальными или максимальными) статическими значениями, я думал, что таким образом я не смогу оценить лист бывшего ветвления (потому что он не будет иметь значение 0).
2) Моя самая большая проблема заключалась в том, чтобы вернуть лучшую позицию второго уровня (следующий ход, который нужно сыграть). Я решил это так:
let evaluateTreeHOF ( tree, player : int) =
let rec loop minOrmax node =
match node with
| LeafP(position, 0) -> LeafP(position, evaluateLeaf(position))
| BranchP(position, children) -> LeafP(position,(children
|> List.map (loop (1 - minOrmax))
|> minimax.[minOrmax]
|> getStaticEvalFromNode))
match tree with
| BranchP(position, children) -> children |> List.map (loop (1 - player)) |> minimax.[player]
Вместо того, чтобы передавать все дерево, я передаю только дочерние элементы начального узла и снова фильтрую полученный список (список бывших ветвей со статическими значениями, которые стали лучшими для текущего уровня). Таким образом я получу нужный узел.
Я думал, что ответы kvb очень интересны, но немного сложны для меня. Остальные я изучил недостаточно, но они просто возвращают мне статическое значение - и я не мог заставить их работать на меня :(
Большое спасибо за все ответы, все они меня очень вдохновили.
Вот мой полный код: (http://www.inf.ufrgs.br/~pmdusso/works/Functional_Implementation_Minimax_FSharp.htm)
Педро Дуссо