【问题标题】:Representing game state with typeclass in Haskell在 Haskell 中用 typeclass 表示游戏状态
【发布时间】:2017-01-04 23:16:55
【问题描述】:

我正在实现一些功能来搜索游戏树以供自己娱乐。例如,一个这样的函数将执行Minimax 算法。这些函数使用两种对象:

  1. 游戏状态,例如棋盘上棋子的位置、下一个要移动的玩家等。
  2. 移动,例如“典当 e2 到 e5”

我的目标是拥有适用于不同游戏的搜索功能,因此我将游戏特定知识作为辅助函数传递,例如:

  • 从给定的游戏状态生成所有可能的移动
  • 给定状态和移动,将移动应用到状态以获得结果状态
  • 找出这是否是一个获胜的位置

这样一来,我的搜索功能确实不是特定于游戏的,但解决方案仍然感觉不对。这也使我的参数列表相当长:

-- Given a game state find the best next move
bestMove :: s -> (s -> [m]) -> (s -> m -> s) -> m    -- more parameters in my real code
bestMove currentState possibleMoves applyMove = ...

问题

我可以用类型类替换这些辅助函数吗?我正在寻找类似的东西:

class Move m where
  apply :: State s => s -> m -> s

class State s where
  possibleMoves :: Move m => s -> [m]

bestMove :: State s => Move m => s -> m
bestMove currentState = ... -- e.g. = head $ possibleMoves state

问题是State 的任何实例只能与Move 的一个特定实例一起工作,反之亦然:

{-# LANGUAGE InstanceSigs #-} 

data ChessState = ...
data ChessMove  = ...

instance Move ChessMove where
  apply :: ChessState -> ChessMove -> ChessState
  apply s m = ...

apply 的类型签名当然是错误的。应该是

apply :: S s => s -> ChessMove -> s

但我确实需要特定于ChessState 的属性才能创建ChessMove

我的整个想法完全走错了轨道,还是有办法编码ChessStateChessMove之间的关系?我在使用 MultiParamTypeClasses 方面取得了一些进展,但它仍然不像我希望的那样。

【问题讨论】:

    标签: haskell typeclass


    【解决方案1】:

    您可以考虑只传入整个该死的游戏树:

    data GameTree m s = GameTree s [(m, GameTree m s)]
    
    bestMove :: GameTree m s -> (s -> Int) -> Maybe m
    bestMove gameTree evaluateState = ...
    

    依靠懒惰来仅扩展您实际查看的游戏树部分。

    【讨论】:

    • 我很难在这个答案和我接受的答案之间做出决定。我觉得这个答案描述了一个更好的解决方案,但对于从不同背景遇到这个问题的人来说可能没有那么有用。
    【解决方案2】:

    首先,如果您可以在没有类型类的情况下做某事,那么远离它们通常是个好主意。您总是可以为特定应用程序制作一个更简单的包装器,特别是如果您适当地翻转参数:

    bestMove :: (s -> [m]) -> (m -> s -> s) -> s -> m
    
    bestChessMove :: ChessState -> ChessMove
    bestChessMove = bestMove possibleChessMoves applyChessMove
    

    也就是说,我不觉得你设想的类是不明智的(不像许多人想挤进 Haskell 的 OO 类......)。而且你完全在正确的轨道上,它应该是一个多参数类:

    {-# LANGUAGE MultiParamTypeClasses #-} 
    
    class MoveState m s where
      apply :: m -> s -> s
      possibleMoves :: s -> [m]
    
    instance MoveState ChessMove ChessState where
      apply = ...
      possibleMoves = ...
    

    然后

    bestMove :: MoveState m s => (s -> Cost) -> s -> m
    

    实际上,这个类比有意义的更通用:对于任何给定的状态类型,您可能只有一种移动类型。一种表达方式是functional dependency| s -> m;我通常更喜欢将Move 类型简单地作为状态类的“属性”:

    {-# LANGUAGE TypeFamilies #-} 
    
    class GameState s where
      type Move s :: *
      apply :: Move s -> s -> s
      possibleMoves :: s -> [Move s]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-15
      • 1970-01-01
      相关资源
      最近更新 更多