如何返回这个 F# minimax 中最好的第一级?

发布于 2024-09-06 15:08:17 字数 2527 浏览 5 评论 0 原文

这个问题更多的是语义算法数据结构问题,而不是 F# 语法问题。 我有一个极小极大算法。极小极大算法应该返回从起始位置开始的最佳下一步动作。为此,它计算所有下一步移动,然后计算下一个移动,直到确定的深度或直到不再有移动。它构建了一个像这样的树:

     P  
   /  \  
 a      b  
/ \  
c d

我有以下数据结构来处理树:

type TreeOfPosition =
    | LeafP   of Position * int
    | BranchP of Position * TreeOfPosition list

在上面的示例树中,Pa是分支,b code>、cd 是叶子。下面的代码是我的极小极大算法:

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)))

并且此修改使得好叶子的静态值上升。 我需要返回最好的二级节点。 希望有人可以帮忙! Pedro Dusso

编辑1

感谢大家的回答,他们对我帮助很大。抱歉没有详细说明这些事情。让我们分部分进行:

1) 我像 LeafP(position, 0) 一样匹配我的 LeafP,因为当我创建树时,我将默认值 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)

佩德罗·杜索

This question is more a semantic-algorithmic-data-structure question than a F# syntactically question.
I have a Minimax algorithm. The minimax algorithm should return the best next move, from a start position. To do this, it calculus all next moves, then the next-next-moves until a determined depth or until there is no more moves. It builds a tree like this:

     P  
   /  \  
 a      b  
/ \  
c d

I have the fallowing data struct to handle the tree:

type TreeOfPosition =
    | LeafP   of Position * int
    | BranchP of Position * TreeOfPosition list

In the exemple tree above, P and a are Branchs and b, c and d are Leafs. The code below is my minimax algorithm:

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

This code are returning me a Leaf, for example, c. When I changed the recursion call to

| BranchP(position, children)  -> 
    LeafP(position, 
          getStaticEvalFromNode(minimax.[minOrmax](
                       List.map (loop (1 - minOrmax)) children)))

And this modification makes the static value of a good leaf go up.
I need to return the best second level node.
Hope somebody can help!
Pedro Dusso

EDIT 1

Thanks for all answers guys, they help me a lot. Sorry about didn't specified the things very much. Let's go in parts:

1) I’m matching my LeafP like LeafP(position, 0) because when I create my tree I set the leafs with a default value of 0 as its static value. As I’m going up my static values, eliminating the leaf and making the (before Branches) leafs with (min or max) static values I thought that this way I would prevent to evaluate a ex-Branch leaf (because it would not have the 0 value).

2) My biggest problem was to get the second level (the next move which has to be played) best position back. I solved it this way:

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]

Instead of passing the entire tree, I’m passing just the children’s of the start node, and filtering the resulted list (a list of ex-Branches with the static values which went up for be the best for its current level) again. This way I’m getting the node I wanted.

I thought the kvb answers very interesting, but a little complicated to me. The other ones I understudied, but they just give me back the static value – and I could not make them to work for me :(

Thanks a lot for all the answers, all of them inspired me a lot.

Here is my full code: (http://www.inf.ufrgs.br/~pmdusso/works/Functional_Implementation_Minimax_FSharp.htm)

Pedro Dusso

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

眼眸里的那抹悲凉 2024-09-13 15:08:18

我不太明白你的样本的某些方面(例如,为什么你只匹配带有 0 的叶子?),所以我将在下面进行一些更改。首先,让我们稍微概括一下树类型,以便它可以在叶子和分支中存储任何类型的数据:

type Tree<'a,'b> = 
| Leaf of 'a 
| Branch of 'b * Tree<'a,'b> list

我们还使用专用的玩家类型,而不是使用 0 或 1:

type Player = Black | White

最后,让我们概括一下对最好移动一点,以便叶子评估函数作为参数传入:

let bestMove evalPos player tree =
  // these replace your minimax function array
  let agg1,agg2,aggBy = 
    match player with
    | Black -> List.min, List.max, List.maxBy
    | White -> List.max, List.min, List.minBy

  // given a tree, this evaluates the score for that tree
  let rec score agg1 agg2 = function
  | Leaf(p) -> evalPos p
  | Branch(_,l) -> agg1 (List.map (score agg2 agg1) l)

  // now we use just need to pick the branch with the highest score
  // (or lowest, depending on the player)
  match tree with
  | Leaf(_) -> failwith "Cannot make any moves from a Leaf!"
  | Branch(_,l) -> aggBy (score agg1 agg2) l 

I don't quite understand some aspects of your sample (e.g. why do you match only against leaves with 0s in them?), so I'll make a few changes below. First of all, let's generalize the tree type a bit, so that it can store any types of data in the leaves and branches:

type Tree<'a,'b> = 
| Leaf of 'a 
| Branch of 'b * Tree<'a,'b> list

Let's also use a dedicated player type, rather than using 0 or 1:

type Player = Black | White

Finally, let's generalize the evaluation of the best move a bit, so that the leaf evaluation function is passed in as an argument:

let bestMove evalPos player tree =
  // these replace your minimax function array
  let agg1,agg2,aggBy = 
    match player with
    | Black -> List.min, List.max, List.maxBy
    | White -> List.max, List.min, List.minBy

  // given a tree, this evaluates the score for that tree
  let rec score agg1 agg2 = function
  | Leaf(p) -> evalPos p
  | Branch(_,l) -> agg1 (List.map (score agg2 agg1) l)

  // now we use just need to pick the branch with the highest score
  // (or lowest, depending on the player)
  match tree with
  | Leaf(_) -> failwith "Cannot make any moves from a Leaf!"
  | Branch(_,l) -> aggBy (score agg1 agg2) l 
萌能量女王 2024-09-13 15:08:18

我认为你可以使用相互递归函数:

let maxTree t = 
  match t with
  | child -> xxx
  | subtrees s ->
      s |> Seq.map minTree |> Seq.max

and minTree t = 
  match t with
  | child -> xxx
  | subtrees s ->
      s |> Seq.map maxTree |> Seq.min

I think you can use mutually recursive functions:

let maxTree t = 
  match t with
  | child -> xxx
  | subtrees s ->
      s |> Seq.map minTree |> Seq.max

and minTree t = 
  match t with
  | child -> xxx
  | subtrees s ->
      s |> Seq.map maxTree |> Seq.min
泪是无色的血 2024-09-13 15:08:18

F#.NET Journal 文章游戏编程:tic-tac-toe(2009 年 12 月 31 日)并使用以下模式:

type t = Leaf | Branch of t seq

let aux k = function
  | Leaf -> []
  | Branch s -> k s

let rec maxTree t = aux (Seq.map minTree >> Seq.max) t
and minTree t = aux (Seq.map maxTree >> Seq.min) t

另请参阅 可玩演示

The solution to this problem was described in the F#.NET Journal article Games programming: tic-tac-toe (31st December 2009) and uses the following pattern:

type t = Leaf | Branch of t seq

let aux k = function
  | Leaf -> []
  | Branch s -> k s

let rec maxTree t = aux (Seq.map minTree >> Seq.max) t
and minTree t = aux (Seq.map maxTree >> Seq.min) t

See also the playable demo.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文