World
The World is what is given to the general HcraftEnv class to instanciate each individual HierarchyCraft environment.
It represents the mapping between items, zones and zone items to their state indexes.
A world can always be built from a list of transformations using
world_from_transformations
.
Example
Build a World from a list of transformations, starting in zone "Start zone", with items "Head" and 2 "Hand", with a zone "Secret zone" containing items "Secret item" and 3 "Gold tresure".
from hcraft.elements import Item, Stack, Zone, world_from_transformations
transformations: List["Transformation"] = ...
world = world_from_transformations(
transformations=transformations,
start_zone=Zone("Start zone"),
start_items=[Item("Head"), Stack(Item("Hand"), 2)],
start_zones_items={
Zone("Secret zone"): [Item("Secret item"), Stack(Item("Gold tresure"), 3)]
}
)
1"""# World 2 3The World is what is given to the general HcraftEnv class 4to instanciate each individual HierarchyCraft environment. 5 6It represents the mapping between items, zones and zone items to their state indexes. 7 8A world can always be built from a list of transformations using 9`hcraft.world.world_from_transformations`. 10 11 12 13## Example 14 15Build a World from a list of transformations, 16starting in zone "Start zone", 17with items "Head" and 2 "Hand", 18with a zone "Secret zone" containing items "Secret item" and 3 "Gold tresure". 19 20 21```python 22from hcraft.elements import Item, Stack, Zone, world_from_transformations 23 24transformations: List["Transformation"] = ... 25world = world_from_transformations( 26 transformations=transformations, 27 start_zone=Zone("Start zone"), 28 start_items=[Item("Head"), Stack(Item("Hand"), 2)], 29 start_zones_items={ 30 Zone("Secret zone"): [Item("Secret item"), Stack(Item("Gold tresure"), 3)] 31 } 32) 33``` 34 35""" 36 37from dataclasses import dataclass, field 38from functools import partial 39from pathlib import Path 40from typing import Dict, List, Optional, Set, Tuple, Union 41 42from hcraft.elements import Item, Stack, Zone 43from hcraft.requirements import RequirementNode, Requirements, req_node_name 44from hcraft.transformation import Transformation, InventoryOwner 45 46 47def _default_resources_path() -> Path: 48 current_dir = Path(__file__).parent 49 return current_dir.joinpath("render", "default_resources") 50 51 52@dataclass() 53class World: 54 """Contain all elements a HierarchyCraft environment will use. 55 56 Elements are items, zones, zones_items and transformations 57 Also contain optional start_zone, start_items and start_zones_items. 58 """ 59 60 items: List[Item] 61 zones: List[Zone] 62 zones_items: List[Item] 63 transformations: List["Transformation"] = field(default_factory=list) 64 65 start_zone: Optional[Zone] = None 66 start_items: List[Stack] = field(default_factory=list) 67 start_zones_items: Dict[Zone, List[Stack]] = field(default_factory=dict) 68 69 resources_path: str = field(default_factory=_default_resources_path) 70 order_world: bool = False 71 72 def __post_init__(self): 73 self._requirements = None 74 75 if self.order_world: 76 item_rank = partial( 77 _get_node_level, self.requirements, node_type=RequirementNode.ITEM 78 ) 79 self.items.sort(key=item_rank) 80 81 zone_item_rank = partial( 82 _get_node_level, self.requirements, node_type=RequirementNode.ZONE_ITEM 83 ) 84 self.zones_items.sort(key=zone_item_rank) 85 86 zone_rank = partial( 87 _get_node_level, self.requirements, node_type=RequirementNode.ZONE 88 ) 89 self.zones.sort(key=zone_rank) 90 91 for transfo in self.transformations: 92 transfo.build(self) 93 94 @property 95 def n_items(self) -> int: 96 """Number of different items the player can have.""" 97 return len(self.items) 98 99 @property 100 def n_zones(self) -> int: 101 """Number of different zones.""" 102 return len(self.zones) 103 104 @property 105 def n_zones_items(self) -> int: 106 """Number of different items the zones can have.""" 107 return len(self.zones_items) 108 109 @property 110 def requirements(self) -> Requirements: 111 """Requirements object to draw an manipulate requirements graph. 112 113 See `hcraft.requirements` for more details. 114 115 """ 116 if self._requirements is None: 117 self._requirements = Requirements(self) 118 return self._requirements 119 120 def slot_from_item(self, item: Item) -> int: 121 """Item's slot in the world""" 122 return self.items.index(item) 123 124 def slot_from_zone(self, zone: Zone) -> int: 125 """Zone's slot in the world""" 126 return self.zones.index(zone) 127 128 def slot_from_zoneitem(self, zone: Zone) -> int: 129 """Item's slot in the world as a zone item.""" 130 return self.zones_items.index(zone) 131 132 133def world_from_transformations( 134 transformations: List["Transformation"], 135 start_zone: Optional[Zone] = None, 136 start_items: Optional[List[Union[Stack, Item]]] = None, 137 start_zones_items: Optional[Dict[Zone, List[Union[Stack, Item]]]] = None, 138 order_world: bool = True, 139) -> World: 140 """Reads the transformation to build the list of items, zones and zones_items 141 composing the world.""" 142 start_items = start_items if start_items is not None else [] 143 for i, stack in enumerate(start_items): 144 if not isinstance(stack, Stack): 145 start_items[i] = Stack(stack) 146 start_zones_items = start_zones_items if start_zones_items is not None else {} 147 for zone, items in start_zones_items.items(): 148 for i, stack in enumerate(items): 149 if not isinstance(stack, Stack): 150 start_zones_items[zone][i] = Stack(stack) 151 152 zones, items, zones_items = _start_elements( 153 start_zone, start_items, start_zones_items 154 ) 155 156 for transfo in transformations: 157 zones, items, zones_items = _transformations_elements( 158 transfo, zones, items, zones_items 159 ) 160 161 return World( 162 items=list(items), 163 zones=list(zones), 164 zones_items=list(zones_items), 165 transformations=transformations, 166 start_zone=start_zone, 167 start_items=start_items, 168 start_zones_items=start_zones_items, 169 order_world=order_world, 170 ) 171 172 173def _start_elements( 174 start_zone: Optional[Zone], 175 start_items: List[Union[Stack, Item]], 176 start_zones_items: Dict[Zone, List[Union[Stack, Item]]], 177) -> Tuple[Set[Zone], Set[Item], Set[Item]]: 178 zones = set() 179 if start_zone is not None: 180 zones.add(start_zone) 181 182 items = set(stack.item for stack in start_items) 183 zones_items = set() 184 for zone, zone_items in start_zones_items.items(): 185 zones.add(zone) 186 zones_items |= set(stack.item for stack in zone_items) 187 return zones, items, zones_items 188 189 190def _transformations_elements( 191 transfo: "Transformation", 192 zones: Set[Zone], 193 items: Set[Item], 194 zones_items: Set[Item], 195) -> Tuple[Set[Zone], Set[Item], Set[Item]]: 196 if transfo.destination is not None: 197 zones.add(transfo.destination) 198 if transfo.zone is not None: 199 zones.add(transfo.zone) 200 for owner, changes in transfo.inventory_changes.items(): 201 if owner is InventoryOwner.ZONES: 202 for _op, zones_stacks in changes.items(): 203 for zone, stacks in zones_stacks.items(): 204 zones.add(zone) 205 zones_items = _add_items_to(stacks, zones_items) 206 continue 207 for _op, stacks in changes.items(): 208 if owner is InventoryOwner.PLAYER: 209 items = _add_items_to(stacks, items) 210 continue 211 zones_items = _add_items_to(stacks, zones_items) 212 213 return zones, items, zones_items 214 215 216def _get_node_level( 217 requirements: Requirements, obj: Union[Item, Zone], node_type: RequirementNode 218): 219 node_name = req_node_name(obj, node_type=node_type) 220 return (requirements.graph.nodes[node_name].get("level", 1000), node_name) 221 222 223def _add_items_to(stacks: Optional[List[Stack]], items_set: Set[Item]): 224 if stacks is not None: 225 for stack in stacks: 226 items_set.add(stack.item) 227 return items_set 228 229 230def _add_dict_items_to( 231 dict_of_stacks: Optional[Dict[Zone, List[Stack]]], 232 items_set: Set[Item], 233 zones_set: Set[Zone], 234): 235 if dict_of_stacks is not None: 236 for zone, stacks in dict_of_stacks.items(): 237 zones_set.add(zone) 238 items_set = _add_items_to(stacks, items_set) 239 return items_set, zones_set
API Documentation
53@dataclass() 54class World: 55 """Contain all elements a HierarchyCraft environment will use. 56 57 Elements are items, zones, zones_items and transformations 58 Also contain optional start_zone, start_items and start_zones_items. 59 """ 60 61 items: List[Item] 62 zones: List[Zone] 63 zones_items: List[Item] 64 transformations: List["Transformation"] = field(default_factory=list) 65 66 start_zone: Optional[Zone] = None 67 start_items: List[Stack] = field(default_factory=list) 68 start_zones_items: Dict[Zone, List[Stack]] = field(default_factory=dict) 69 70 resources_path: str = field(default_factory=_default_resources_path) 71 order_world: bool = False 72 73 def __post_init__(self): 74 self._requirements = None 75 76 if self.order_world: 77 item_rank = partial( 78 _get_node_level, self.requirements, node_type=RequirementNode.ITEM 79 ) 80 self.items.sort(key=item_rank) 81 82 zone_item_rank = partial( 83 _get_node_level, self.requirements, node_type=RequirementNode.ZONE_ITEM 84 ) 85 self.zones_items.sort(key=zone_item_rank) 86 87 zone_rank = partial( 88 _get_node_level, self.requirements, node_type=RequirementNode.ZONE 89 ) 90 self.zones.sort(key=zone_rank) 91 92 for transfo in self.transformations: 93 transfo.build(self) 94 95 @property 96 def n_items(self) -> int: 97 """Number of different items the player can have.""" 98 return len(self.items) 99 100 @property 101 def n_zones(self) -> int: 102 """Number of different zones.""" 103 return len(self.zones) 104 105 @property 106 def n_zones_items(self) -> int: 107 """Number of different items the zones can have.""" 108 return len(self.zones_items) 109 110 @property 111 def requirements(self) -> Requirements: 112 """Requirements object to draw an manipulate requirements graph. 113 114 See `hcraft.requirements` for more details. 115 116 """ 117 if self._requirements is None: 118 self._requirements = Requirements(self) 119 return self._requirements 120 121 def slot_from_item(self, item: Item) -> int: 122 """Item's slot in the world""" 123 return self.items.index(item) 124 125 def slot_from_zone(self, zone: Zone) -> int: 126 """Zone's slot in the world""" 127 return self.zones.index(zone) 128 129 def slot_from_zoneitem(self, zone: Zone) -> int: 130 """Item's slot in the world as a zone item.""" 131 return self.zones_items.index(zone)
Contain all elements a HierarchyCraft environment will use.
Elements are items, zones, zones_items and transformations Also contain optional start_zone, start_items and start_zones_items.
95 @property 96 def n_items(self) -> int: 97 """Number of different items the player can have.""" 98 return len(self.items)
Number of different items the player can have.
100 @property 101 def n_zones(self) -> int: 102 """Number of different zones.""" 103 return len(self.zones)
Number of different zones.
105 @property 106 def n_zones_items(self) -> int: 107 """Number of different items the zones can have.""" 108 return len(self.zones_items)
Number of different items the zones can have.
110 @property 111 def requirements(self) -> Requirements: 112 """Requirements object to draw an manipulate requirements graph. 113 114 See `hcraft.requirements` for more details. 115 116 """ 117 if self._requirements is None: 118 self._requirements = Requirements(self) 119 return self._requirements
Requirements object to draw an manipulate requirements graph.
See hcraft.requirements
for more details.
121 def slot_from_item(self, item: Item) -> int: 122 """Item's slot in the world""" 123 return self.items.index(item)
Item's slot in the world
125 def slot_from_zone(self, zone: Zone) -> int: 126 """Zone's slot in the world""" 127 return self.zones.index(zone)
Zone's slot in the world
134def world_from_transformations( 135 transformations: List["Transformation"], 136 start_zone: Optional[Zone] = None, 137 start_items: Optional[List[Union[Stack, Item]]] = None, 138 start_zones_items: Optional[Dict[Zone, List[Union[Stack, Item]]]] = None, 139 order_world: bool = True, 140) -> World: 141 """Reads the transformation to build the list of items, zones and zones_items 142 composing the world.""" 143 start_items = start_items if start_items is not None else [] 144 for i, stack in enumerate(start_items): 145 if not isinstance(stack, Stack): 146 start_items[i] = Stack(stack) 147 start_zones_items = start_zones_items if start_zones_items is not None else {} 148 for zone, items in start_zones_items.items(): 149 for i, stack in enumerate(items): 150 if not isinstance(stack, Stack): 151 start_zones_items[zone][i] = Stack(stack) 152 153 zones, items, zones_items = _start_elements( 154 start_zone, start_items, start_zones_items 155 ) 156 157 for transfo in transformations: 158 zones, items, zones_items = _transformations_elements( 159 transfo, zones, items, zones_items 160 ) 161 162 return World( 163 items=list(items), 164 zones=list(zones), 165 zones_items=list(zones_items), 166 transformations=transformations, 167 start_zone=start_zone, 168 start_items=start_items, 169 start_zones_items=start_zones_items, 170 order_world=order_world, 171 )
Reads the transformation to build the list of items, zones and zones_items composing the world.