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
My question is
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. PlayerPosition
Location
types or introduce some LocationId in representing the PlayerPosition
and potentially the items positions 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依赖注入失败
下一篇: 如何在文本中表示和跟踪可变状态