How to represent and track mutable state in a text

This question is about how to design an app, and layout data, to represent mutable state in an adventure game where there are locations the player can be in, and items which can either be in those locations, or in the players inventory after they pick them up.

newtype ItemName     = ItemName Text
newtype LocationName = LocationName Text
newtype Description  = Description Text
new type ItemWeight  = ItemWeight Int

data Location = Location LocationName Description [Item]
data Item     = Item ItemName [ItemName] Description ItemWeight
data Player   = Player PlayerPosition [Item]

data PlayerPosition = ...

It may not make sense for each Location to have a list of Item s, since this is mutable, and everything else is immutable. Likewise for Player

In such a game the core logic falls into a function called.

playMove :: Move -> GameState -> ([MoveResult], GameState)

where the Move and MoveResult functions are

data Move                              data MoveResult
  = MoveTo Direction                     = EnteredLocation Location
  | PickUpItem ItemName                  | NoLocationAt Direction
  | UseItemWithItem ItemName ItemName    | PickedUpItem Item
  | Examine ItemName                     | UsedItemWithItem Item
  | Quit                                 | CantUseItems Item Item
                                         | NoSuchItem Item
                                         | Description Item
                                         | InventoryFull ItemWeight Item
                                         | GameWon
                                         | ...

Parsing user input into Move values, and outputting the MoveResult s, is something that happens in the IO monad. Direction is just a sum-type over North | South | East | West North | South | East | West

Most of these will no doubt involve a GameState which captures

  • The player position in the world
  • The contents of their inventory
  • Where the items are located. Items may be in a location, in the player's inventory, or in limbo. Items in limbo have not yet been created by combining items, or having been erased in the combining them.
  • My question is

  • What is a good data-structure to represent the GameState . Such a structure should encapsulate the player's position and inventory contents, and where items may be found in the world. It should faciliate an API like
  • moveTo :: GameState -> Direction -> ([MoveResult], GameState) where the MoveResult is either EnteredLocation or NoLocationAt . This in turn requires a function like locationAt :: Location -> Direction -> Maybe Location
  • pickUp :: GameState -> ItemName -> ([MoveResult], GameState) where the MoveResult is PickedUpItem or NoSuchItem . In this case how does one match ItemName to an Item
  • useItemWithItem :: GameState -> (ItemName, ItemName) -> ([MoveResult], GameState) where the MoveResult is either PickedUpItem if a new items results, or CantUserItems otherwise. This in turn requires a function like combineItems :: Item -> Item -> Maybe Item to see if items can be combined.
  • What is a good data-structure to represent PlayerPosition
  • Should I use heavy Location types or introduce some LocationId in representing the PlayerPosition and potentially the items positions
  • For the graph mapping origin Location to destination Location via a Direction edge, what is the best data-structure to use. I was thinking a Data.Map of (Location, Direction) to Location .
  • I am aware of zippers, and the the fact that playMove fits into the State monad. My issue is how to structure GameState to capture the mutable and immutable parts of this problem.


    Edit: The Answer

    I did find an answer from this productive discussion on Reddit. In case anyone else finds this question, /u/achadoseperdidos in that discussion pointed out that the answer to my question is provided by chapter 11 of Learn PureScript by Example which actually describes how to use the RWS monads to write an adventure game - exactly the answer I was seeking.


    At this scale, you need not worry about size or efficiency. Simply use a record like:

    import qualified Data.Map as M
    
    data Location = Location
      { locationname :: String
      , description :: String
      } deriving Ord
    
    data GameStep = GameStep
      { itemsWorld :: M.Map Location [Item]
      , inventory :: [Item]
      , position :: Location
      }
    
    链接地址: http://www.djcxy.com/p/58770.html

    上一篇: EJB 3.1依赖注入失败

    下一篇: 如何在文本中表示和跟踪可变状态