Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 6851297
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 27, 20262026-05-27T01:13:52+00:00 2026-05-27T01:13:52+00:00

Inspired by Brent Yorgey’s adventure game , I’ve been writing a small text based

  • 0

Inspired by Brent Yorgey’s
adventure game,
I’ve been writing a small text based adventure game (a la Zork) that uses the
MonadPrompt
library. It’s been fairly straightforward to use it to separate the IO backend
from the actual function that governs gameplay, but I’m now trying to do
something a bit more complicated with it.

Basically, I want to enable undo and redo as a feature of the game. My strategy
for this has been to keep a zipper of gamestates (which includes what the last
input was). Since I want to be able to maintain history when reloading
the game, The save file is just a list of all inputs the player performed that
can affect the gamestate (so examining the inventory would not be included,
say). The idea is to quickly replay the last game from the inputs in the save
file when loading a game (skipping output to the terminal, and taking input from
the list in the file), and thereby build up the full history of gamestates.

Here is some code that shows basically the setup I have (I apologize for the length, but this is much simplified from the actual code):

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
...

Next is the replayIO function. It takes a backend function to execute when it’s
done replaying (usually basicIO ) and a list of actions to replay

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) -}
... 

This implementation of replayIO doesn’t work though, because replayIO isn’t
directly recursive, you can’t actually remove Actions from the list of actions
passed to replayIO. It gets an initial list of actions from the function that
loads the save file, and then it can peek at the first action in the list.

There only solution that has occurred to me so far is to maintain the list of
replay actions inside the GameState. I don’t like this because it means I can’t
cleanly separate basicIO and replayIO. I’d like for replayIO to handle its
action list, and then when it passes control to basicIO for that list to
disappear entirely.

So far, I’ve used runPromptM from the MonadPrompt package to use the Prompt
monad, but looking through the package, the runPromptC and runRecPromptC
functions look like they are much more powerful, but I don’t understand them
well enough to see how (or if) they might be useful to me here.

Hopefully I’ve included sufficient detail to explain my problem, if anyone can lead me out of the woods here, I’d really appreciate it.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-27T01:13:52+00:00Added an answer on May 27, 2026 at 1:13 am

    From what I can tell, there is no way to switch prompt handlers halfway through running a Prompt action, so you will need a single handler that can deal with both the case where there are still actions left to replay, and the case when you’ve resumed normal play.

    The best way I see of solving this would be to add another StateT transformer to your stack to store the remaining list of actions to perform. That way, the replay logic can be kept separate from the main game logic in basicIO, and your replay handler can just call lift . basicIO when there are no actions left, and do no-ops or pick actions out of the state otherwise.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Inspired from the game GunTactyx , where you write programs controlling fighting robots. Guntactyx
Inspired by this topic , I decided to write a simple program that does
Inspired by this answer .. Can you guys point me to something similar--something that
Inspired by how-do-i-display-the-current-value-of-an-android-preference-in-the-preference-summary i created my own EditTextPreference that shows its current value in
inspired by Clojure, and Peter Norvig I have been trying my hand at implementing
Inspired by Stack Overflow question Idomatic batch processing of text in Emacs? I tried out
Inspired by std::string in a multi-threaded program and another answer (seen somewhere), that STL
Inspired by Muhammad Alkarouri answer in What are good uses for Python3's "Function Annotations"
Inspired by a recent TED talk , I want to write a small piece
I'm making a minecraft-inspired game through Java LWJGL, which is heavy into development already.

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.