Copyright | Brent Yorgey |
---|---|
License | BSD-3-Clause |
Maintainer | byorgey@gmail.com |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
An Entity
represents an object that exists in the world. Each
entity has a way to be displayed, some metadata such as a name and
description, some properties, and possibly an inventory of other
entities.
This module also defines the Inventory
type, since the two types
are mutually recursive (an inventory contains entities, which can
have inventories).
Synopsis
- data EntityProperty
- newtype GrowthTime = GrowthTime (Integer, Integer)
- defaultGrowthTime :: GrowthTime
- data Entity
- mkEntity :: Display -> Text -> [Text] -> [EntityProperty] -> [Capability] -> Entity
- entityDisplay :: Lens' Entity Display
- entityName :: Lens' Entity Text
- entityPlural :: Lens' Entity (Maybe Text)
- entityNameFor :: Int -> Getter Entity Text
- entityDescription :: Lens' Entity [Text]
- entityOrientation :: Lens' Entity (Maybe (V2 Int64))
- entityGrowth :: Lens' Entity (Maybe GrowthTime)
- entityYields :: Lens' Entity (Maybe Text)
- entityProperties :: Lens' Entity [EntityProperty]
- hasProperty :: Entity -> EntityProperty -> Bool
- entityCapabilities :: Lens' Entity [Capability]
- entityInventory :: Lens' Entity Inventory
- entityHash :: Getter Entity Int
- data EntityMap = EntityMap {}
- buildEntityMap :: [Entity] -> EntityMap
- loadEntities :: MonadIO m => m (Either Text EntityMap)
- lookupEntityName :: Text -> EntityMap -> Maybe Entity
- deviceForCap :: Capability -> EntityMap -> [Entity]
- data Inventory
- type Count = Int
- empty :: Inventory
- singleton :: Entity -> Inventory
- fromList :: [Entity] -> Inventory
- fromElems :: [(Count, Entity)] -> Inventory
- lookup :: Entity -> Inventory -> Count
- lookupByName :: Text -> Inventory -> [Entity]
- countByName :: Text -> Inventory -> Count
- contains :: Inventory -> Entity -> Bool
- contains0plus :: Entity -> Inventory -> Bool
- elems :: Inventory -> [(Count, Entity)]
- isSubsetOf :: Inventory -> Inventory -> Bool
- isEmpty :: Inventory -> Bool
- inventoryCapabilities :: Inventory -> Set Capability
- insert :: Entity -> Inventory -> Inventory
- insertCount :: Count -> Entity -> Inventory -> Inventory
- delete :: Entity -> Inventory -> Inventory
- deleteCount :: Count -> Entity -> Inventory -> Inventory
- deleteAll :: Entity -> Inventory -> Inventory
- union :: Inventory -> Inventory -> Inventory
- difference :: Inventory -> Inventory -> Inventory
Properties
data EntityProperty Source #
Various properties that an entity can have, which affect how robots can interact with it.
Unwalkable | Robots can't move onto a cell containing this entity. |
Portable | |
Growable | Regrows from a seed after it is harvested. |
Infinite | Regenerates infinitely when grabbed or harvested. |
Liquid | Robots drown if they walk on this without a boat. |
Known | Robots automatically know what this is without having to scan it. |
Instances
newtype GrowthTime Source #
How long an entity takes to regrow. This represents the minimum and maximum amount of time taken by one growth stage (there are two stages). The actual time for each stage will be chosen uniformly at random between these two values.
Instances
Entities
A record to hold information about an entity.
The constructor for Entity
is intentionally not exported. To
construct one manually, use the mkEntity
function.
There are two main constraints on the way entities are stored:
- We want to be able to easily modify an entity in one particular cell of the world (for example, painting one tree red).
- In an inventory, we want to store identical entities only once, along with a count.
We could get (2) nicely by storing only names of entities, and having a global lookup table from names to entity records. However, storing names instead of actual entity records in the world makes (1) more complex: every time we modify an entity we would have to generate a fresh name for the modified entity and add it to the global entity table. This approach is also annoying because it means we can't just uses lenses to drill down into the properties of an entity in the world or in an inventory, but have to do an intermediate lookup in the global (mutable!) entity table.
On the other hand, if we just store entity records everywhere, checking them for equality becomes expensive. Having an inventory be a map with entities themselves as keys sounds awful.
The solution we adopt here is that every Entity
record carries
along a hash value of all the other fields. We just assume that
these hashes are unique (a collision is of course possible but
extremely unlikely). Entities can be efficiently compared just
by looking at their hashes; they can be stored in a map using
hash values as keys; and we provide lenses which automatically
recompute the hash value when modifying a field of an entity
record. Note also that world storage is still efficient, too:
thanks to referential transparency, in practice most of the
entities stored in the world that are the same will literally
just be stored as pointers to the same shared record.
Instances
:: Display | Display |
-> Text | Entity name |
-> [Text] | Entity description |
-> [EntityProperty] | Properties |
-> [Capability] | Capabilities |
-> Entity |
Create an entity with no orientation, an empty inventory, providing no capabilities (automatically filling in the hash value).
Lenses
Our own custom lenses which properly recompute the cached hash value each time something gets updated. See https://byorgey.wordpress.com/2021/09/17/automatically-updated-cached-views-with-lens/ for the approach used here.
entityDisplay :: Lens' Entity Display Source #
The Display
explaining how to draw this entity in the world display.
entityPlural :: Lens' Entity (Maybe Text) Source #
The irregular plural version of the entity's name, if there is one.
entityNameFor :: Int -> Getter Entity Text Source #
Get a version of the entity's name appropriate to the number---the singular name for 1, and a plural name for any other number. The plural name is obtained either by looking it up if irregular, or by applying standard heuristics otherwise.
entityDescription :: Lens' Entity [Text] Source #
A longer, free-form description of the entity. Each Text
value
represents a paragraph.
entityOrientation :: Lens' Entity (Maybe (V2 Int64)) Source #
The direction this entity is facing (if it has one).
entityGrowth :: Lens' Entity (Maybe GrowthTime) Source #
How long this entity takes to grow, if it regrows.
entityYields :: Lens' Entity (Maybe Text) Source #
The name of a different entity yielded when this entity is grabbed, if any.
entityProperties :: Lens' Entity [EntityProperty] Source #
The properties enjoyed by this entity.
hasProperty :: Entity -> EntityProperty -> Bool Source #
Test whether an entity has a certain property.
entityCapabilities :: Lens' Entity [Capability] Source #
The capabilities this entity provides when installed.
entityInventory :: Lens' Entity Inventory Source #
The inventory of other entities carried by this entity.
entityHash :: Getter Entity Int Source #
Get the hash of an entity. Note that this is a getter, not a lens; the Swarm.Game.Entity module carefully maintains some internal invariants ensuring that hashes work properly, and by golly, no one else is going to mess that up.
Entity map
An EntityMap
is a data structure containing all the loaded
entities, allowing them to be looked up either by name or by what
capabilities they provide (if any).
Instances
buildEntityMap :: [Entity] -> EntityMap Source #
Build an EntityMap
from a list of entities. The idea is that
this will be called once at startup, when loading the entities
from a file; see loadEntities
.
loadEntities :: MonadIO m => m (Either Text EntityMap) Source #
Load entities from a data file called entities.yaml
, producing
either an EntityMap
or a pretty-printed parse error.
deviceForCap :: Capability -> EntityMap -> [Entity] Source #
Find all entities which are devices that provide the given capability.
Inventories
An inventory is really just a bag/multiset of entities. That is, it contains some entities, along with the number of times each occurs. Entities can be looked up directly, or by name.
Instances
FromJSON Inventory Source # | |
ToJSON Inventory Source # | |
Defined in Swarm.Game.Entity | |
Generic Inventory Source # | |
Show Inventory Source # | |
Eq Inventory Source # | Inventories are compared by hash for efficiency. |
Hashable Inventory Source # | |
Defined in Swarm.Game.Entity | |
type Rep Inventory Source # | |
Defined in Swarm.Game.Entity type Rep Inventory = D1 ('MetaData "Inventory" "Swarm.Game.Entity" "swarm-0.1.0.0-CFIPFkeeTOhKLDsfeG4aYn" 'False) (C1 ('MetaCons "Inventory" 'PrefixI 'True) (S1 ('MetaSel ('Just "counts") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 (IntMap (Count, Entity))) :*: (S1 ('MetaSel ('Just "byName") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 (Map Text IntSet)) :*: S1 ('MetaSel ('Just "inventoryHash") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Int)))) |
A convenient synonym to remind us when an Int
is supposed to
represent how many of something we have.
Construction
fromElems :: [(Count, Entity)] -> Inventory Source #
Create an inventory from a list of entities and their counts.
Lookup
lookup :: Entity -> Inventory -> Count Source #
Look up an entity in an inventory, returning the number of copies contained.
lookupByName :: Text -> Inventory -> [Entity] Source #
Look up an entity by name in an inventory, returning a list of
matching entities. Note, if this returns some entities, it does
*not* mean we necessarily have any in our inventory! It just
means we *know about* them. If you want to know whether you have
any, use lookup
and see whether the resulting Count
is
positive, or just use countByName
in the first place.
countByName :: Text -> Inventory -> Count Source #
Look up an entity by name and see how many there are in the
inventory. If there are multiple entities with the same name, it
just picks the first one returned from lookupByName
.
contains :: Inventory -> Entity -> Bool Source #
Check whether an inventory contains at least one of a given entity.
contains0plus :: Entity -> Inventory -> Bool Source #
Check whether an inventory has an entry for entity (used by robots).
elems :: Inventory -> [(Count, Entity)] Source #
Get the entities in an inventory and their associated counts.
isSubsetOf :: Inventory -> Inventory -> Bool Source #
Check if the first inventory is a subset of the second. Note that entities with a count of 0 are ignored.
isEmpty :: Inventory -> Bool Source #
Check whether an inventory is empty, meaning that it contains 0 total entities (although it may still know about some entities, that is, have them as keys with a count of 0).
inventoryCapabilities :: Inventory -> Set Capability Source #
Compute the set of capabilities provided by the devices in an inventory.
Modification
insert :: Entity -> Inventory -> Inventory Source #
Insert an entity into an inventory. If the inventory already contains this entity, then only its count will be incremented.
insertCount :: Count -> Entity -> Inventory -> Inventory Source #
Insert a certain number of copies of an entity into an inventory. If the inventory already contains this entity, then only its count will be incremented.
delete :: Entity -> Inventory -> Inventory Source #
Delete a single copy of a certain entity from an inventory.
deleteCount :: Count -> Entity -> Inventory -> Inventory Source #
Delete a specified number of copies of an entity from an inventory.