通过递归折叠实现minimax

我正在为游戏的标准8 * 8草稿版本编写跳棋AI。  该状态被表示为一个镜头,带有代表列表板上零件的坐标列表。我想做的就是按照此伪代码进行Min-Max搜索。

function minimax(position,depth,maximizingPlayer)
   if depth == 0 or game over in position
        return static evaluation of position

   if maximizingPlayer
         maxEval = -infinity
         for each child of position
              eval = minimax(child,depth-1,False)
              maxEval = max(maxEval,eval)
         return maxEval
   else 
          minEval = +infinity
          for each child of position
              eval = minimax(child,true)
              minEval = min(minEval,eval)
         return minEval

根据我的理解,在我的情况下,position将是GameState。因此,在我的程序中,我想再次调用minimax的所有子级上的GameState,每个子级只是一个GameState并对其应用了移动。最终,我将达到深度0,在该深度中,我将返回一个启发式函数,该函数已进行计算。我受困的地方是如何在移动后迭代每个可能的GameState。我有一个函数可以计算特定GameState可以做出的所有可能动作,但是我仍然坚持如何遍历所有这些动作,并使用应用了每个动作的新GameState调用minimax。 / p>

回到伪代码,我知道child是一个函数调用applyMove,它接受​​Move和当前的GameState,并返回带有新棋子位置的GameState。每个“孩子”将因不同的举动而成为不同的GameState。我对Haskell来说还很陌生,我知道我可能需要为此使用折叠。但是我只是坚持如何编写它,我找不到很多可以轻松地与自己的情况相关的示例。任何建议/提示都将不胜感激。

移动列表看起来像这样:[[(1,2),(2,3)],[(3,6),7)]]的{​​{1}}和child的{​​{1}}将成为应用移动之后的GameState,例如

GameState

yymbaya 回答:通过递归折叠实现minimax

您已经具有一些功能:

legalMoves :: Position -> [Move]
applyMove :: Position -> Move -> Position

我认为您的minimax可以使用不同的签名来变得更简洁:与其让布尔决定是否最大化或最小化(每种情况都有不同的情况),总是尝试最大化并改变评估值会更简单而是通过在每一步翻转其符号来实现功能。

有了这些信息后,您实际上就不需要手动编写折叠操作:只需对每个合法举动进行map递归调用,然后将它们与maximum粘合在一起,以找到最适合的举动当前玩家。

minimax :: (Position -> Int) -> Int -> Position -> Int
minimax eval 0 pos = eval pos
minimax eval n pos = case legalMoves pos of
  [] -> eval pos
  moves -> maximum . map negate 
       . map (minimax (negate . eval) (n - 1) . applyMove pos) 
       $ moves

请注意,您的规范无法确定什么动作是最好的,只有做出最佳动作才能获得什么分数。为了找到最佳移动,您需要使minimax返回一个元组,其中包含得分和到达目标的移动,或类似的东西。

本文链接:https://www.f2er.com/3162131.html

大家都在问