mettagrid 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mettagrid might be problematic. Click here for more details.

Files changed (38) hide show
  1. mettagrid-0.0.1/LICENSE +21 -0
  2. mettagrid-0.0.1/PKG-INFO +23 -0
  3. mettagrid-0.0.1/README.md +2 -0
  4. mettagrid-0.0.1/mettagrid/__init__.py +0 -0
  5. mettagrid-0.0.1/mettagrid/actions/__init__.py +0 -0
  6. mettagrid-0.0.1/mettagrid/actions/actions.pxd +31 -0
  7. mettagrid-0.0.1/mettagrid/actions/actions.pyx +59 -0
  8. mettagrid-0.0.1/mettagrid/actions/attack.pxd +6 -0
  9. mettagrid-0.0.1/mettagrid/actions/attack.pyx +61 -0
  10. mettagrid-0.0.1/mettagrid/actions/gift.pxd +4 -0
  11. mettagrid-0.0.1/mettagrid/actions/gift.pyx +24 -0
  12. mettagrid-0.0.1/mettagrid/actions/move.pxd +4 -0
  13. mettagrid-0.0.1/mettagrid/actions/move.pyx +31 -0
  14. mettagrid-0.0.1/mettagrid/actions/rotate.pxd +4 -0
  15. mettagrid-0.0.1/mettagrid/actions/rotate.pyx +28 -0
  16. mettagrid-0.0.1/mettagrid/actions/shield.pxd +4 -0
  17. mettagrid-0.0.1/mettagrid/actions/shield.pyx +26 -0
  18. mettagrid-0.0.1/mettagrid/actions/use.pxd +4 -0
  19. mettagrid-0.0.1/mettagrid/actions/use.pyx +66 -0
  20. mettagrid-0.0.1/mettagrid/config/game_builder.py +101 -0
  21. mettagrid-0.0.1/mettagrid/config/sample_config.py +27 -0
  22. mettagrid-0.0.1/mettagrid/mettagrid.pyx +78 -0
  23. mettagrid-0.0.1/mettagrid/mettagrid_env.py +181 -0
  24. mettagrid-0.0.1/mettagrid/objects.pxd +197 -0
  25. mettagrid-0.0.1/mettagrid/objects.pyx +67 -0
  26. mettagrid-0.0.1/mettagrid/renderer/assets/ore-0.png +0 -0
  27. mettagrid-0.0.1/mettagrid/renderer/assets/ore-1.png +0 -0
  28. mettagrid-0.0.1/mettagrid/renderer/assets/ore-2.png +0 -0
  29. mettagrid-0.0.1/mettagrid/renderer/assets/ore-3.png +0 -0
  30. mettagrid-0.0.1/mettagrid/renderer/assets/ore-4.png +0 -0
  31. mettagrid-0.0.1/mettagrid/renderer/assets/ore-5.png +0 -0
  32. mettagrid-0.0.1/mettagrid/renderer/assets/ore-6.png +0 -0
  33. mettagrid-0.0.1/mettagrid/renderer/assets/puffer_chars.png +0 -0
  34. mettagrid-0.0.1/mettagrid/renderer/assets/tiny_galaxy_items.png +0 -0
  35. mettagrid-0.0.1/mettagrid/renderer/assets/tiny_galaxy_monsters.png +0 -0
  36. mettagrid-0.0.1/mettagrid/renderer/assets/wall1-0.png +0 -0
  37. mettagrid-0.0.1/mettagrid/renderer/raylib_client.py +180 -0
  38. mettagrid-0.0.1/pyproject.toml +22 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 David Bloomin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.1
