如何让这段Haskell代码更简洁?

Vie*_*ele 7 algorithm haskell functional-programming

作为练习,我正在尝试为Haskell中的赌场游戏"战争"编写模拟.

http://en.wikipedia.org/wiki/Casino_war

这是一个非常简单的游戏,有一些规则.用我所知道的任何命令式语言编写一个非常简单的问题,但是我很难在Haskell中编写它.

我到目前为止的代码:

 -- Simulation for the Casino War

import System.Random
import Data.Map

-------------------------------------------------------------------------------
-- stolen from the internet

fisherYatesStep :: RandomGen g => (Map Int a, g) -> (Int, a) -> (Map Int a, g)
fisherYatesStep (m, gen) (i, x) = ((insert j x . insert i (m ! j)) m, gen')
    where
        (j, gen') = randomR (0, i) gen

fisherYates :: RandomGen g => g -> [a] -> ([a], g)
fisherYates gen [] = ([], gen)
fisherYates gen l = toElems $ Prelude.foldl
        fisherYatesStep (initial (head l) gen) (numerate (tail l))
    where
        toElems (x, y) = (elems x, y)
        numerate = zip [1..]
        initial x gen = (singleton 0 x, gen)

-------------------------------------------------------------------------------

data State = Deal | Tie deriving Show

-- state: game state
-- # cards to deal
-- # cards to burn
-- cards on the table
-- indices for tied players
-- # players
-- players winning
-- dealer's winning
type GameState = (State, Int, Int, [Int], [Int], Int, [Int], Int)

gameRound :: GameState -> Int -> GameState
gameRound (Deal, toDeal, toBurn, inPlay, tied, numPlayers, pWins, dWins) card
    | toDeal > 0 =
        -- not enough card, deal a card
        (Deal, toDeal - 1, 0, card:inPlay, tied, numPlayers, pWins, dWins)
    | toDeal == 0 =
        -- enough cards in play now
        -- here should detemine whether or not there is any ties on the table,
        -- and go to the tie state
        let
            dealerCard = head inPlay
            p = zipWith (+) pWins $ (tail inPlay) >>=
                (\x -> if x < dealerCard then return (-1) else return 1)
            d = if dealerCard == (maximum inPlay) then dWins + 1 else dWins - 1
        in
            (Deal, numPlayers + 1, 0, [], tied, numPlayers, p, d)
gameRound (Tie, toDeal, toBurn, inPlay, tied, numPlayers, pWins, dWins) card
    -- i have no idea how to write the logic for the tie state AKA the "war" state
    | otherwise = (Tie, toDeal, toBurn, inPlay, tied, numPlayers, pWins, dWins)

-------------------------------------------------------------------------------

main = do
    rand <- newStdGen
    -- create the shuffled deck
    (deck, _) <- return $ fisherYates rand $ [2 .. 14] >>= (replicate 6)
    -- fold the state updating function over the deck
    putStrLn $ show $ Prelude.foldl gameRound
        (Deal, 7, 0, [], [], 6, [0 ..], 0) deck

-------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

我理解为什么额外的工作必须用于创建随机数,但我很确定我缺少一些基本的构造或概念.保持状态集合并在输入列表上运行分支逻辑不应该是这样的尴尬.我甚至无法找到一种很好的方法来为表格上存在联系的情况编写逻辑.

我不是要求完整的解决方案.如果有人可以指出我做错了什么,或者一些相关的好阅读材料,那将是非常好的.

提前致谢.

Pet*_*ter 6

用于维护应用程序状态的有用设计模式是所谓的状态monad.您可以在此处找到说明和一些介绍性示例.此外,您可能需要考虑使用带有命名字段而非元组的数据类型GameState,例如:

data GameState = GameState { state :: State, 
                             toDeal :: Int
                           -- and so on
                           }
Run Code Online (Sandbox Code Playgroud)

这将使用记录语法更容易访问/更新单个字段.