我有一个简单的单人纸牌游戏:
data Player = Player {
_hand :: [Card],
_deck :: [Card],
_board :: [Card]}
$(makeLenses ''Player)
一些卡牌具有效果。例如,“Erk”是一张具有以下效果的卡牌:
Flip a coin. If heads, shuffle your deck.
我已经按照以下方式实施了它:
shuffleDeck :: (MonadRandom m, Functor m) => Player -> m Player
shuffleDeck = deck shuffleM
randomCoin :: (MonadRandom m) => m Coin
randomCoin = getRandom
flipCoin :: (MonadRandom m) => m a -> m a -> m a
flipCoin head tail = randomCoin >>= branch where
branch Head = head
branch Tail = tail
-- Flip a coin. If heads, shuffle your deck.
erk :: (MonadRandom m, Functor m) => Player -> m Player
erk player = flipCoin (deck shuffleM player) (return player)
虽然这样做确实可以完成任务,但我发现强制耦合到Random
库有问题。如果我以后有一张卡片依赖于另一个单子,那么我将不得不重写到目前为止定义的每一张卡片的定义(使它们具有相同的类型)。我更喜欢一种完全独立于Random(和其他任何东西)描述游戏逻辑的方法。就像这样:
erk :: CardAction
erk = do
coin <- flipCoin
case coin of
Head -> shuffleDeck
Tail -> doNothing
后来,我可以编写一个名为runGame
的函数来进行连接。
runGame :: (RandomGen g) => g -> CardAction -> Player -> Player
我不确定那会有帮助。对于这个模式,正确的语言处理方式是什么?
MonadRandom
不是一个单子(monad),而是一个支持随机数生成的单子类型类(type class for monads)。您可以在广泛的单子变换堆栈中使用erk
而不进行更改。 - András Kovács