diff --git a/minigrid/__init__.py b/minigrid/__init__.py index 37c5bc2c8..6c08209f3 100644 --- a/minigrid/__init__.py +++ b/minigrid/__init__.py @@ -1501,6 +1501,41 @@ def register_minigrid_envs(): entry_point='minigrid.envs:LEnv_18' ) + register( + id='MiniGrid-TRoom-16x16-v0', + entry_point='minigrid.envs:TRoomEnv_16' + ) + + register( + id='MiniGrid-TRoom-18x18-v0', + entry_point='minigrid.envs:TRoomEnv_18' + ) + + register( + id='MiniGrid-TRoom-20x20-v0', + entry_point='minigrid.envs:TRoomEnv_20' + ) + + register( + id='MiniGrid-DonutRoom-16x16-v0', + entry_point='minigrid.envs:DonutEnv_16' + ) + + register( + id='MiniGrid-DonutRoom-18x18-v0', + entry_point='minigrid.envs:DonutEnv_18' + ) + + register( + id='MiniGrid-DonutRoom-20x20-v0', + entry_point='minigrid.envs:DonutEnv_20' + ) + + register( + id='MiniGrid-OrthDonutRoom-16x16-v0', + entry_point='minigrid.envs:OrthogonalDonutEnv_16' + ) + __version__ = "1.2.6" register_minigrid_envs() diff --git a/minigrid/envs/DonutRoom.py b/minigrid/envs/DonutRoom.py new file mode 100644 index 000000000..c9c444200 --- /dev/null +++ b/minigrid/envs/DonutRoom.py @@ -0,0 +1,277 @@ +from minigrid.minigrid_env import MiniGridEnv +from minigrid.core.grid import Grid +from minigrid.core.world_object import Floor +from minigrid.core.mission import MissionSpace +from gymnasium import spaces +import numpy as np + + +class Donut_Env(MiniGridEnv): + """ + Donut-room environment in modern *minigrid* style (matching File 1 structure), + but with the *exact* _gen_grid (walls + objects) from the DonutLapRoom snippet you sent. + """ + + def __init__( + self, + size=16, + Lwidth=10, + Lheight=8, + agent_start_pos=(3, 3), + agent_start_dir=0, + tri_color="blue", + plus_color="red", + x_color="yellow", + order="TPXD", + **kwargs, + ): + self.agent_start_pos = agent_start_pos + self.agent_start_dir = agent_start_dir + + self.Lwidth = Lwidth + self.Lheight = Lheight + self.tri_color = tri_color + self.plus_color = plus_color + self.x_color = x_color + self.order = order + + # Keep this like the snippet (and File 1) + see_through_walls = True + + # Mirror the snippet's naming + self.start_pos = agent_start_pos + self.size = size + + mission_space = MissionSpace(mission_func=self._gen_mission) + max_steps = kwargs.pop("max_steps", 10 * size * size) + + super().__init__( + mission_space=mission_space, + grid_size=size, + max_steps=max_steps, + see_through_walls=see_through_walls, + **kwargs, + ) + + # Match File 1 + self.action_space = spaces.Discrete(4) + + @staticmethod + def _gen_mission(): + return "reach the goal" + + def _gen_grid(self, width, height, regenerate=True): + # Exact regenerate behavior from your snippet + if not regenerate: + if self.start_pos is not None: + self.agent_pos = self.start_pos + self.agent_dir = self.agent_start_dir + else: + self.place_agent() + return + + self.grid = Grid(width, height) + + # Outer walls (exact) + self.grid.horz_wall(0, 0) + self.grid.vert_wall(0, 0) + self.grid.horz_wall(0, height - 1) + self.grid.vert_wall(width - 1, 0) + + # Donut wall (horizontal bar) (exact) + for i in range(int(height / 2) - 4, int(height / 2) + 4): + self.grid.horz_wall(int(self.Lwidth / 2), i, length=8) + + # Place agent (exact) + if self.start_pos is not None: + self.agent_pos = self.start_pos + self.agent_dir = self.agent_start_dir + else: + self.place_agent() + + # Place the four ordered shapes (exact) + loc = [ + (width / 3 - 4, height / 3 - 4), + (2 * width / 3 - 1, height / 3 - 1), + (width / 3 - 3, 2 * height / 3 - 2), + (2 * width / 3 - 2, 2 * height / 3 - 2), + ] + + shapes = { + "T": {"name": "triangle", "color": self.tri_color}, + "P": {"name": "dash", "color": self.plus_color}, + "X": {"name": "x", "color": self.x_color}, + "D": {"name": "dash", "color": self.tri_color}, + } + + for idx, char in enumerate(self.order): + self.place_shape(shapes[char]["name"], loc[idx], shapes[char]["color"]) + + # Additional decorations (exact) + self.place_shape("plus", (width / 3 - 1, height / 3 - 5), self.x_color) + self.place_shape("plus", (width / 3, height / 3 - 5), self.x_color) + self.place_shape("plus", (width / 3 + 1, height / 3 - 5), self.x_color) + self.place_shape("plus", (width / 3 + 2, height / 3 - 5), self.x_color) + + self.place_shape("plus", (width / 3 - 3, height / 3 + 6), self.plus_color) + self.place_shape("plus", (width / 3 - 2, height / 3 + 6), self.plus_color) + self.place_shape("plus", (width / 3 - 1, height / 3 + 6), self.plus_color) + self.place_shape("plus", (width / 3, height / 3 + 6), self.plus_color) + + self.mission = self._gen_mission() + + def place_shape(self, shape, pos, color): + """ + Exact shape definitions from your snippet. + Place a 6x6 shape with lower left corner at (x,y) + """ + shapegrid = { + "plus": np.array( + [ + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 0, 0], + ] + ), + "triangle": np.array( + [ + [1, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + ] + ), + "x": np.array( + [ + [0, 0, 0, 0, 1, 1], + [1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + ] + ), + "dash": np.array( + [ + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1], + ] + ), + } + + shapecoords = np.transpose(np.nonzero(shapegrid[shape])) + np.array(pos, dtype="int32") + for coord in shapecoords: + self.put_obj(Floor(color), int(coord[0]), int(coord[1])) + + +class DonutEnv_16(Donut_Env): + def __init__(self, **kwargs): + super().__init__(size=16, agent_start_pos=None, **kwargs) + + +class DonutEnv_18(Donut_Env): + def __init__(self, **kwargs): + super().__init__(size=18, agent_start_pos=None, **kwargs) + + +class DonutEnv_20(Donut_Env): + def __init__(self, **kwargs): + super().__init__(size=20, agent_start_pos=None, **kwargs) + + +import numpy as np +from minigrid.core.world_object import Wall, Floor, COLOR_TO_IDX, COLORS +from minigrid.core.grid import Grid + +# Registering your specific "Maximally Different" palette +custom_colors = { + "cyan": np.array([0, 255, 255]), + "magenta": np.array([255, 0, 255]), + "white": np.array([255, 255, 255]), + "lime": np.array([50, 205, 50]), + "orange": np.array([255, 165, 0]), + "black": np.array([0, 0, 0]) +} + +for name, rgb in custom_colors.items(): + if name not in COLOR_TO_IDX: + COLOR_TO_IDX[name] = len(COLOR_TO_IDX) + COLORS[name] = rgb + +class CustomColorDonutEnv(Donut_Env): + def __init__(self, **kwargs): + # Mapping the new colors to the shape attributes + super().__init__( + tri_color="cyan", # Triangle + plus_color="white", # Dash/Plus + x_color="lime", # X + **kwargs + ) + + def _gen_grid(self, width, height, regenerate=True): + self.grid = Grid(width, height) + + # 1. WALLS (Black) + w_col = "black" + for i in range(width): + self.grid.set(i, 0, Wall(w_col)) + self.grid.set(i, height - 1, Wall(w_col)) + for i in range(height): + self.grid.set(0, i, Wall(w_col)) + self.grid.set(width - 1, i, Wall(w_col)) + + # Donut wall + for i in range(int(height / 2) - 4, int(height / 2) + 4): + for j in range(8): + self.grid.set(int(self.Lwidth / 2) + j, i, Wall(w_col)) + + # 2. AGENT (Place before floor to avoid the infinite loop) + if self.start_pos is not None: + self.agent_pos = self.start_pos + self.agent_dir = self.agent_start_dir + else: + self.place_agent() + + # 3. OBJECTS (Cyan, White, Lime) + loc = [ + (width / 3 - 4, height / 3 - 4), + (2 * width / 3 - 1, height / 3 - 1), + (width / 3 - 3, 2 * height / 3 - 2), + (2 * width / 3 - 2, 2 * height / 3 - 2), + ] + shapes = { + "T": {"name": "triangle", "color": self.tri_color}, + "P": {"name": "dash", "color": self.plus_color}, + "X": {"name": "x", "color": self.x_color}, + "D": {"name": "dash", "color": self.tri_color}, + } + for idx, char in enumerate(self.order): + self.place_shape(shapes[char]["name"], loc[idx], shapes[char]["color"]) + + # Extra decorations + for i in range(4): + self.place_shape("plus", (width / 3 - 1 + i, height / 3 - 5), self.x_color) + self.place_shape("plus", (width / 3 - 3 + i, height / 3 + 6), self.plus_color) + + # 4. FLOOR (Magenta) + # We fill the empty background with Magenta + for x in range(width): + for y in range(height): + if self.grid.get(x, y) is None: + self.grid.set(x, y, Floor("magenta")) + + self.mission = self._gen_mission() + +# Registration for your 16x16 version +class OrthogonalDonutEnv_16(CustomColorDonutEnv): + def __init__(self, **kwargs): + super().__init__(size=16, agent_start_pos=None, **kwargs) \ No newline at end of file diff --git a/minigrid/envs/Lroom.py b/minigrid/envs/Lroom.py index 197f09bb1..966aea135 100644 --- a/minigrid/envs/Lroom.py +++ b/minigrid/envs/Lroom.py @@ -27,19 +27,23 @@ def __init__( mission_space = MissionSpace(mission_func=self._gen_mission) + #This genius step uses max_steps if it is in kwargs, and defaults to the right if not + max_steps = kwargs.pop("max_steps", 10 * size * size) super().__init__( mission_space=mission_space, grid_size=size, - max_steps=10*size*size, - # Set this to True for maximum speed - see_through_walls=True + max_steps=max_steps, + see_through_walls=True, + **kwargs ) + self.action_space = spaces.Discrete(4) + @staticmethod def _gen_mission(): return "reach the goal" - def _gen_grid(self, width, height): + def _gen_grid(self, width, height, regenerate): # Create an empty grid self.grid = Grid(width, height) @@ -111,7 +115,8 @@ def __init__(self, **kwargs): class LEnv_20(L_Env): def __init__(self, **kwargs): - super().__init__(size=20,Lwidth=12,Lheight=10,**kwargs) + super().__init__(size=20,Lwidth=12,Lheight=10, agent_start_pos=None, + **kwargs) class LEnv_18(L_Env): def __init__(self, **kwargs): diff --git a/minigrid/envs/TRoom.py b/minigrid/envs/TRoom.py new file mode 100644 index 000000000..c05d9d39d --- /dev/null +++ b/minigrid/envs/TRoom.py @@ -0,0 +1,176 @@ +from minigrid.minigrid_env import MiniGridEnv +from minigrid.core.grid import Grid +from minigrid.core.world_object import Floor +from minigrid.core.mission import MissionSpace +from gymnasium import spaces +import numpy as np + + +class TRoom_Env(MiniGridEnv): + """ + T-room environment in the *minigrid* (modern) style, matching File 1's structure: + - MissionSpace + _gen_mission + - kwargs plumbing for max_steps + - action_space = Discrete(4) + - see_through_walls=True + - agent_start_pos/dir handling identical to File 1 + - same object placement logic as File 2 (order + colors + locations) + - same T-room wall logic as File 2 + """ + + def __init__( + self, + size=16, + agent_start_pos=(1, 1), + agent_start_dir=0, + tri_color="blue", + plus_color="red", + x_color="yellow", + order="TPXD", + **kwargs, + ): + self.agent_start_pos = agent_start_pos + self.agent_start_dir = agent_start_dir + + self.tri_color = tri_color + self.plus_color = plus_color + self.x_color = x_color + self.order = order + self.size = size + + mission_space = MissionSpace(mission_func=self._gen_mission) + + # Match File 1 behavior: pop max_steps if provided, otherwise default + max_steps = kwargs.pop("max_steps", 10 * size * size) + + super().__init__( + mission_space=mission_space, + grid_size=size, + max_steps=max_steps, + see_through_walls=True, + **kwargs, + ) + + # Match File 1: only 4 actions + self.action_space = spaces.Discrete(4) + + @staticmethod + def _gen_mission(): + return "reach the goal" + + def _gen_grid(self, width, height, regenerate): + # Create an empty grid + self.grid = Grid(width, height) + + # Surrounding walls (same as File 2 / File 1 style) + self.grid.horz_wall(0, 0) + self.grid.vert_wall(0, 0) + self.grid.horz_wall(0, height - 1) + self.grid.vert_wall(width - 1, 0) + + # --- Shapes: same locations + order logic as File 2 --- + loc = [ + (width / 3 - 4, height / 3 - 4), + (2 * width / 3 - 2, height / 3 - 4), + (width / 3 - 3, 2 * height / 3 - 2), + (2 * width / 3 - 2, 2 * height / 3 - 2), + ] + + shapes = { + "T": {"name": "triangle", "color": self.tri_color}, + "P": {"name": "plus", "color": self.plus_color}, + "X": {"name": "x", "color": self.x_color}, + "D": {"name": "dash", "color": self.tri_color}, + } + + for idx, char in enumerate(self.order): + self.place_shape(shapes[char]["name"], loc[idx], shapes[char]["color"]) + + self.mission = "get to the green goal square" + + # --- T-room walls: same logic as File 2 --- + TRoom_delimeter = 5 + if self.size == 18: + TRoom_delimeter = 6 + elif self.size == 20: + TRoom_delimeter = 7 + + for i in range(TRoom_delimeter): + self.grid.vert_wall(i, int(width / 2), length=int(width / 2)) + self.grid.vert_wall(int(height - 2) - i, int(width / 2), length=int(width / 2)) + + for j in range(int(TRoom_delimeter / 2) + 2): + self.grid.horz_wall(0, j, length=width - 1) + + # Place the agent (match File 1) + if self.agent_start_pos is not None: + self.agent_pos = self.agent_start_pos + self.agent_dir = self.agent_start_dir + else: + self.place_agent() + + def place_shape(self, shape, pos, color): + """ + Place a 6x6 shape with lower left corner at (x,y) + """ + shapegrid = { + "plus": np.array( + [ + [0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1], + [0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 0, 0], + ] + ), + "triangle": np.array( + [ + [1, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 0], + [1, 1, 1, 0, 0, 0], + [1, 1, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 1, 1], + ] + ), + "x": np.array( + [ + [1, 1, 0, 0, 1, 1], + [1, 1, 1, 1, 1, 1], + [0, 1, 1, 1, 1, 0], + [0, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 1, 1], + [1, 1, 0, 0, 1, 1], + ] + ), + "dash": np.array( + [ + [1, 1, 0, 0, 0, 0], + [1, 1, 1, 0, 0, 0], + [0, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 0], + [0, 0, 0, 1, 1, 1], + [0, 0, 0, 0, 1, 1], + ] + ), + } + + shapecoords = np.transpose(np.nonzero(shapegrid[shape])) + np.array(pos, dtype="int32") + for coord in shapecoords: + self.put_obj(Floor(color), int(coord[0]), int(coord[1])) + + +class TRoomEnv_16(TRoom_Env): + def __init__(self, **kwargs): + super().__init__(size=16, agent_start_pos=None, order="TPXD", **kwargs) + + +class TRoomEnv_18(TRoom_Env): + def __init__(self, **kwargs): + super().__init__(size=18, agent_start_pos=None, order="TPXD", **kwargs) + + +class TRoomEnv_20(TRoom_Env): + def __init__(self, **kwargs): + super().__init__(size=20, agent_start_pos=None, order="TPXD", **kwargs) diff --git a/minigrid/envs/__init__.py b/minigrid/envs/__init__.py index 2f018426e..5c28bd4e7 100644 --- a/minigrid/envs/__init__.py +++ b/minigrid/envs/__init__.py @@ -18,6 +18,8 @@ from minigrid.envs.lavagap import LavaGapEnv from minigrid.envs.lockedroom import LockedRoom, LockedRoomEnv from minigrid.envs.Lroom import LEnv_16, LEnv_18, LEnv_20 +from minigrid.envs.DonutRoom import DonutEnv_16, DonutEnv_18, DonutEnv_20, OrthogonalDonutEnv_16 +from minigrid.envs.TRoom import TRoomEnv_16, TRoomEnv_18, TRoomEnv_20 from minigrid.envs.memory import MemoryEnv from minigrid.envs.multiroom import MultiRoom, MultiRoomEnv from minigrid.envs.obstructedmaze import ( diff --git a/setup.py b/setup.py index f1b140614..b1855ba4f 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,7 @@ def get_tests_requirements(): long_description=long_description, long_description_content_type="text/markdown", keywords=["Memory, Environment, Agent, RL, Gymnasium"], - python_requires=">=3.7, <3.11", + python_requires=">=3.7", packages=[package for package in find_packages() if package.startswith("minigrid")], include_package_data=True, install_requires=get_requirements(),