Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/visual-studio/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 用MonadCompt实现回放_Haskell_Functional Programming_Monads - Fatal编程技术网

Haskell 用MonadCompt实现回放

Haskell 用MonadCompt实现回放,haskell,functional-programming,monads,Haskell,Functional Programming,Monads,灵感来源于布伦特·约基的 , 我一直在写一个基于文本的小冒险游戏(LaZork),它使用 图书馆。使用它来分离IO后端非常简单 从控制游戏性的实际功能来看,但我现在正在尝试 有点复杂 基本上,我想启用撤销和重做作为游戏的一个功能。我的策略 因为这一直是为了保持一个拉链的游戏状态(其中包括什么是最后的 输入为)。因为我希望在重新加载时能够维护历史记录 在游戏中,保存文件只是玩家执行的所有输入的列表 可能会影响游戏状态(因此不包括检查库存, 说)。这样做的目的是通过保存中的输入快速重播最后一场比赛

灵感来源于布伦特·约基的 , 我一直在写一个基于文本的小冒险游戏(LaZork),它使用 图书馆。使用它来分离IO后端非常简单 从控制游戏性的实际功能来看,但我现在正在尝试 有点复杂

基本上,我想启用撤销和重做作为游戏的一个功能。我的策略 因为这一直是为了保持一个拉链的游戏状态(其中包括什么是最后的 输入为)。因为我希望在重新加载时能够维护历史记录 在游戏中,保存文件只是玩家执行的所有输入的列表 可能会影响游戏状态(因此不包括检查库存, 说)。这样做的目的是通过保存中的输入快速重播最后一场比赛 加载游戏时的文件(跳过输出到终端,并从 列表),从而建立游戏状态的完整历史记录

下面是一些代码,基本上显示了我的设置(我为长度道歉,但这比实际代码要简单得多):

但是,
replayIO
的这种实现不起作用,因为
replayIO
不起作用 直接递归,实际上无法从操作列表中删除操作 传递到
replayIO
。它从该函数获取操作的初始列表 加载保存文件,然后它可以查看列表中的第一个操作

到目前为止,我想到的唯一解决办法是维护 在
游戏状态中重播动作
。我不喜欢这个,因为这意味着我不能 将
basicIO
replayIO
干净地分开。我想让
replayIO
处理它的 操作列表,然后将控制权传递给
basicIO
,以使该列表 完全消失

到目前为止,我已经使用MonadCompt包中的
runPromptM
来使用提示符 monad,但查看包,runPromptC和runRecPromptC 函数看起来更强大,但我不理解它们 好吧,看看它们在这里对我有多有用


希望我已经包含了足够的细节来解释我的问题,如果有人能带我走出困境,我将非常感激。

从我所能告诉的,在运行
提示
操作的中途,没有办法切换提示处理程序,因此,您将需要一个单独的处理程序来处理仍有操作需要重放的情况,以及恢复正常播放的情况


我认为解决这个问题的最佳方法是向堆栈中添加另一个
StateT
转换器,以存储要执行的其余操作列表。这样,重放逻辑就可以与
basicIO
中的主游戏逻辑分开,重放处理程序只需调用
lift即可。基本操作
当没有剩余操作时,不要执行任何操作或选择状态以外的操作。

谢谢!这个解决方案对我有用。我想我希望有一些奇特的类型的黑客可以在处理程序之间切换,但那个库太复杂了,我无法去探索(连续剧让我头疼)
data Action = UndoAction | RedoAction | Go Direction -- etc ...
-- Actions are what we parse user input into, there is also error handling
-- that I left out of this example
data RPGPrompt a where
    Say :: String -> RPGPrompt ()
    QueryUser :: String -> RPGPrompt Action
    Undo :: RPGPrompt ( Prompt RPGPrompt ())
    Redo :: RPGPrompt ( Prompt RPGPrompt ())
    {-
    ... More prompts like save, quit etc. Also a prompt for the play function 
        to query the underlying gamestate (but not the GameZipper directly)
    -}

data GameState = GameState { {- hp, location etc -} }
data GameZipper = GameZipper { past :: [GameState],
                               present :: GameState, 
                               future :: [GameState]}

play :: Prompt RPGPrompt ()
play = do
  a <- prompt (QueryUser "What do you want to do?")
  case a of
    Go dir -> {- modify gamestate to change location ... -} >> play
    UndoAction -> prompt (Say "Undo!") >> join (prompt Undo)
    ... 

parseAction :: String -> Action
...

undo :: GameZipper -> GameZipper
-- shifts the last state to the present state and the current state to the future

basicIO :: RPGPrompt a -> StateT GameZipper IO a
basicIO (Say x) = putStrLn x
basicIO (QueryUser query) = do
  putStrLn query
  r <- parseAction <$> getLine
  case r of
     UndoAction -> {- ... check if undo is possible etc -}
     Go dir -> {- ... push old gamestate into past in gamezipper, 
                   create fresh gamestate for present ... -} >> return r
     ...
basicIO (Undo) = modify undo >> return play
...
replayIO :: (RPGPrompt a -> StateT GameZipper IO a) -> 
            [Action] ->
            RPGPrompt a ->
            StateT GameZipper IO a
replayIO _ _ (Say _) = return () -- don't output anything
replayIO resume [] (QueryUser t) = resume (QueryUser t)
replayIO _ (action:actions) (Query _) =
   case action of
      ... {- similar to basicIO here, but any non-gamestate-affecting 
             actions are no-ops (though the save file shouldn't record them 
             technically) -}
...