3

因此,我正在为类似跳棋的游戏开发极小极大实现,以帮助自己更好地学习 Haskell。我遇到问题的函数需要一个游戏状态列表,并生成直接后继游戏状态的列表。像跳棋一样,如果可以跳,玩家必须跳。如果有多个,玩家可以选择。

在大多数情况下,这与 list monad 配合得很好:遍历所有输入游戏状态,遍历所有可以跳跃的弹珠,遍历该弹珠的所有跳跃。这个列表单子很好地将所有列表扁平化为最后的简单状态列表。

诀窍是,如果没有找到给定游戏状态的跳转,我需要返回当前游戏状态,而不是空列表。下面的代码是我想出的最好的方法,但它对我来说真的很难看。关于如何清理它的任何建议?

eHex :: Coord -> Coord -- Returns the coordinates immediately to the east on the board
nwHex :: Coord -> Coord -- Returns the coordinates immediately to the northwest on the board

generateJumpsIter :: [ZertzState] -> [ZertzState]
generateJumpsIter states = do
    ws <- states
    case children ws of
      [] -> return ws
      n@_ -> n
  where 
    children ws@(ZertzState s1 s2 b p) = do
      (c, color)  <- occupiedCoords ws
      (start, end) <- [(eHex, wHex), (wHex, eHex), (swHex, neHex),
                       (neHex, swHex), (nwHex, seHex), (seHex, nwHex)]
      if (hexOccupied b $ start c) && (hexOpen b $ end c)
        then case p of
          1 -> return $ ZertzState (scoreMarble s1 color) s2
                                   (jumpMarble (start c) c (end c) b) p
          (-1) -> return $ ZertzState s1 (scoreMarble s2 color)
                                      (jumpMarble (start c) c (end c) b) p
        else []

编辑:为 *Hex 函数提供示例类型签名。

4

2 回答 2

3

诀窍是,如果没有找到给定游戏状态的跳转,我需要返回当前游戏状态,而不是空列表。

为什么?我已经写过几次 minimax 了,我无法想象这样一个函数的用途。使用类型函数不是更好吗

nextStates :: [ZertzState] -> [Maybe [ZertzState]]

或者

nextStates :: [ZertzState] -> [[ZertzState]]

但是,如果您真的想返回“下一个状态列表,或者如果该列表为空,则返回原始状态”,那么您想要的类型是

nextStates :: [ZertzState] -> [Either ZertzState [ZertzState]]

然后你可以很容易地把它弄平。

至于如何实现,我推荐定义一个类型的辅助函数

[ZertzState] -> [(ZertzState, [ZertzState])]

而且你可以映射

(\(start, succs) -> if null succs then Left start else Right succs)

结果,加上其他各种事情。

正如 Fred Brooks 所说(释义),一旦你得到正确的类型,代码实际上会自己编写。

于 2009-08-05T19:59:08.427 回答
1

不要滥用列表的 monads 符号,它太重了。此外,您可以以相同的方式使用列表推导:

do x <- [1..3]
   y <- [2..5]      <=>  [ x + y | x <- [1..3], y <- [2..5] ]
   return x + y

现在为了“简化”

listOfHex :: [(Coord -> Coord,Coord -> Coord)]
listOfHex = [ (eHex, wHex), (wHex, eHex), (swHex, neHex)
            , (neHex, swHex), (nwHex, seHex), (seHex, nwHex)]

generateJumpsIter :: [ZertzState] -> [ZertzState]
generateJumpsIter states =
    [if null ws then ws else children ws | ws <- states]
  where -- I named it foo because I don t know what it do....
    foo True   1  = ZertzState (scoreMarble s1 color) s2
                               (jumpMarble (start c) c (end c) b) p
    foo True (-1) = ZertzState s1 (scoreMarble s2 color)
                               (jumpMarble (start c) c (end c) b) p
    foo False  _  = []
    foo _ _ = error "Bleh"

    children ws@(ZertzState s1 s2 b p) =
      [ foo (valid c hex) p | (c, _)  <- occupiedCoords ws, hex <- listOfHex ]
        where valid c (start, end) =
                 (hexOccupied b $ start c) && (hexOpen b $ end c)

顶部的 let in 列表理解让我有点困扰,但由于我没有所有代码,我真的不知道如何以其他方式进行。如果您可以进行更深入的修改,我建议您使用更多组合器(map、foldr、foldl' 等),因为根据我的经验,它们确实减少了代码大小。

请注意,代码未经测试,可能无法编译。

于 2009-08-05T09:04:54.933 回答