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

@dataclass()
class World:
 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.

World( items: List[hcraft.Item], zones: List[hcraft.Zone], zones_items: List[hcraft.Item], transformations: List[hcraft.Transformation] = <factory>, start_zone: Optional[hcraft.Zone] = None, start_items: List[hcraft.Stack] = <factory>, start_zones_items: Dict[hcraft.Zone, List[hcraft.Stack]] = <factory>, resources_path: str = <factory>, order_world: bool = False)
items: List[hcraft.Item]
zones: List[hcraft.Zone]
zones_items: List[hcraft.Item]
transformations: List[hcraft.Transformation]
start_zone: Optional[hcraft.Zone] = None
start_items: List[hcraft.Stack]
start_zones_items: Dict[hcraft.Zone, List[hcraft.Stack]]
resources_path: str
order_world: bool = False
n_items: int
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.

n_zones: int
100    @property
101    def n_zones(self) -> int:
102        """Number of different zones."""
103        return len(self.zones)

Number of different zones.

n_zones_items: int
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.

requirements: hcraft.requirements.Requirements
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.

def slot_from_item(self, item: hcraft.Item) -> int:
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

def slot_from_zone(self, zone: hcraft.Zone) -> int:
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

def slot_from_zoneitem(self, zone: hcraft.Zone) -> int:
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)

Item's slot in the world as a zone item.

def world_from_transformations( transformations: List[hcraft.Transformation], start_zone: Optional[hcraft.Zone] = None, start_items: Optional[List[Union[hcraft.Stack, hcraft.Item]]] = None, start_zones_items: Optional[Dict[hcraft.Zone, List[Union[hcraft.Stack, hcraft.Item]]]] = None, order_world: bool = True) -> 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.