gpt4 book ai didi

haskell - 使用 MonadPrompt 实现回放

转载 作者:行者123 更新时间:2023-12-03 14:58:29 25 4
gpt4 key购买 nike

灵感来自 Brent Yorgey 的
adventure game ,
我一直在编写一个基于文本的小型冒险游戏(la Zork),它使用
MonadPrompt
图书馆。使用它来分离 IO 后端相当简单
从控制游戏玩法的实际功能,但我现在正在尝试做
一些更复杂的东西。

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

这是一些基本显示我的设置的代码(我为长度道歉,但这比实际代码简化了很多):

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 函数。它需要一个后端函数来执行
完成重播(通常是 basicIO )和要重播的 Action 列表
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) -}
...
replayIO 的这个实现但不起作用,因为 replayIO不是
直接递归,您实际上无法从操作列表中删除操作
传递给 replayIO .它从函数中获取初始操作列表
加载保存文件,然后它可以查看列表中的第一个 Action 。

到目前为止,我想到的唯一解决方案是维护列表
GameState 中回放操作.我不喜欢这个,因为这意味着我不能
干净地分开 basicIOreplayIO .我想要 replayIO处理其
操作列表,然后将控制权传递给 basicIO对于该列表
完全消失。

到目前为止,我用过 runPromptM从 MonadPrompt 包中使用 Prompt
monad,但查看包,runPromptC 和 runRecPromptC
功能看起来更强大,但我不明白它们
足以看到它们如何(或是否)在这里对我有用。

希望我已经包含了足够的细节来解释我的问题,如果有人可以带我走出这里的树林,我将非常感激。

最佳答案

据我所知,没有办法在运行 Prompt 中途切换提示处理程序。 Action ,因此您将需要一个处理程序来处理仍有 Action 要重播的情况以及恢复正常播放的情况。

我认为解决此问题的最佳方法是添加另一个 StateT转换到您的堆栈以存储要执行的剩余操作列表。这样,回放逻辑可以与 basicIO 中的主游戏逻辑分开。 ,并且您的重播处理程序可以调用 lift . basicIO当没有任何 Action 时,不执行任何操作或从状态中选择 Action 。

关于haskell - 使用 MonadPrompt 实现回放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8203745/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com