2
+ Name: mettagrid
3
+ Version: 0.0.1
4
+ Summary: A fast grid-based open-ended MARL environment
5
+ Home-page: https://daveey.github.io
6
+ License: MIT
7
+ Keywords: puffergrid,gridworld,minigrid,rl,reinforcement-learning,environment,gym
8
+ Author: David Bloomin
9
+ Author-email: daveey@gmail.com
10
+ Requires-Python: >=3.10,<4.0
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Dist: cython (>=3.0.11,<4.0.0)
17
+ Requires-Dist: numpy (>=1.21.0,<2.0.0)
18
+ Project-URL: Repository, https://github.com/Metta-AI/mettagrid
19
+ Description-Content-Type: text/markdown
20
+
21
+ # mettagrid
22
+ A fast grid-based open-ended MARL environment
23
+
@@ -0,0 +1,2 @@
1
+ # mettagrid
2
+ A fast grid-based open-ended MARL environment
File without changes
File without changes
@@ -0,0 +1,31 @@
1
+
2
+ from libc.stdio cimport printf
3
+ from libcpp.string cimport string
4
+ from libcpp.map cimport map
5
+
6
+ from puffergrid.grid_object cimport TypeId, GridObjectId
7
+ from puffergrid.action cimport ActionHandler, ActionArg
8
+ from mettagrid.objects cimport Agent
9
+
10
+ cdef struct StatNames:
11
+ string action
12
+ string action_energy
13
+ map[TypeId, string] target
14
+ map[TypeId, string] target_energy
15
+
16
+ cdef class MettaActionHandler(ActionHandler):
17
+ cdef StatNames _stats
18
+ cdef string action_name
19
+ cdef int action_cost
20
+
21
+ cdef char handle_action(
22
+ self,
23
+ unsigned int actor_id,
24
+ GridObjectId actor_object_id,
25
+ ActionArg arg)
26
+
27
+ cdef char _handle_action(
28
+ self,
29
+ unsigned int actor_id,
30
+ Agent * actor,
31
+ ActionArg arg)
@@ -0,0 +1,59 @@
1
+
2
+ from libc.stdio cimport printf
3
+
4
+ from omegaconf import OmegaConf
5
+
6
+ from puffergrid.grid_object cimport GridObjectId
7
+ from puffergrid.action cimport ActionHandler, ActionArg
8
+ from mettagrid.objects cimport Agent, ObjectTypeNames
9
+
10
+ cdef extern from "<string>" namespace "std":
11
+ string to_string(int val)
12
+
13
+ cdef class MettaActionHandler(ActionHandler):
14
+ def __init__(self, cfg: OmegaConf, action_name, action_cost=0):
15
+ self.action_name = action_name
16
+
17
+ self._stats.action = "action." + action_name
18
+ self._stats.action_energy = "action." + action_name + ".energy"
19
+
20
+ for t, n in enumerate(ObjectTypeNames):
21
+ self._stats.target[t] = self._stats.action + "." + n
22
+ self._stats.target_energy[t] = self._stats.action_energy + "." + n
23
+
24
+ self.action_cost = cfg.cost
25
+
26
+ cdef char handle_action(
27
+ self,
28
+ unsigned int actor_id,
29
+ GridObjectId actor_object_id,
30
+ ActionArg arg):
31
+
32
+ cdef Agent *actor = <Agent*>self.env._grid.object(actor_object_id)
33
+
34
+ if actor.frozen:
35
+ return False
36
+
37
+ if actor.energy < self.action_cost:
38
+ return False
39
+
40
+ actor.energy -= self.action_cost
41
+ self.env._stats.agent_add(actor_id, self._stats.action_energy.c_str(), self.action_cost)
42
+
43
+ cdef char result = self._handle_action(actor_id, actor, arg)
44
+
45
+ if result:
46
+ self.env._stats.agent_incr(actor_id, self._stats.action.c_str())
47
+
48
+ return result
49
+
50
+ cdef char _handle_action(
51
+ self,
52
+ unsigned int actor_id,
53
+ Agent * actor,
54
+ ActionArg arg):
55
+ return False
56
+
57
+
58
+
59
+
@@ -0,0 +1,6 @@
1
+ from mettagrid.actions.actions cimport MettaActionHandler
2
+
3
+ cdef class Attack(MettaActionHandler):
4
+ cdef public int damage
5
+
6
+ pass
@@ -0,0 +1,61 @@
1
+
2
+ from libc.stdio cimport printf
3
+ from omegaconf import OmegaConf
4
+
5
+ from puffergrid.grid_object cimport GridLocation, GridObjectId, Orientation, GridObject
6
+ from puffergrid.action cimport ActionHandler, ActionArg
7
+ from mettagrid.objects cimport MettaObject, ObjectType, Usable, Altar, Agent, Events, GridLayer
8
+ from mettagrid.objects cimport Generator, Converter, InventoryItem, ObjectTypeNames, InventoryItemNames
9
+ from mettagrid.actions.actions cimport MettaActionHandler
10
+
11
+ cdef class Attack(MettaActionHandler):
12
+ def __init__(self, cfg: OmegaConf):
13
+ MettaActionHandler.__init__(self, cfg, "attack")
14
+
15
+ cdef char _handle_action(
16
+ self,
17
+ unsigned int actor_id,
18
+ Agent * actor,
19
+ ActionArg arg):
20
+
21
+ if arg > 9 or arg < 1:
22
+ return False
23
+
24
+ cdef short distance = 0
25
+ cdef short offset = 0
26
+ distance = 1 + (arg - 1) // 3
27
+ offset = (arg - 1) % 3 - 1
28
+
29
+ cdef GridLocation target_loc = self.env._grid.relative_location(
30
+ actor.location,
31
+ <Orientation>actor.orientation,
32
+ distance, offset)
33
+
34
+ target_loc.layer = GridLayer.Agent_Layer
35
+ cdef Agent * agent_target = <Agent *>self.env._grid.object_at(target_loc)
36
+ if agent_target:
37
+ self.env._stats.agent_incr(actor_id, self._stats.target[agent_target._type_id].c_str())
38
+ if agent_target.shield and agent_target.energy >= self.damage:
39
+ agent_target.energy -= self.damage
40
+ self.env._stats.agent_add(actor_id, "shield_damage", self.damage)
41
+ else:
42
+ self.env._stats.agent_add(actor_id, "shield_damage", agent_target.energy)
43
+ agent_target.energy = 0
44
+ agent_target.shield = False
45
+ agent_target.frozen = True
46
+ self.env._stats.agent_incr(actor_id, "attack.frozen")
47
+ return True
48
+
49
+ target_loc.layer = GridLayer.Object_Layer
50
+ cdef MettaObject * object_target = <MettaObject *>self.env._grid.object_at(target_loc)
51
+ if object_target:
52
+ self.env._stats.agent_incr(actor_id, self._stats.target[object_target._type_id].c_str())
53
+ object_target.hp -= 1
54
+ self.env._stats.agent_incr(actor_id, "damage." + ObjectTypeNames[object_target._type_id])
55
+ if object_target.hp <= 0:
56
+ self.env._grid.remove_object(object_target)
57
+ self.env._stats.agent_incr(actor_id, "destroyed." + ObjectTypeNames[object_target._type_id])
58
+
59
+ return True
60
+
61
+ return False
@@ -0,0 +1,4 @@
1
+ from mettagrid.actions.actions cimport MettaActionHandler
2
+
3
+ cdef class Gift(MettaActionHandler):
4
+ pass
@@ -0,0 +1,24 @@
1
+
2
+ from libc.stdio cimport printf
3
+ from omegaconf import OmegaConf
4
+
5
+ from puffergrid.grid_object cimport GridLocation, GridObjectId, Orientation, GridObject
6
+ from puffergrid.action cimport ActionHandler, ActionArg
7
+ from mettagrid.objects cimport MettaObject, ObjectType, Usable, Altar, Agent, Events, GridLayer
8
+ from mettagrid.objects cimport Generator, Converter, InventoryItem, ObjectTypeNames, InventoryItemNames
9
+ from mettagrid.actions.actions cimport MettaActionHandler
10
+
11
+
12
+ cdef class Gift(MettaActionHandler):
13
+ def __init__(self, cfg: OmegaConf):
14
+ MettaActionHandler.__init__(self, cfg, "gift")
15
+
16
+ cdef char _handle_action(
17
+ self,
18
+ unsigned int actor_id,
19
+ Agent * actor,
20
+ ActionArg arg):
21
+ return False
22
+
23
+
24
+
@@ -0,0 +1,4 @@
1
+ from mettagrid.actions.actions cimport MettaActionHandler
2
+
3
+ cdef class Move(MettaActionHandler):
4
+ pass
@@ -0,0 +1,31 @@
1
+
2
+ from libc.stdio cimport printf
3
+
4
+ from omegaconf import OmegaConf
5
+
6
+ from puffergrid.grid_object cimport GridLocation, GridObjectId, GridObject, Orientation
7
+ from puffergrid.action cimport ActionHandler, ActionArg
8
+ from mettagrid.objects cimport MettaObject, ObjectType, Usable, Altar, Agent, Events, GridLayer
9
+ from mettagrid.objects cimport Generator, Converter, InventoryItem, ObjectTypeNames, InventoryItemNames
10
+ from mettagrid.actions.actions cimport MettaActionHandler
11
+
12
+ cdef class Move(MettaActionHandler):
13
+ def __init__(self, cfg: OmegaConf):
14
+ MettaActionHandler.__init__(self, cfg, "move")
15
+
16
+ cdef char _handle_action(
17
+ self,
18
+ unsigned int actor_id,
19
+ Agent * actor,
20
+ ActionArg arg):
21
+
22
+ cdef unsigned short direction = arg
23
+ if direction >= 2:
24
+ return False
25
+
26
+ cdef Orientation orientation = <Orientation>((actor.orientation + 2*(direction)) % 4)
27
+ cdef GridLocation old_loc = actor.location
28
+ cdef GridLocation new_loc = self.env._grid.relative_location(old_loc, orientation)
29
+ if not self.env._grid.is_empty(new_loc.r, new_loc.c):
30
+ return False
31
+ return self.env._grid.move_object(actor.id, new_loc)
@@ -0,0 +1,4 @@
1
+ from mettagrid.actions.actions cimport MettaActionHandler
2
+
3
+ cdef class Rotate(MettaActionHandler):
4
+ pass
@@ -0,0 +1,28 @@
1
+
2
+ from libc.stdio cimport printf
3
+
4
+ from omegaconf import OmegaConf
5
+
6
+ from puffergrid.grid_object cimport GridLocation, GridObjectId, Orientation, GridObject
7
+ from puffergrid.action cimport ActionHandler, ActionArg
8
+ from mettagrid.objects cimport MettaObject, ObjectType, Usable, Altar, Agent, Events, GridLayer
9
+ from mettagrid.objects cimport Generator, Converter, InventoryItem, ObjectTypeNames, InventoryItemNames
10
+ from mettagrid.actions.actions cimport MettaActionHandler
11
+
12
+
13
+ cdef class Rotate(MettaActionHandler):
14
+ def __init__(self, cfg: OmegaConf):
15
+ MettaActionHandler.__init__(self, cfg, "rotate")
16
+
17
+ cdef char _handle_action(
18
+ self,
19
+ unsigned int actor_id,
20
+ Agent * actor,
21
+ ActionArg arg):
22
+
23
+ cdef unsigned short orientation = arg
24
+ if orientation >= 4:
25
+ return False
26
+
27
+ actor.orientation = orientation
28
+ return True
@@ -0,0 +1,4 @@
1
+ from mettagrid.actions.actions cimport MettaActionHandler
2
+
3
+ cdef class Shield(MettaActionHandler):
4
+ pass
@@ -0,0 +1,26 @@
1
+
2
+ from libc.stdio cimport printf
3
+
4
+ from omegaconf import OmegaConf
5
+
6
+ from puffergrid.grid_object cimport GridLocation, GridObjectId, Orientation, GridObject
7
+ from puffergrid.action cimport ActionHandler, ActionArg
8
+ from mettagrid.objects cimport MettaObject, ObjectType, Usable, Altar, Agent, Events, GridLayer
9
+ from mettagrid.objects cimport Generator, Converter, InventoryItem, ObjectTypeNames, InventoryItemNames
10
+ from mettagrid.actions.actions cimport MettaActionHandler
11
+
12
+ cdef class Shield(MettaActionHandler):
13
+ def __init__(self, cfg: OmegaConf):
14
+ MettaActionHandler.__init__(self, cfg, "shield")
15
+
16
+ cdef char _handle_action(
17
+ self,
18
+ unsigned int actor_id,
19
+ Agent * actor,
20
+ ActionArg arg):
21
+
22
+ if actor.shield:
23
+ actor.shield = True
24
+ else:
25
+ actor.shield = False
26
+
@@ -0,0 +1,4 @@
1
+ from mettagrid.actions.actions cimport MettaActionHandler
2
+
3
+ cdef class Use(MettaActionHandler):
4
+ pass
@@ -0,0 +1,66 @@
1
+
2
+ from libc.stdio cimport printf
3
+
4
+ from omegaconf import OmegaConf
5
+
6
+ from puffergrid.grid_object cimport GridLocation, GridObjectId, Orientation, GridObject
7
+ from puffergrid.action cimport ActionHandler, ActionArg
8
+ from mettagrid.objects cimport MettaObject, ObjectType, Usable, Altar, Agent, Events, GridLayer
9
+ from mettagrid.objects cimport Generator, Converter, InventoryItem, ObjectTypeNames, InventoryItemNames
10
+ from mettagrid.actions.actions cimport MettaActionHandler
11
+
12
+ cdef class Use(MettaActionHandler):
13
+ def __init__(self, cfg: OmegaConf):
14
+ MettaActionHandler.__init__(self, cfg, "use")
15
+
16
+ cdef char _handle_action(
17
+ self,
18
+ unsigned int actor_id,
19
+ Agent * actor,
20
+ ActionArg arg):
21
+
22
+ cdef GridLocation target_loc = self.env._grid.relative_location(
23
+ actor.location,
24
+ <Orientation>actor.orientation
25
+ )
26
+ target_loc.layer = GridLayer.Object_Layer
27
+ cdef MettaObject *target = <MettaObject*>self.env._grid.object_at(target_loc)
28
+ if target == NULL:
29
+ return False
30
+
31
+ if not target.usable(actor):
32
+ return False
33
+
34
+ cdef Usable *usable = <Usable*> target
35
+ actor.energy -= usable.use_cost
36
+
37
+ usable.ready = 0
38
+ self.env._event_manager.schedule_event(Events.Reset, usable.cooldown, usable.id, 0)
39
+
40
+ self.env._stats.agent_incr(actor_id, self._stats.target[target._type_id].c_str())
41
+ self.env._stats.agent_add(actor_id, self._stats.target_energy[target._type_id].c_str(), usable.use_cost + self.action_cost)
42
+
43
+ if target._type_id == ObjectType.AltarT:
44
+ self.env._rewards[actor_id] += 1
45
+
46
+ cdef Generator *generator
47
+ if target._type_id == ObjectType.GeneratorT:
48
+ generator = <Generator*>target
49
+ generator.r1 -= 1
50
+ actor.update_inventory(InventoryItem.r1, 1)
51
+ self.env._stats.agent_incr(actor_id, "r1.gained")
52
+ self.env._stats.game_incr("r1.harvested")
53
+
54
+ cdef Converter *converter
55
+ if target._type_id == ObjectType.ConverterT:
56
+ converter = <Converter*>target
57
+ actor.update_inventory(converter.input_resource, -1)
58
+ self.env._stats.agent_incr(actor_id, InventoryItemNames[converter.input_resource] + ".used")
59
+
60
+ actor.update_inventory(converter.output_resource, 1)
61
+ self.env._stats.agent_incr(actor_id, InventoryItemNames[converter.input_resource] + ".gained")
62
+
63
+ actor.energy += converter.output_energy
64
+ self.env._stats.agent_add(actor_id, "energy.gained", converter.output_energy)
65
+
66
+ return True
@@ -0,0 +1,101 @@
1
+ from typing import Dict, List
2
+ import numpy as np
3
+ import yaml
4
+ from omegaconf import OmegaConf
5
+
6
+ class NoAliasDumper(yaml.Dumper):
7
+ def ignore_aliases(self, data):
8
+ return True
9
+
10
+ class MettaGridGameBuilder():
11
+ def __init__(
12
+ self,
13
+ obs_width: int,
14
+ obs_height: int,
15
+ tile_size: int,
16
+ max_steps: int,
17
+ num_agents: int,
18
+ no_energy_steps: int,
19
+ objects,
20
+ actions,
21
+ map):
22
+
23
+ self.obs_width = obs_width
24
+ self.obs_height = obs_height
25
+ self.tile_size = tile_size
26
+ self.num_agents = num_agents
27
+ self.max_steps = max_steps
28
+
29
+ self._symbols = {
30
+ "agent": "A",
31
+ "altar": "a",
32
+ "converter": "c",
33
+ "generator": "g",
34
+ "wall": "w",
35
+ "empty": " ",
36
+ }
37
+
38
+ self.no_energy_steps = no_energy_steps
39
+ objects = OmegaConf.create(objects)
40
+ self.object_configs = objects
41
+ actions = OmegaConf.create(actions)
42
+ self.action_configs = actions
43
+ self.map_config = OmegaConf.create(map)
44
+
45
+
46
+ def level(self):
47
+ layout = self.map_config.layout
48
+
49
+ if "rooms" in layout:
50
+ return self.build_map(layout.rooms)
51
+ else:
52
+ return self.build_map(
53
+ [["room"] * layout.rooms_x] * layout.rooms_y)
54
+
55
+ def build_map(self, rooms):
56
+ num_agents = 0
57
+ layers = []
58
+ for layer in rooms:
59
+ rooms = []
60
+ for room_name in layer:
61
+ room_config = self.map_config[room_name]
62
+ rooms.append(self.build_room(room_config, num_agents + 1))
63
+ num_agents += room_config.objects.agent
64
+ layers.append(np.concatenate(rooms, axis=1))
65
+ level = np.concatenate(layers, axis=0)
66
+ assert num_agents == self.num_agents, f"Number of agents in map ({num_agents}) does not match num_agents ({self.num_agents})"
67
+
68
+ footer = np.full((1, level.shape[1]), "W", dtype="U6")
69
+ footer[0, 0] = "q"
70
+
71
+ level = np.concatenate([level, footer], axis=0)
72
+ return level
73
+
74
+ def build_room(self, room_config, starting_agent=1):
75
+ symbols = []
76
+ content_width = room_config.width - 2*room_config.border
77
+ content_height = room_config.height - 2*room_config.border
78
+ area = content_width * content_height
79
+
80
+ for obj_name, count in room_config.objects.items():
81
+ symbol = self._symbols[obj_name]
82
+ if obj_name == "agent":
83
+ symbols.extend([f"{symbol}{i+starting_agent}" for i in range(count)])
84
+ else:
85
+ symbols.extend([symbol] * count)
86
+
87
+ assert(len(symbols) <= area), f"Too many objects in room: {len(symbols)} > {area}"
88
+ symbols.extend(["."] * (area - len(symbols)))
89
+ symbols = np.array(symbols).astype("U8")
90
+ np.random.shuffle(symbols)
91
+ content = symbols.reshape(content_height, content_width)
92
+ room = np.full((room_config.height, room_config.width), "W", dtype="U6")
93
+ room[room_config.border:room_config.border+content_height,
94
+ room_config.border:room_config.border+content_width] = content
95
+
96
+ return room
97
+
98
+ def termination_conditions(self):
99
+ return {
100
+ "Win": [ {"lt": ["game:max_steps", "game:step"]} ],
101
+ }
@@ -0,0 +1,27 @@
1
+
2
+
3
+ import numpy as np
4
+ from omegaconf import DictConfig, ListConfig
5
+
6
+
7
+ def sample_config(value):
8
+ if isinstance(value, int):
9
+ return value
10
+ if isinstance(value, float):
11
+ return value
12
+ if isinstance(value, DictConfig):
13
+ return {
14
+ key: sample_config(value)
15
+ for key, value in value.items()
16
+ }
17
+ if isinstance(value, ListConfig):
18
+ if len(value) == 0:
19
+ return value
20
+ if isinstance(value[0], int):
21
+ assert len(value) == 2, f"Found a list with length != 2 {value}"
22
+ return np.random.randint(value[0], value[1])
23
+ if isinstance(value[0], float):
24
+ assert len(value) == 2, f"Found a list with length != 2 {value}"
25
+ return np.random.uniform(value[0], value[1])
26
+ return value
27
+ return value
@@ -0,0 +1,78 @@
1
+
2
+ from libc.stdio cimport printf
3
+
4
+ import numpy as np
5
+ import gymnasium as gym
6
+ from omegaconf import OmegaConf
7
+
8
+ from puffergrid.grid_env cimport GridEnv
9
+
10
+ from mettagrid.objects cimport ObjectLayers, Agent, ResetHandler, Wall, Generator, Converter, Altar
11
+ from mettagrid.objects cimport MettaObservationEncoder
12
+ from mettagrid.actions.move import Move
13
+ from mettagrid.actions.rotate import Rotate
14
+ from mettagrid.actions.use import Use
15
+ from mettagrid.actions.attack import Attack
16
+ from mettagrid.actions.shield import Shield
17
+ from mettagrid.actions.gift import Gift
18
+
19
+ cdef class MettaGrid(GridEnv):
20
+ cdef:
21
+ object _cfg
22
+
23
+ def __init__(self, cfg: OmegaConf, map: np.ndarray):
24
+ self._cfg = cfg
25
+
26
+ GridEnv.__init__(
27
+ self,
28
+ cfg.num_agents,
29
+ map.shape[1],
30
+ map.shape[0],
31
+ cfg.max_steps,
32
+ dict(ObjectLayers).values(),
33
+ cfg.obs_width, cfg.obs_height,
34
+ MettaObservationEncoder(),
35
+ [
36
+ Move(cfg.actions.move),
37
+ Rotate(cfg.actions.rotate),
38
+ Use(cfg.actions.use),
39
+ Attack(cfg.actions.attack),
40
+ Shield(cfg.actions.shield),
41
+ Gift(cfg.actions.gift),
42
+ ],
43
+ [
44
+ ResetHandler()
45
+ ]
46
+ )
47
+
48
+
49
+ cdef Agent *agent
50
+ for r in range(map.shape[0]):
51
+ for c in range(map.shape[1]):
52
+ if map[r,c] == "W":
53
+ self._grid.add_object(new Wall(r, c, cfg.objects.wall))
54
+ self._stats.game_incr("objects.wall")
55
+ elif map[r,c] == "g":
56
+ self._grid.add_object(new Generator(r, c, cfg.objects.generator))
57
+ self._stats.game_incr("objects.generator")
58
+ elif map[r,c] == "c":
59
+ self._grid.add_object(new Converter(r, c, cfg.objects.converter))
60
+ self._stats.game_incr("objects.converter")
61
+ elif map[r,c] == "a":
62
+ self._grid.add_object(new Altar(r, c, cfg.objects.altar))
63
+ self._stats.game_incr("objects.altar")
64
+ elif map[r,c][0] == "A":
65
+ agent = new Agent(r, c, cfg.objects.agent)
66
+ self._grid.add_object(agent)
67
+ self.add_agent(agent)
68
+ self._stats.game_incr("objects.agent")
69
+
70
+
71
+ def render(self):
72
+ grid = self.render_ascii(["A", "#", "g", "c", "a"])
73
+ for r in grid:
74
+ print("".join(r))
75
+
76
+ @property
77
+ def action_space(self):
78
+ return gym.spaces.MultiDiscrete((self.num_actions(), 10), dtype=np.uint32)
@@ -0,0 +1,181 @@
1
+ from typing import Any, Dict
2
+
3
+ import pufferlib
4
+ import numpy as np
5
+ from omegaconf import OmegaConf
6
+
7
+ from mettagrid.config.game_builder import MettaGridGameBuilder
8
+ from mettagrid.renderer.raylib_client import MettaRaylibClient
9
+ from mettagrid.config.sample_config import sample_config
10
+ from mettagrid.mettagrid_c import MettaGrid
11
+ from pufferlib.environments.ocean.render import GridRender
12
+
13
+ class GridClient:
14
+ def __init__(self, width, height):
15
+ self._width = width
16
+ self._height = height
17
+
18
+ class MettaGridEnv(pufferlib.PufferEnv):
19
+ def __init__(self, render_mode: str, **cfg):
20
+ super().__init__()
21
+
22
+ self._render_mode = render_mode
23
+ self._cfg = OmegaConf.create(cfg)
24
+ self.make_env()
25
+
26
+ if render_mode == "human":
27
+ self._renderer = MettaRaylibClient(
28
+ self._env.map_width(), self._env.map_height(),
29
+ )
30
+ elif render_mode == "raylib":
31
+ self._renderer = GridRender(
32
+ self._env.map_width(), self._env.map_height(),
33
+ fps=10
34
+ )
35
+
36
+
37
+ def make_env(self):
38
+ game_cfg = OmegaConf.create(sample_config(self._cfg.game))
39
+ self._game_builder = MettaGridGameBuilder(**game_cfg)
40
+ level = self._game_builder.level()
41
+ self._c_env = MettaGrid(game_cfg, level)
42
+ self._grid_env = self._c_env
43
+ self._num_agents = self._c_env.num_agents()
44
+
45
+ # self._grid_env = PufferGridEnv(self._c_env)
46
+ env = self._grid_env
47
+
48
+ self._env = env
49
+ #self._env = LastActionTracker(self._grid_env)
50
+ #self._env = Kinship(**sample_config(self._cfg.kinship), env=self._env)
51
+ #self._env = RewardTracker(self._env)
52
+ #self._env = FeatureMasker(self._env, self._cfg.hidden_features)
53
+ self.done = False
54
+
55
+ def reset(self, **kwargs):
56
+ self.make_env()
57
+ if hasattr(self, "buf"):
58
+ self._c_env.set_buffers(
59
+ self.buf.observations,
60
+ self.buf.terminals,
61
+ self.buf.truncations,
62
+ self.buf.rewards)
63
+
64
+ # obs, infos = self._env.reset(**kwargs)
65
+ # self._compute_max_energy()
66
+ # return obs, infos
67
+ obs, infos = self._c_env.reset()
68
+ return obs, infos
69
+
70
+ def step(self, actions):
71
+ obs, rewards, terminated, truncated, infos = self._c_env.step(actions.astype(np.int32))
72
+
73
+ rewards_sum = rewards.sum()
74
+ if rewards_sum != 0:
75
+ reward_mean = rewards_sum / self._num_agents
76
+ rewards -= reward_mean
77
+
78
+ if terminated.all() or truncated.all():
79
+ self.done = True
80
+
81
+ stats = self._c_env.get_episode_stats()
82
+ episode_rewards = self._c_env.get_episode_rewards()
83
+ episode_rewards_sum = episode_rewards.sum()
84
+ episode_rewards_mean = episode_rewards_sum / self._num_agents
85
+
86
+ infos = {
87
+ "episode/reward.sum": episode_rewards_sum,
88
+ "episode/reward.mean": episode_rewards_mean,
89
+ "episode/reward.min": episode_rewards.min(),
90
+ "episode/reward.max": episode_rewards.max(),
91
+ "episode_length": self._c_env.current_timestep(),
92
+ }
93
+
94
+ agent_stats = {}
95
+ for a_stats in stats["agent_stats"]:
96
+ for k, v in a_stats.items():
97
+ if k not in agent_stats:
98
+ agent_stats[k] = 0
99
+ agent_stats[k] += v
100
+
101
+ for k, v in agent_stats.items():
102
+ infos[f"agent_stats/{k}"] = float(v) / self._num_agents
103
+
104
+ return obs, list(rewards), terminated.all(), truncated.all(), infos
105
+
106
+ def process_episode_stats(self, episode_stats: Dict[str, Any]):
107
+ for agent_stats in episode_stats["agent_stats"]:
108
+ extra_stats = {}
109
+ for stat_name in agent_stats.keys():
110
+ if stat_name.startswith("action_"):
111
+ extra_stats[stat_name + "_pct"] = agent_stats[stat_name] / self._grid_env.current_timestep
112
+
113
+
114
+ # for object in self._game_builder.object_configs.keys():
115
+ # if stat_name.startswith(f"stats_{object}_") and object != "agent":
116
+ # symbol = self._game_builder._objects[object].symbol
117
+ # num_obj = self._griddly_yaml["Environment"]["Levels"][0].count(symbol)
118
+ # if num_obj == 0:
119
+ # num_obj = 1
120
+ # extra_stats[stat_name + "_pct"] = agent_stats[stat_name] / num_obj
121
+
122
+ agent_stats.update(extra_stats)
123
+ agent_stats.update(episode_stats["game_stats"])
124
+ # agent_stats["level_max_energy"] = self._max_level_energy
125
+ # agent_stats["level_max_energy_per_agent"] = self._max_level_energy_per_agent
126
+ # agent_stats["level_max_reward_per_agent"] = self._max_level_reward_per_agent
127
+
128
+ def _compute_max_energy(self):
129
+ pass
130
+ # num_generators = self._griddly_yaml["Environment"]["Levels"][0].count("g")
131
+ # num_converters = self._griddly_yaml["Environment"]["Levels"][0].count("c")
132
+ # max_resources = num_generators * min(
133
+ # self._game_builder.object_configs.generator.initial_resources,
134
+ # self._max_steps / self._game_builder.object_configs.generator.cooldown)
135
+
136
+ # max_conversions = num_converters * (
137
+ # self._max_steps / self._game_builder.object_configs.converter.cooldown
138
+ # )
139
+ # max_conv_energy = min(max_resources, max_conversions) * \
140
+ # np.mean(list(self._game_builder.object_configs.converter.energy_output.values()))
141
+
142
+ # initial_energy = self._game_builder.object_configs.agent.initial_energy * self._game_builder.num_agents
143
+
144
+ # self._max_level_energy = max_conv_energy + initial_energy
145
+ # self._max_level_energy_per_agent = self._max_level_energy / self._game_builder.num_agents
146
+
147
+ # self._max_level_reward_per_agent = self._max_level_energy_per_agent
148
+
149
+
150
+ @property
151
+ def _max_steps(self):
152
+ return self._game_builder.max_steps
153
+
154
+ @property
155
+ def observation_space(self):
156
+ return self._env.observation_space
157
+
158
+ @property
159
+ def action_space(self):
160
+ return self._env.action_space
161
+
162
+ @property
163
+ def player_count(self):
164
+ return self._num_agents
165
+
166
+ def render(self, *args, **kwargs):
167
+ return self._renderer.render(
168
+ self._c_env.grid_objects(),
169
+ )
170
+
171
+ @property
172
+ def grid_features(self):
173
+ return self._env.grid_features()
174
+
175
+ @property
176
+ def global_features(self):
177
+ return []
178
+
179
+ @property
180
+ def render_mode(self):
181
+ return self._render_mode
@@ -0,0 +1,197 @@
1
+ # distutils: language=c++
2
+ # cython: warn.undeclared=False
3
+
4
+ cimport cython
5
+
6
+ from libcpp.vector cimport vector
7
+ from libcpp.map cimport map
8
+ from libcpp.string cimport string
9
+ from puffergrid.grid_env import StatsTracker
10
+ from libc.stdio cimport printf
11
+ from puffergrid.observation_encoder cimport ObservationEncoder, ObsType
12
+ from puffergrid.grid_object cimport GridObject, TypeId, GridCoord, GridLocation, GridObjectId
13
+ from puffergrid.event cimport EventHandler, EventArg
14
+
15
+ cdef enum GridLayer:
16
+ Agent_Layer = 0
17
+ Object_Layer = 1
18
+
19
+ ctypedef map[string, int] ObjectConfig
20
+
21
+ cdef cppclass MettaObject(GridObject):
22
+ unsigned int hp
23
+
24
+ inline void init_mo(ObjectConfig cfg):
25
+ this.hp = cfg[b"hp"]
26
+
27
+ inline char usable(const Agent *actor):
28
+ return False
29
+
30
+ inline char attackable():
31
+ return False
32
+
33
+ cdef cppclass Usable(MettaObject):
34
+ unsigned int use_cost
35
+ unsigned int cooldown
36
+ unsigned char ready
37
+
38
+ inline void init_usable(ObjectConfig cfg):
39
+ this.use_cost = cfg[b"use_cost"]
40
+ this.cooldown = cfg[b"cooldown"]
41
+ this.ready = 1
42
+
43
+ inline char usable(const Agent *actor):
44
+ return this.ready and this.use_cost <= actor.energy
45
+
46
+ cdef enum ObjectType:
47
+ AgentT = 0
48
+ WallT = 1
49
+ GeneratorT = 2
50
+ ConverterT = 3
51
+ AltarT = 4
52
+ Count = 5
53
+
54
+ cdef vector[string] ObjectTypeNames # defined in objects.pyx
55
+
56
+ cdef enum InventoryItem:
57
+ r1 = 0,
58
+ r2 = 1,
59
+ r3 = 2,
60
+ InventoryCount = 3
61
+
62
+ cdef vector[string] InventoryItemNames # defined in objects.pyx
63
+
64
+
65
+ cdef cppclass Agent(MettaObject):
66
+ char frozen
67
+ unsigned int energy
68
+ unsigned int orientation
69
+ char shield
70
+ vector[unsigned short] inventory
71
+
72
+ inline Agent(GridCoord r, GridCoord c, ObjectConfig cfg):
73
+ GridObject.init(ObjectType.AgentT, GridLocation(r, c, GridLayer.Agent_Layer))
74
+ MettaObject.init_mo(cfg)
75
+ this.frozen = False
76
+ this.energy = cfg[b"initial_energy"]
77
+ this.orientation = 0
78
+ this.inventory.resize(InventoryItem.InventoryCount)
79
+
80
+ inline void update_inventory(InventoryItem item, short amount):
81
+ this.inventory[<InventoryItem>item] += amount
82
+
83
+ inline void obs(ObsType[:] obs):
84
+ obs[0] = 1
85
+ obs[1] = this.hp
86
+ obs[2] = this.frozen
87
+ obs[3] = this.energy
88
+ obs[4] = this.orientation
89
+ obs[5] = this.shield
90
+
91
+ cdef unsigned short idx = 6
92
+ cdef unsigned short i
93
+ for i in range(InventoryItem.InventoryCount):
94
+ obs[idx + i] = this.inventory[i]
95
+
96
+ @staticmethod
97
+ inline vector[string] feature_names():
98
+ return [
99
+ "agent", "agent:hp", "agent:frozen", "agent:energy", "agent:orientation",
100
+ "agent:shield"
101
+ ] + [
102
+ "agent:inv:" + n for n in InventoryItemNames]
103
+
104
+ cdef cppclass Wall(MettaObject):
105
+ inline Wall(GridCoord r, GridCoord c, ObjectConfig cfg):
106
+ GridObject.init(ObjectType.WallT, GridLocation(r, c, GridLayer.Object_Layer))
107
+ MettaObject.init_mo(cfg)
108
+
109
+ inline void obs(ObsType[:] obs):
110
+ obs[0] = 1
111
+ obs[1] = hp
112
+
113
+ @staticmethod
114
+ inline vector[string] feature_names():
115
+ return ["wall", "wall:hp"]
116
+
117
+ cdef cppclass Generator(Usable):
118
+ unsigned int r1
119
+
120
+ inline Generator(GridCoord r, GridCoord c, ObjectConfig cfg):
121
+ GridObject.init(ObjectType.GeneratorT, GridLocation(r, c, GridLayer.Object_Layer))
122
+ MettaObject.init_mo(cfg)
123
+ Usable.init_usable(cfg)
124
+ this.r1 = cfg[b"initial_resources"]
125
+
126
+ inline char usable(const Agent *actor):
127
+ return Usable.usable(actor) and this.r1 > 0
128
+
129
+ inline void obs(ObsType[:] obs):
130
+ obs[0] = 1
131
+ obs[1] = this.hp
132
+ obs[2] = this.r1
133
+ obs[3] = this.ready
134
+
135
+
136
+ @staticmethod
137
+ inline vector[string] feature_names():
138
+ return ["generator", "generator:hp", "generator:r1", "generator:ready"]
139
+
140
+ cdef cppclass Converter(Usable):
141
+ InventoryItem input_resource
142
+ InventoryItem output_resource
143
+ short output_energy
144
+
145
+ inline Converter(GridCoord r, GridCoord c, ObjectConfig cfg):
146
+ GridObject.init(ObjectType.ConverterT, GridLocation(r, c, GridLayer.Object_Layer))
147
+ MettaObject.init_mo(cfg)
148
+ Usable.init_usable(cfg)
149
+ this.input_resource = InventoryItem.r1
150
+ this.output_resource = InventoryItem.r2
151
+ this.output_energy = cfg[b"energy_output.r1"]
152
+
153
+ inline char usable(const Agent *actor):
154
+ return Usable.usable(actor) and actor.inventory[this.input_resource] > 0
155
+
156
+ inline obs(ObsType[:] obs):
157
+ obs[0] = 1
158
+ obs[1] = hp
159
+ obs[2] = input_resource
160
+ obs[3] = output_resource
161
+ obs[4] = output_energy
162
+ obs[5] = ready
163
+
164
+ @staticmethod
165
+ inline vector[string] feature_names():
166
+ return ["converter", "converter:hp", "converter:input_resource", "converter:output_resource", "converter:output_energy", "converter:ready"]
167
+
168
+ cdef cppclass Altar(Usable):
169
+ inline Altar(GridCoord r, GridCoord c, ObjectConfig cfg):
170
+ GridObject.init(ObjectType.AltarT, GridLocation(r, c, GridLayer.Object_Layer))
171
+ MettaObject.init_mo(cfg)
172
+ Usable.init_usable(cfg)
173
+
174
+ inline void obs(ObsType[:] obs):
175
+ obs[0] = 1
176
+ obs[1] = hp
177
+ obs[2] = ready
178
+
179
+ @staticmethod
180
+ inline vector[string] feature_names():
181
+ return ["altar", "altar:hp", "altar:ready"]
182
+
183
+ cdef map[TypeId, GridLayer] ObjectLayers
184
+
185
+ cdef class ResetHandler(EventHandler):
186
+ cdef inline void handle_event(self, GridObjectId obj_id, EventArg arg):
187
+ cdef Usable *usable = <Usable*>self.env._grid.object(obj_id)
188
+ usable.ready = True
189
+ self.env._stats.game_incr("resets." + ObjectTypeNames[usable._type_id])
190
+
191
+ cdef enum Events:
192
+ Reset = 0
193
+
194
+ cdef class MettaObservationEncoder(ObservationEncoder):
195
+ cdef vector[short] _offsets
196
+ cdef vector[string] _feature_names
197
+
@@ -0,0 +1,67 @@
1
+ # distutils: language=c++
2
+
3
+ from libc.stdio cimport printf
4
+ from libcpp.string cimport string
5
+ from libcpp.vector cimport vector
6
+ from puffergrid.grid_object cimport GridObject, GridObjectId
7
+
8
+ cdef vector[string] ObjectTypeNames = [
9
+ "agent",
10
+ "wall",
11
+ "generator",
12
+ "converter",
13
+ "altar"
14
+ ]
15
+
16
+ cdef vector[string] InventoryItemNames = [
17
+ "r1",
18
+ "r2",
19
+ "r3"
20
+ ]
21
+
22
+ ObjectLayers = {
23
+ ObjectType.AgentT: GridLayer.Agent_Layer,
24
+ ObjectType.WallT: GridLayer.Object_Layer,
25
+ ObjectType.GeneratorT: GridLayer.Object_Layer,
26
+ ObjectType.ConverterT: GridLayer.Object_Layer,
27
+ ObjectType.AltarT: GridLayer.Object_Layer,
28
+ }
29
+
30
+ cdef class MettaObservationEncoder(ObservationEncoder):
31
+ def __init__(self) -> None:
32
+ self._offsets.resize(ObjectType.Count)
33
+
34
+ features = []
35
+ self._offsets[ObjectType.AgentT] = 0
36
+ features.extend(Agent.feature_names())
37
+
38
+ self._offsets[ObjectType.WallT] = len(features)
39
+ features.extend(Wall.feature_names())
40
+
41
+ self._offsets[ObjectType.GeneratorT] = len(features)
42
+ features.extend(Generator.feature_names())
43
+
44
+ self._offsets[ObjectType.ConverterT] = len(features)
45
+ features.extend(Converter.feature_names())
46
+
47
+ self._offsets[ObjectType.AltarT] = len(features)
48
+ features.extend(Altar.feature_names())
49
+
50
+ self._feature_names = features
51
+
52
+ cdef encode(self, GridObject *obj, ObsType[:] obs):
53
+ if obj._type_id == ObjectType.AgentT:
54
+ (<Agent*>obj).obs(obs[self._offsets[ObjectType.AgentT]:])
55
+ elif obj._type_id == ObjectType.WallT:
56
+ (<Wall*>obj).obs(obs[self._offsets[ObjectType.WallT]:])
57
+ elif obj._type_id == ObjectType.GeneratorT:
58
+ (<Generator*>obj).obs(obs[self._offsets[ObjectType.GeneratorT]:])
59
+ elif obj._type_id == ObjectType.ConverterT:
60
+ (<Converter*>obj).obs(obs[self._offsets[ObjectType.ConverterT]:])
61
+ elif obj._type_id == ObjectType.AltarT:
62
+ (<Altar*>obj).obs(obs[self._offsets[ObjectType.AltarT]:])
63
+ else:
64
+ printf("Encoding object of unknown type: %d\n", obj._type_id)
65
+
66
+ cdef vector[string] feature_names(self):
67
+ return self._feature_names
@@ -0,0 +1,180 @@
1
+ from pdb import set_trace as T
2
+ import numpy as np
3
+ import os
4
+
5
+ import pettingzoo
6
+ import gymnasium
7
+
8
+ import pufferlib
9
+ from pufferlib.environments.ocean import render
10
+
11
+ class MettaRaylibClient:
12
+ def __init__(self, width, height, tile_size=32):
13
+ self.width = width
14
+ self.height = height
15
+ self.tile_size = tile_size
16
+
17
+
18
+ sprite_sheet_path = os.path.join(
19
+ *self.__module__.split('.')[:-1], './puffer_chars.png')
20
+ self.asset_map = {
21
+ 1: (0, 0, 128, 128),
22
+ 3: (128, 0, 128, 128),
23
+ 4: (256, 0, 128, 128),
24
+ # 5: (384, 0, 128, 128),
25
+ 5: (512, 0, 128, 128), #star
26
+ }
27
+
28
+ from raylib import rl, colors
29
+ rl.InitWindow(width*tile_size, height*tile_size,
30
+ "PufferLib Ray Grid".encode())
31
+ rl.SetTargetFPS(10)
32
+ self.puffer = rl.LoadTexture(sprite_sheet_path.encode())
33
+ self.rl = rl
34
+ self.colors = colors
35
+
36
+ import pyray as ray
37
+ camera = ray.Camera2D()
38
+ camera.target = ray.Vector2(0.0, 0.0)
39
+ camera.rotation = 0.0
40
+ camera.zoom = 1.0
41
+ self.camera = camera
42
+
43
+ from cffi import FFI
44
+ self.ffi = FFI()
45
+
46
+ def _cdata_to_numpy(self):
47
+ image = self.rl.LoadImageFromScreen()
48
+ width, height, channels = image.width, image.height, 4
49
+ cdata = self.ffi.buffer(image.data, width*height*channels)
50
+ return np.frombuffer(cdata, dtype=np.uint8
51
+ ).reshape((height, width, channels))[:, :, :3]
52
+
53
+ def render(self, grid):
54
+ rl = self.rl
55
+ colors = self.colors
56
+ ay, ax = None, None
57
+
58
+ ts = self.tile_size
59
+
60
+ pos = rl.GetMousePosition()
61
+ raw_mouse_x = pos.x
62
+ raw_mouse_y = pos.y
63
+ mouse_x = int(raw_mouse_x // ts)
64
+ mouse_y = int(raw_mouse_y // ts)
65
+ ay = int(np.clip((pos.y - ts*self.height//2) / 50, -3, 3)) + 3
66
+ ax = int(np.clip((pos.x - ts*self.width//2) / 50, -3, 3)) + 3
67
+
68
+ if rl.IsKeyDown(rl.KEY_ESCAPE):
69
+ exit(0)
70
+
71
+ action_id = 0
72
+ action_arg = 0
73
+
74
+ if rl.IsKeyDown(rl.KEY_E):
75
+ action_id = 0
76
+ action_arg = 0
77
+ elif rl.IsKeyDown(rl.KEY_Q):
78
+ action_id = 0
79
+ action_arg = 1
80
+
81
+ elif rl.IsKeyDown(rl.KEY_W):
82
+ action_id = 1
83
+ action_arg = 0
84
+ elif rl.IsKeyDown(rl.KEY_S):
85
+ action_id = 1
86
+ action_arg = 1
87
+ elif rl.IsKeyDown(rl.KEY_A):
88
+ action_id = 1
89
+ action_arg = 2
90
+ elif rl.IsKeyDown(rl.KEY_R):
91
+ action_id = 1
92
+ action_arg = 3
93
+
94
+ # if rl.IsKeyDown(rl.KEY_LEFT_SHIFT):
95
+ # target_heros = 2
96
+
97
+ action = (action_id, action_arg)
98
+
99
+ rl.BeginDrawing()
100
+ rl.BeginMode2D(self.camera)
101
+ rl.ClearBackground([6, 24, 24, 255])
102
+ for y in range(self.height):
103
+ for x in range(self.width):
104
+ tile = grid[y, x]
105
+ tx = x*ts
106
+ ty = y*ts
107
+ if tile == 0:
108
+ continue
109
+ elif tile == 2:
110
+ # Wall
111
+ rl.DrawRectangle(x*ts, y*ts, ts, ts, [0, 0, 0, 255])
112
+ continue
113
+ else:
114
+ # Player
115
+ source_rect = self.asset_map[tile]
116
+ dest_rect = (tx, ty, ts, ts)
117
+ rl.DrawTexturePro(self.puffer, source_rect, dest_rect,
118
+ (0, 0), 0, colors.WHITE)
119
+
120
+ # Draw circle at mouse x, y
121
+ rl.DrawCircle(ts*mouse_x + ts//2, ts*mouse_y + ts//8, ts//8, [255, 0, 0, 255])
122
+
123
+ rl.EndMode2D()
124
+
125
+ # Draw HUD
126
+ # player = entities[0]
127
+ # hud_y = self.height*ts - 2*ts
128
+ # draw_bars(rl, player, 2*ts, hud_y, 10*ts, 24, draw_text=True)
129
+
130
+ # off_color = [255, 255, 255, 255]
131
+ # on_color = [0, 255, 0, 255]
132
+
133
+ # q_color = on_color if skill_q else off_color
134
+ # w_color = on_color if skill_w else off_color
135
+ # e_color = on_color if skill_e else off_color
136
+
137
+ # q_cd = player.q_timer
138
+ # w_cd = player.w_timer
139
+ # e_cd = player.e_timer
140
+
141
+ # rl.DrawText(f'Q: {q_cd}'.encode(), 13*ts, hud_y - 20, 40, q_color)
142
+ # rl.DrawText(f'W: {w_cd}'.encode(), 17*ts, hud_y - 20, 40, w_color)
143
+ # rl.DrawText(f'E: {e_cd}'.encode(), 21*ts, hud_y - 20, 40, e_color)
144
+ # rl.DrawText(f'Stun: {player.stun_timer}'.encode(), 25*ts, hud_y - 20, 20, e_color)
145
+ # rl.DrawText(f'Move: {player.move_timer}'.encode(), 25*ts, hud_y, 20, e_color)
146
+
147
+ rl.EndDrawing()
148
+ return self._cdata_to_numpy(), action
149
+
150
+ def draw_bars(rl, entity, x, y, width, height=4, draw_text=False):
151
+ health_bar = entity.health / entity.max_health
152
+ mana_bar = entity.mana / entity.max_mana
153
+ if entity.max_health == 0:
154
+ health_bar = 2
155
+ if entity.max_mana == 0:
156
+ mana_bar = 2
157
+ rl.DrawRectangle(x, y, width, height, [255, 0, 0, 255])
158
+ rl.DrawRectangle(x, y, int(width*health_bar), height, [0, 255, 0, 255])
159
+
160
+ if entity.entity_type == 0:
161
+ rl.DrawRectangle(x, y - height - 2, width, height, [255, 0, 0, 255])
162
+ rl.DrawRectangle(x, y - height - 2, int(width*mana_bar), height, [0, 255, 255, 255])
163
+
164
+ if draw_text:
165
+ health = int(entity.health)
166
+ mana = int(entity.mana)
167
+ max_health = int(entity.max_health)
168
+ max_mana = int(entity.max_mana)
169
+ rl.DrawText(f'Health: {health}/{max_health}'.encode(),
170
+ x+8, y+2, 20, [255, 255, 255, 255])
171
+ rl.DrawText(f'Mana: {mana}/{max_mana}'.encode(),
172
+ x+8, y+2 - height - 2, 20, [255, 255, 255, 255])
173
+
174
+ #rl.DrawRectangle(x, y - 2*height - 4, int(width*mana_bar), height, [255, 255, 0, 255])
175
+ rl.DrawText(f'Experience: {entity.xp}'.encode(),
176
+ x+8, y - 2*height - 4, 20, [255, 255, 255, 255])
177
+
178
+ elif entity.entity_type == 0:
179
+ rl.DrawText(f'Level: {entity.level}'.encode(),
180
+ x+4, y -2*height - 12, 12, [255, 255, 255, 255])
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["poetry-core>=1.0.0"]
3
+ build-backend = "poetry.core.masonry.api"
4
+
5
+ [tool.poetry]
6
+ name = "mettagrid"
7
+ version = "0.0.1"
8
+ description = "A fast grid-based open-ended MARL environment"
9
+ authors = ["David Bloomin <daveey@gmail.com>"]
10
+ license = "MIT"
11
+ readme = "README.md"
12
+ homepage = "https://daveey.github.io"
13
+ repository = "https://github.com/Metta-AI/mettagrid"
14
+ keywords = ["puffergrid", "gridworld", "minigrid", "rl", "reinforcement-learning", "environment", "gym"]
15
+
16
+ [tool.poetry.dependencies]
17
+ python = ">=3.10,<4.0"
18
+ numpy = "^1.21.0"
19
+ cython = "^3.0.11"
20
+
21
+ [tool.poetry.dev-dependencies]
22
+ pytest = "^6.2.4"