Recursive HierarchyCraft Environments

The goal of the environment is to get the last item. But each item requires all the previous items, hence the number of actions required is exponential with the number of items.

Example:
>>> env = RecursiveHcraft(n_items=4)
For example, if there is 4 items, the last item is 3.
But 3 requires all previous items: {2, 1, 0}.
And 2 requires all previous items: {1, 0}.
And 1 requires all previous items: {0}.
Finally Item 0 can be obtained directly.
Thus the number of actions required is $1 + 2 + 4 + 1 = 8 = 2^4$.

Requirements graph (n_items=6):

 1"""# Recursive HierarchyCraft Environments
 2
 3The goal of the environment is to get the last item.
 4But each item requires all the previous items,
 5hence the number of actions required is exponential with the number of items.
 6
 7Example:
 8    >>> env = RecursiveHcraft(n_items=4)
 9    For example, if there is 4 items, the last item is 3.
10    But 3 requires all previous items: {2, 1, 0}.
11    And 2 requires all previous items: {1, 0}.
12    And 1 requires all previous items: {0}.
13    Finally Item 0 can be obtained directly.
14    Thus the number of actions required is $1 + 2 + 4 + 1 = 8 = 2^4$.
15
16Requirements graph (n_items=6):
17<div class="graph">
18.. include:: ../../../docs/images/requirements_graphs/RecursiveHcraft-I6.html
19</div>
20"""
21
22from typing import List
23
24from hcraft.elements import Item
25from hcraft.env import HcraftEnv
26from hcraft.transformation import Transformation, Use, Yield, PLAYER
27from hcraft.task import GetItemTask
28from hcraft.world import world_from_transformations
29
30# gym is an optional dependency
31try:
32    import gymnasium as gym
33
34    gym.register(
35        id="RecursiveHcraft-v1",
36        entry_point="hcraft.examples.recursive:RecursiveHcraftEnv",
37    )
38
39except ImportError:
40    pass
41
42
43class RecursiveHcraftEnv(HcraftEnv):
44    """RecursiveHcraft Environment"""
45
46    def __init__(self, n_items: int = 6, **kwargs):
47        items = [Item(str(i)) for i in range(n_items)]
48        self.n_items = n_items
49        transformations = self.build_transformations(items)
50        world = world_from_transformations(transformations)
51        if "purpose" not in kwargs:
52            kwargs["purpose"] = GetItemTask(items[-1])
53        super().__init__(
54            world,
55            name=f"RecursiveHcraft-I{n_items}",
56            **kwargs,
57        )
58
59    def build_transformations(self, items: List[Item]) -> List[Transformation]:
60        """Build transformations to make every item accessible.
61
62        Args:
63            items: List of items.
64
65        Returns:
66            List of transformations.
67
68        """
69        transformation = []
70
71        for index, item in enumerate(items):
72            inventory_changes = [Yield(PLAYER, item)]
73            if index > 0:
74                inventory_changes += [
75                    Use(PLAYER, items[item_id], consume=1) for item_id in range(index)
76                ]
77            new_recipe = Transformation(inventory_changes=inventory_changes)
78            transformation.append(new_recipe)
79
80        return transformation

API Documentation

class RecursiveHcraftEnv(typing.Generic[~ObsType, ~ActType]):
44class RecursiveHcraftEnv(HcraftEnv):
45    """RecursiveHcraft Environment"""
46
47    def __init__(self, n_items: int = 6, **kwargs):
48        items = [Item(str(i)) for i in range(n_items)]
49        self.n_items = n_items
50        transformations = self.build_transformations(items)
51        world = world_from_transformations(transformations)
52        if "purpose" not in kwargs:
53            kwargs["purpose"] = GetItemTask(items[-1])
54        super().__init__(
55            world,
56            name=f"RecursiveHcraft-I{n_items}",
57            **kwargs,
58        )
59
60    def build_transformations(self, items: List[Item]) -> List[Transformation]:
61        """Build transformations to make every item accessible.
62
63        Args:
64            items: List of items.
65
66        Returns:
67            List of transformations.
68
69        """
70        transformation = []
71
72        for index, item in enumerate(items):
73            inventory_changes = [Yield(PLAYER, item)]
74            if index > 0:
75                inventory_changes += [
76                    Use(PLAYER, items[item_id], consume=1) for item_id in range(index)
77                ]
78            new_recipe = Transformation(inventory_changes=inventory_changes)
79            transformation.append(new_recipe)
80
81        return transformation

RecursiveHcraft Environment

RecursiveHcraftEnv(n_items: int = 6, **kwargs)
47    def __init__(self, n_items: int = 6, **kwargs):
48        items = [Item(str(i)) for i in range(n_items)]
49        self.n_items = n_items
50        transformations = self.build_transformations(items)
51        world = world_from_transformations(transformations)
52        if "purpose" not in kwargs:
53            kwargs["purpose"] = GetItemTask(items[-1])
54        super().__init__(
55            world,
56            name=f"RecursiveHcraft-I{n_items}",
57            **kwargs,
58        )
Arguments:
  • world: World defining the environment.
  • purpose: Purpose of the player, defining rewards and termination. Defaults to None, hence a sandbox environment.
  • invalid_reward: Reward given to the agent for invalid actions. Defaults to -1.0.
  • render_window: Window using to render the environment with pygame.
  • name: Name of the environement. Defaults to 'HierarchyCraft'.
  • max_step: (Optional[int], optional): Maximum number of steps before episode truncation. If None, never truncates the episode. Defaults to None.
n_items
def build_transformations( self, items: List[hcraft.Item]) -> List[hcraft.Transformation]:
60    def build_transformations(self, items: List[Item]) -> List[Transformation]:
61        """Build transformations to make every item accessible.
62
63        Args:
64            items: List of items.
65
66        Returns:
67            List of transformations.
68
69        """
70        transformation = []
71
72        for index, item in enumerate(items):
73            inventory_changes = [Yield(PLAYER, item)]
74            if index > 0:
75                inventory_changes += [
76                    Use(PLAYER, items[item_id], consume=1) for item_id in range(index)
77                ]
78            new_recipe = Transformation(inventory_changes=inventory_changes)
79            transformation.append(new_recipe)
80
81        return transformation

Build transformations to make every item accessible.

Arguments:
  • items: List of items.
Returns:

List of transformations.