Browse Source

Merge pull request #209 from rodrigodelazcano/old_gym_versions

Compatibility old gym versions, v25, v24, v23, and v22
Mark Towers 2 years ago
parent
commit
04e9a6722c

+ 1 - 1
README.md

@@ -1,6 +1,6 @@
 # Minimalistic Gridworld Environment (MiniGrid)
 # Minimalistic Gridworld Environment (MiniGrid)
 
 
-[![Build Status](https://travis-ci.org/maximecb/gym-minigrid.svg?branch=master)](https://travis-ci.org/maximecb/gym-minigrid)
+[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://pre-commit.com/) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
 
 
 There are other gridworld Gym environments out there, but this one is
 There are other gridworld Gym environments out there, but this one is
 designed to be particularly simple, lightweight and fast. The code has very few
 designed to be particularly simple, lightweight and fast. The code has very few

+ 1 - 1
gym_minigrid/benchmark.py

@@ -18,7 +18,7 @@ parser.add_argument("--num_resets", default=200)
 parser.add_argument("--num_frames", default=5000)
 parser.add_argument("--num_frames", default=5000)
 args = parser.parse_args()
 args = parser.parse_args()
 
 
-env = gym.make(args.env_name, render_mode="rgb_array")
+env = gym.make(args.env_name)
 
 
 # Benchmark env.reset
 # Benchmark env.reset
 t0 = time.time()
 t0 = time.time()

+ 6 - 1
gym_minigrid/envs/blockedunlockpickup.py

@@ -1,4 +1,4 @@
-from gym_minigrid.minigrid import Ball
+from gym_minigrid.minigrid import COLOR_NAMES, Ball, MissionSpace
 from gym_minigrid.roomgrid import RoomGrid
 from gym_minigrid.roomgrid import RoomGrid
 
 
 
 
@@ -10,7 +10,12 @@ class BlockedUnlockPickupEnv(RoomGrid):
 
 
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
         room_size = 6
         room_size = 6
+        mission_space = MissionSpace(
+            mission_func=lambda color, type: f"pick up the {color} {type}",
+            ordered_placeholders=[COLOR_NAMES, ["box", "key"]],
+        )
         super().__init__(
         super().__init__(
+            mission_space=mission_space,
             num_rows=1,
             num_rows=1,
             num_cols=2,
             num_cols=2,
             room_size=room_size,
             room_size=room_size,

+ 12 - 1
gym_minigrid/envs/crossing.py

@@ -2,7 +2,7 @@ import itertools as itt
 
 
 import numpy as np
 import numpy as np
 
 
-from gym_minigrid.minigrid import Goal, Grid, Lava, MiniGridEnv
+from gym_minigrid.minigrid import Goal, Grid, Lava, MiniGridEnv, MissionSpace
 
 
 
 
 class CrossingEnv(MiniGridEnv):
 class CrossingEnv(MiniGridEnv):
@@ -13,7 +13,18 @@ class CrossingEnv(MiniGridEnv):
     def __init__(self, size=9, num_crossings=1, obstacle_type=Lava, **kwargs):
     def __init__(self, size=9, num_crossings=1, obstacle_type=Lava, **kwargs):
         self.num_crossings = num_crossings
         self.num_crossings = num_crossings
         self.obstacle_type = obstacle_type
         self.obstacle_type = obstacle_type
+
+        if obstacle_type == Lava:
+            mission_space = MissionSpace(
+                mission_func=lambda: "avoid the lava and get to the green goal square"
+            )
+        else:
+            mission_space = MissionSpace(
+                mission_func=lambda: "find the opening and get to the green goal square"
+            )
+
         super().__init__(
         super().__init__(
+            mission_space=mission_space,
             grid_size=size,
             grid_size=size,
             max_steps=4 * size * size,
             max_steps=4 * size * size,
             # Set this to True for maximum speed
             # Set this to True for maximum speed

+ 6 - 1
gym_minigrid/envs/distshift.py

@@ -1,4 +1,4 @@
-from gym_minigrid.minigrid import Goal, Grid, Lava, MiniGridEnv
+from gym_minigrid.minigrid import Goal, Grid, Lava, MiniGridEnv, MissionSpace
 
 
 
 
 class DistShiftEnv(MiniGridEnv):
 class DistShiftEnv(MiniGridEnv):
@@ -20,7 +20,12 @@ class DistShiftEnv(MiniGridEnv):
         self.goal_pos = (width - 2, 1)
         self.goal_pos = (width - 2, 1)
         self.strip2_row = strip2_row
         self.strip2_row = strip2_row
 
 
+        mission_space = MissionSpace(
+            mission_func=lambda: "get to the green goal square"
+        )
+
         super().__init__(
         super().__init__(
+            mission_space=mission_space,
             width=width,
             width=width,
             height=height,
             height=height,
             max_steps=4 * width * height,
             max_steps=4 * width * height,

+ 5 - 2
gym_minigrid/envs/doorkey.py

@@ -1,4 +1,4 @@
-from gym_minigrid.minigrid import Door, Goal, Grid, Key, MiniGridEnv
+from gym_minigrid.minigrid import Door, Goal, Grid, Key, MiniGridEnv, MissionSpace
 
 
 
 
 class DoorKeyEnv(MiniGridEnv):
 class DoorKeyEnv(MiniGridEnv):
@@ -9,7 +9,10 @@ class DoorKeyEnv(MiniGridEnv):
     def __init__(self, size=8, **kwargs):
     def __init__(self, size=8, **kwargs):
         if "max_steps" not in kwargs:
         if "max_steps" not in kwargs:
             kwargs["max_steps"] = 10 * size * size
             kwargs["max_steps"] = 10 * size * size
-        super().__init__(grid_size=size, **kwargs)
+        mission_space = MissionSpace(
+            mission_func=lambda: "use the key to open the door and then get to the goal"
+        )
+        super().__init__(mission_space=mission_space, grid_size=size, **kwargs)
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):
         # Create an empty grid
         # Create an empty grid

+ 7 - 1
gym_minigrid/envs/dynamicobstacles.py

@@ -2,7 +2,7 @@ from operator import add
 
 
 import gym
 import gym
 
 
-from gym_minigrid.minigrid import Ball, Goal, Grid, MiniGridEnv
+from gym_minigrid.minigrid import Ball, Goal, Grid, MiniGridEnv, MissionSpace
 
 
 
 
 class DynamicObstaclesEnv(MiniGridEnv):
 class DynamicObstaclesEnv(MiniGridEnv):
@@ -21,7 +21,13 @@ class DynamicObstaclesEnv(MiniGridEnv):
             self.n_obstacles = int(n_obstacles)
             self.n_obstacles = int(n_obstacles)
         else:
         else:
             self.n_obstacles = int(size / 2)
             self.n_obstacles = int(size / 2)
+
+        mission_space = MissionSpace(
+            mission_func=lambda: "get to the green goal square"
+        )
+
         super().__init__(
         super().__init__(
+            mission_space=mission_space,
             grid_size=size,
             grid_size=size,
             max_steps=4 * size * size,
             max_steps=4 * size * size,
             # Set this to True for maximum speed
             # Set this to True for maximum speed

+ 6 - 1
gym_minigrid/envs/empty.py

@@ -1,4 +1,4 @@
-from gym_minigrid.minigrid import Goal, Grid, MiniGridEnv
+from gym_minigrid.minigrid import Goal, Grid, MiniGridEnv, MissionSpace
 
 
 
 
 class EmptyEnv(MiniGridEnv):
 class EmptyEnv(MiniGridEnv):
@@ -10,7 +10,12 @@ class EmptyEnv(MiniGridEnv):
         self.agent_start_pos = agent_start_pos
         self.agent_start_pos = agent_start_pos
         self.agent_start_dir = agent_start_dir
         self.agent_start_dir = agent_start_dir
 
 
+        mission_space = MissionSpace(
+            mission_func=lambda: "get to the green goal square"
+        )
+
         super().__init__(
         super().__init__(
+            mission_space=mission_space,
             grid_size=size,
             grid_size=size,
             max_steps=4 * size * size,
             max_steps=4 * size * size,
             # Set this to True for maximum speed
             # Set this to True for maximum speed

+ 26 - 6
gym_minigrid/envs/fetch.py

@@ -1,4 +1,11 @@
-from gym_minigrid.minigrid import COLOR_NAMES, Ball, Grid, Key, MiniGridEnv
+from gym_minigrid.minigrid import (
+    COLOR_NAMES,
+    Ball,
+    Grid,
+    Key,
+    MiniGridEnv,
+    MissionSpace,
+)
 
 
 
 
 class FetchEnv(MiniGridEnv):
 class FetchEnv(MiniGridEnv):
@@ -9,9 +16,24 @@ class FetchEnv(MiniGridEnv):
 
 
     def __init__(self, size=8, numObjs=3, **kwargs):
     def __init__(self, size=8, numObjs=3, **kwargs):
         self.numObjs = numObjs
         self.numObjs = numObjs
-
+        self.obj_types = ["key", "ball"]
+
+        MISSION_SYNTAX = [
+            "get a",
+            "go get a",
+            "fetch a",
+            "go fetch a",
+            "you must fetch a",
+        ]
+        self.size = size
+        mission_space = MissionSpace(
+            mission_func=lambda syntax, color, type: f"{syntax} {color} {type}",
+            ordered_placeholders=[MISSION_SYNTAX, COLOR_NAMES, self.obj_types],
+        )
         super().__init__(
         super().__init__(
-            grid_size=size,
+            mission_space=mission_space,
+            width=size,
+            height=size,
             max_steps=5 * size**2,
             max_steps=5 * size**2,
             # Set this to True for maximum speed
             # Set this to True for maximum speed
             see_through_walls=True,
             see_through_walls=True,
@@ -27,13 +49,11 @@ class FetchEnv(MiniGridEnv):
         self.grid.vert_wall(0, 0)
         self.grid.vert_wall(0, 0)
         self.grid.vert_wall(width - 1, 0)
         self.grid.vert_wall(width - 1, 0)
 
 
-        types = ["key", "ball"]
-
         objs = []
         objs = []
 
 
         # For each object to be generated
         # For each object to be generated
         while len(objs) < self.numObjs:
         while len(objs) < self.numObjs:
-            objType = self._rand_elem(types)
+            objType = self._rand_elem(self.obj_types)
             objColor = self._rand_elem(COLOR_NAMES)
             objColor = self._rand_elem(COLOR_NAMES)
 
 
             if objType == "key":
             if objType == "key":

+ 12 - 4
gym_minigrid/envs/fourrooms.py

@@ -1,4 +1,4 @@
-from gym_minigrid.minigrid import Goal, Grid, MiniGridEnv
+from gym_minigrid.minigrid import Goal, Grid, MiniGridEnv, MissionSpace
 
 
 
 
 class FourRoomsEnv(MiniGridEnv):
 class FourRoomsEnv(MiniGridEnv):
@@ -10,7 +10,17 @@ class FourRoomsEnv(MiniGridEnv):
     def __init__(self, agent_pos=None, goal_pos=None, **kwargs):
     def __init__(self, agent_pos=None, goal_pos=None, **kwargs):
         self._agent_default_pos = agent_pos
         self._agent_default_pos = agent_pos
         self._goal_default_pos = goal_pos
         self._goal_default_pos = goal_pos
-        super().__init__(grid_size=19, max_steps=100, **kwargs)
+
+        self.size = 19
+        mission_space = MissionSpace(mission_func=lambda: "reach the goal")
+
+        super().__init__(
+            mission_space=mission_space,
+            width=self.size,
+            height=self.size,
+            max_steps=100,
+            **kwargs
+        )
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):
         # Create the grid
         # Create the grid
@@ -62,8 +72,6 @@ class FourRoomsEnv(MiniGridEnv):
         else:
         else:
             self.place_obj(Goal())
             self.place_obj(Goal())
 
 
-        self.mission = "reach the goal"
-
     def step(self, action):
     def step(self, action):
         obs, reward, done, info = MiniGridEnv.step(self, action)
         obs, reward, done, info = MiniGridEnv.step(self, action)
         return obs, reward, done, info
         return obs, reward, done, info

+ 10 - 4
gym_minigrid/envs/gotodoor.py

@@ -1,4 +1,4 @@
-from gym_minigrid.minigrid import COLOR_NAMES, Door, Grid, MiniGridEnv
+from gym_minigrid.minigrid import COLOR_NAMES, Door, Grid, MiniGridEnv, MissionSpace
 
 
 
 
 class GoToDoorEnv(MiniGridEnv):
 class GoToDoorEnv(MiniGridEnv):
@@ -9,13 +9,19 @@ class GoToDoorEnv(MiniGridEnv):
 
 
     def __init__(self, size=5, **kwargs):
     def __init__(self, size=5, **kwargs):
         assert size >= 5
         assert size >= 5
-
+        self.size = size
+        mission_space = MissionSpace(
+            mission_func=lambda color: f"go to the {color} door",
+            ordered_placeholders=[COLOR_NAMES],
+        )
         super().__init__(
         super().__init__(
-            grid_size=size,
+            mission_space=mission_space,
+            width=size,
+            height=size,
             max_steps=5 * size**2,
             max_steps=5 * size**2,
             # Set this to True for maximum speed
             # Set this to True for maximum speed
             see_through_walls=True,
             see_through_walls=True,
-            **kwargs
+            **kwargs,
         )
         )
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):

+ 19 - 2
gym_minigrid/envs/gotoobject.py

@@ -1,4 +1,12 @@
-from gym_minigrid.minigrid import COLOR_NAMES, Ball, Box, Grid, Key, MiniGridEnv
+from gym_minigrid.minigrid import (
+    COLOR_NAMES,
+    Ball,
+    Box,
+    Grid,
+    Key,
+    MiniGridEnv,
+    MissionSpace,
+)
 
 
 
 
 class GoToObjectEnv(MiniGridEnv):
 class GoToObjectEnv(MiniGridEnv):
@@ -9,9 +17,18 @@ class GoToObjectEnv(MiniGridEnv):
 
 
     def __init__(self, size=6, numObjs=2, **kwargs):
     def __init__(self, size=6, numObjs=2, **kwargs):
         self.numObjs = numObjs
         self.numObjs = numObjs
+        self.size = size
+        # Types of objects to be generated
+        self.obj_types = ["key", "ball", "box"]
 
 
+        mission_space = MissionSpace(
+            mission_func=lambda color, type: f"go to the {color} {type}",
+            ordered_placeholders=[COLOR_NAMES, self.obj_types],
+        )
         super().__init__(
         super().__init__(
-            grid_size=size,
+            mission_space=mission_space,
+            width=size,
+            height=size,
             max_steps=5 * size**2,
             max_steps=5 * size**2,
             # Set this to True for maximum speed
             # Set this to True for maximum speed
             see_through_walls=True,
             see_through_walls=True,

+ 6 - 1
gym_minigrid/envs/keycorridor.py

@@ -1,3 +1,4 @@
+from gym_minigrid.minigrid import COLOR_NAMES, MissionSpace
 from gym_minigrid.roomgrid import RoomGrid
 from gym_minigrid.roomgrid import RoomGrid
 
 
 
 
@@ -9,8 +10,12 @@ class KeyCorridorEnv(RoomGrid):
 
 
     def __init__(self, num_rows=3, obj_type="ball", room_size=6, **kwargs):
     def __init__(self, num_rows=3, obj_type="ball", room_size=6, **kwargs):
         self.obj_type = obj_type
         self.obj_type = obj_type
-
+        mission_space = MissionSpace(
+            mission_func=lambda color: f"pick up the {color} {obj_type}",
+            ordered_placeholders=[COLOR_NAMES],
+        )
         super().__init__(
         super().__init__(
+            mission_space=mission_space,
             room_size=room_size,
             room_size=room_size,
             num_rows=num_rows,
             num_rows=num_rows,
             max_steps=30 * room_size**2,
             max_steps=30 * room_size**2,

+ 15 - 3
gym_minigrid/envs/lavagap.py

@@ -1,6 +1,6 @@
 import numpy as np
 import numpy as np
 
 
-from gym_minigrid.minigrid import Goal, Grid, Lava, MiniGridEnv
+from gym_minigrid.minigrid import Goal, Grid, Lava, MiniGridEnv, MissionSpace
 
 
 
 
 class LavaGapEnv(MiniGridEnv):
 class LavaGapEnv(MiniGridEnv):
@@ -11,12 +11,24 @@ class LavaGapEnv(MiniGridEnv):
 
 
     def __init__(self, size, obstacle_type=Lava, **kwargs):
     def __init__(self, size, obstacle_type=Lava, **kwargs):
         self.obstacle_type = obstacle_type
         self.obstacle_type = obstacle_type
+        self.size = size
+
+        if obstacle_type == Lava:
+            mission_space = MissionSpace(
+                mission_func=lambda: "avoid the lava and get to the green goal square"
+            )
+        else:
+            mission_space = MissionSpace(
+                mission_func=lambda: "find the opening and get to the green goal square"
+            )
+
         super().__init__(
         super().__init__(
-            grid_size=size,
+            mission_space=mission_space,
+            width=size,
+            height=size,
             max_steps=4 * size * size,
             max_steps=4 * size * size,
             # Set this to True for maximum speed
             # Set this to True for maximum speed
             see_through_walls=False,
             see_through_walls=False,
-            **kwargs
         )
         )
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):

+ 22 - 2
gym_minigrid/envs/lockedroom.py

@@ -1,4 +1,13 @@
-from gym_minigrid.minigrid import COLOR_NAMES, Door, Goal, Grid, Key, MiniGridEnv, Wall
+from gym_minigrid.minigrid import (
+    COLOR_NAMES,
+    Door,
+    Goal,
+    Grid,
+    Key,
+    MiniGridEnv,
+    MissionSpace,
+    Wall,
+)
 
 
 
 
 class LockedRoom:
 class LockedRoom:
@@ -22,7 +31,18 @@ class LockedRoomEnv(MiniGridEnv):
     """
     """
 
 
     def __init__(self, size=19, **kwargs):
     def __init__(self, size=19, **kwargs):
-        super().__init__(grid_size=size, max_steps=10 * size, **kwargs)
+        self.size = size
+        mission_space = MissionSpace(
+            mission_func=lambda lockedroom_color, keyroom_color, door_color: f"get the {lockedroom_color} key from the {keyroom_color} room, unlock the {door_color} door and go to the goal",
+            ordered_placeholders=[COLOR_NAMES] * 3,
+        )
+        super().__init__(
+            mission_space=mission_space,
+            width=size,
+            height=size,
+            max_steps=10 * size,
+            **kwargs,
+        )
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):
         # Create the grid
         # Create the grid

+ 8 - 2
gym_minigrid/envs/memory.py

@@ -1,6 +1,6 @@
 import numpy as np
 import numpy as np
 
 
-from gym_minigrid.minigrid import Ball, Grid, Key, MiniGridEnv, Wall
+from gym_minigrid.minigrid import Ball, Grid, Key, MiniGridEnv, MissionSpace, Wall
 
 
 
 
 class MemoryEnv(MiniGridEnv):
 class MemoryEnv(MiniGridEnv):
@@ -14,9 +14,15 @@ class MemoryEnv(MiniGridEnv):
     """
     """
 
 
     def __init__(self, size=8, random_length=False, **kwargs):
     def __init__(self, size=8, random_length=False, **kwargs):
+        self.size = size
         self.random_length = random_length
         self.random_length = random_length
+        mission_space = MissionSpace(
+            mission_func=lambda: "go to the matching object at the end of the hallway"
+        )
         super().__init__(
         super().__init__(
-            grid_size=size,
+            mission_space=mission_space,
+            width=size,
+            height=size,
             max_steps=5 * size**2,
             max_steps=5 * size**2,
             # Set this to True for maximum speed
             # Set this to True for maximum speed
             see_through_walls=False,
             see_through_walls=False,

+ 21 - 2
gym_minigrid/envs/multiroom.py

@@ -1,4 +1,12 @@
-from gym_minigrid.minigrid import COLOR_NAMES, Door, Goal, Grid, MiniGridEnv, Wall
+from gym_minigrid.minigrid import (
+    COLOR_NAMES,
+    Door,
+    Goal,
+    Grid,
+    MiniGridEnv,
+    MissionSpace,
+    Wall,
+)
 
 
 
 
 class MultiRoom:
 class MultiRoom:
@@ -25,7 +33,18 @@ class MultiRoomEnv(MiniGridEnv):
 
 
         self.rooms = []
         self.rooms = []
 
 
-        super().__init__(grid_size=25, max_steps=self.maxNumRooms * 20, **kwargs)
+        mission_space = MissionSpace(
+            mission_func=lambda: "traverse the rooms to get to the goal"
+        )
+
+        self.size = 25
+
+        super().__init__(
+            mission_space=mission_space,
+            width=self.size,
+            height=self.size,
+            max_steps=self.maxNumRooms * 20,
+        )
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):
         roomList = []
         roomList = []

+ 8 - 4
gym_minigrid/envs/obstructedmaze.py

@@ -1,4 +1,4 @@
-from gym_minigrid.minigrid import COLOR_NAMES, DIR_TO_VEC, Ball, Box, Key
+from gym_minigrid.minigrid import COLOR_NAMES, DIR_TO_VEC, Ball, Box, Key, MissionSpace
 from gym_minigrid.roomgrid import RoomGrid
 from gym_minigrid.roomgrid import RoomGrid
 
 
 
 
@@ -12,12 +12,16 @@ class ObstructedMazeEnv(RoomGrid):
         room_size = 6
         room_size = 6
         max_steps = 4 * num_rooms_visited * room_size**2
         max_steps = 4 * num_rooms_visited * room_size**2
 
 
+        mission_space = MissionSpace(
+            mission_func=lambda: f"pick up the {COLOR_NAMES[0]} ball",
+        )
         super().__init__(
         super().__init__(
+            mission_space=mission_space,
             room_size=room_size,
             room_size=room_size,
             num_rows=num_rows,
             num_rows=num_rows,
             num_cols=num_cols,
             num_cols=num_cols,
             max_steps=max_steps,
             max_steps=max_steps,
-            **kwargs
+            **kwargs,
         )
         )
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):
@@ -121,7 +125,7 @@ class ObstructedMaze_Full(ObstructedMazeEnv):
         blocked=True,
         blocked=True,
         num_quarters=4,
         num_quarters=4,
         num_rooms_visited=25,
         num_rooms_visited=25,
-        **kwargs
+        **kwargs,
     ):
     ):
         self.agent_room = agent_room
         self.agent_room = agent_room
         self.key_in_box = key_in_box
         self.key_in_box = key_in_box
@@ -155,7 +159,7 @@ class ObstructedMaze_Full(ObstructedMazeEnv):
                     door_idx=(i + k) % 4,
                     door_idx=(i + k) % 4,
                     color=self.door_colors[(i + k) % len(self.door_colors)],
                     color=self.door_colors[(i + k) % len(self.door_colors)],
                     key_in_box=self.key_in_box,
                     key_in_box=self.key_in_box,
-                    blocked=self.blocked
+                    blocked=self.blocked,
                 )
                 )
 
 
         corners = [(2, 0), (2, 2), (0, 2), (0, 0)][: self.num_quarters]
         corners = [(2, 0), (2, 2), (0, 2), (0, 0)][: self.num_quarters]

+ 19 - 2
gym_minigrid/envs/playground.py

@@ -1,4 +1,13 @@
-from gym_minigrid.minigrid import COLOR_NAMES, Ball, Box, Door, Grid, Key, MiniGridEnv
+from gym_minigrid.minigrid import (
+    COLOR_NAMES,
+    Ball,
+    Box,
+    Door,
+    Grid,
+    Key,
+    MiniGridEnv,
+    MissionSpace,
+)
 
 
 
 
 class PlaygroundEnv(MiniGridEnv):
 class PlaygroundEnv(MiniGridEnv):
@@ -8,7 +17,15 @@ class PlaygroundEnv(MiniGridEnv):
     """
     """
 
 
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
-        super().__init__(grid_size=19, max_steps=100, **kwargs)
+        mission_space = MissionSpace(mission_func=lambda: "")
+        self.size = 19
+        super().__init__(
+            mission_space=mission_space,
+            width=self.size,
+            height=self.size,
+            max_steps=100,
+            **kwargs
+        )
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):
         # Create the grid
         # Create the grid

+ 23 - 4
gym_minigrid/envs/putnear.py

@@ -1,4 +1,12 @@
-from gym_minigrid.minigrid import COLOR_NAMES, Ball, Box, Grid, Key, MiniGridEnv
+from gym_minigrid.minigrid import (
+    COLOR_NAMES,
+    Ball,
+    Box,
+    Grid,
+    Key,
+    MiniGridEnv,
+    MissionSpace,
+)
 
 
 
 
 class PutNearEnv(MiniGridEnv):
 class PutNearEnv(MiniGridEnv):
@@ -8,14 +16,25 @@ class PutNearEnv(MiniGridEnv):
     """
     """
 
 
     def __init__(self, size=6, numObjs=2, **kwargs):
     def __init__(self, size=6, numObjs=2, **kwargs):
+        self.size = size
         self.numObjs = numObjs
         self.numObjs = numObjs
-
+        self.obj_types = ["key", "ball", "box"]
+        mission_space = MissionSpace(
+            mission_func=lambda move_color, move_type, target_color, target_type: f"put the {move_color} {move_type} near the {target_color} {target_type}",
+            ordered_placeholders=[
+                COLOR_NAMES,
+                self.obj_types,
+                COLOR_NAMES,
+                self.obj_types,
+            ],
+        )
         super().__init__(
         super().__init__(
-            grid_size=size,
+            mission_space=mission_space,
+            width=size,
+            height=size,
             max_steps=5 * size,
             max_steps=5 * size,
             # Set this to True for maximum speed
             # Set this to True for maximum speed
             see_through_walls=True,
             see_through_walls=True,
-            **kwargs
         )
         )
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):

+ 9 - 3
gym_minigrid/envs/redbluedoors.py

@@ -1,4 +1,4 @@
-from gym_minigrid.minigrid import Door, Grid, MiniGridEnv
+from gym_minigrid.minigrid import Door, Grid, MiniGridEnv, MissionSpace
 
 
 
 
 class RedBlueDoorEnv(MiniGridEnv):
 class RedBlueDoorEnv(MiniGridEnv):
@@ -10,9 +10,15 @@ class RedBlueDoorEnv(MiniGridEnv):
 
 
     def __init__(self, size=8, **kwargs):
     def __init__(self, size=8, **kwargs):
         self.size = size
         self.size = size
-
+        mission_space = MissionSpace(
+            mission_func=lambda: "open the red door then the blue door"
+        )
         super().__init__(
         super().__init__(
-            width=2 * size, height=size, max_steps=20 * size * size, **kwargs
+            mission_space=mission_space,
+            width=2 * size,
+            height=size,
+            max_steps=20 * size * size,
+            **kwargs
         )
         )
 
 
     def _gen_grid(self, width, height):
     def _gen_grid(self, width, height):

+ 3 - 0
gym_minigrid/envs/unlock.py

@@ -1,3 +1,4 @@
+from gym_minigrid.minigrid import MissionSpace
 from gym_minigrid.roomgrid import RoomGrid
 from gym_minigrid.roomgrid import RoomGrid
 
 
 
 
@@ -8,7 +9,9 @@ class UnlockEnv(RoomGrid):
 
 
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
         room_size = 6
         room_size = 6
+        mission_space = MissionSpace(mission_func=lambda: "open the door")
         super().__init__(
         super().__init__(
+            mission_space=mission_space,
             num_rows=1,
             num_rows=1,
             num_cols=2,
             num_cols=2,
             room_size=room_size,
             room_size=room_size,

+ 6 - 0
gym_minigrid/envs/unlockpickup.py

@@ -1,3 +1,4 @@
+from gym_minigrid.minigrid import COLOR_NAMES, MissionSpace
 from gym_minigrid.roomgrid import RoomGrid
 from gym_minigrid.roomgrid import RoomGrid
 
 
 
 
@@ -8,7 +9,12 @@ class UnlockPickupEnv(RoomGrid):
 
 
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
         room_size = 6
         room_size = 6
+        mission_space = MissionSpace(
+            mission_func=lambda color: f"pick up the {color} box",
+            ordered_placeholders=[COLOR_NAMES],
+        )
         super().__init__(
         super().__init__(
+            mission_space=mission_space,
             num_rows=1,
             num_rows=1,
             num_cols=2,
             num_cols=2,
             room_size=room_size,
             room_size=room_size,

+ 1 - 1
gym_minigrid/manual_control.py

@@ -93,7 +93,7 @@ parser.add_argument(
 
 
 args = parser.parse_args()
 args = parser.parse_args()
 
 
-env = gym.make(args.env, render_mode="rgb_array")
+env = gym.make(args.env)
 
 
 if args.agent_view:
 if args.agent_view:
     env = RGBImgPartialObsWrapper(env)
     env = RGBImgPartialObsWrapper(env)

+ 204 - 37
gym_minigrid/minigrid.py

@@ -1,14 +1,13 @@
 import hashlib
 import hashlib
 import math
 import math
-import string
 from abc import abstractmethod
 from abc import abstractmethod
 from enum import IntEnum
 from enum import IntEnum
-from functools import partial
+from typing import Any, Callable, Optional, Union
 
 
 import gym
 import gym
 import numpy as np
 import numpy as np
 from gym import spaces
 from gym import spaces
-from gym.utils.renderer import Renderer
+from gym.utils import seeding
 
 
 # Size in pixels of a tile in the full-scale human view
 # Size in pixels of a tile in the full-scale human view
 from gym_minigrid.rendering import (
 from gym_minigrid.rendering import (
@@ -79,6 +78,197 @@ DIR_TO_VEC = [
 ]
 ]
 
 
 
 
+def check_if_no_duplicate(duplicate_list: list) -> bool:
+    """Check if given list contains any duplicates"""
+    return len(set(duplicate_list)) == len(duplicate_list)
+
+
+class MissionSpace(spaces.Space[str]):
+    r"""A space representing a mission for the Gym-Minigrid environments.
+    The space allows generating random mission strings constructed with an input placeholder list.
+    Example Usage::
+        >>> observation_space = MissionSpace(mission_func=lambda color: f"Get the {color} ball.",
+                                                ordered_placeholders=[["green", "blue"]])
+        >>> observation_space.sample()
+            "Get the green ball."
+        >>> observation_space = MissionSpace(mission_func=lambda : "Get the ball.".,
+                                                ordered_placeholders=None)
+        >>> observation_space.sample()
+            "Get the ball."
+    """
+
+    def __init__(
+        self,
+        mission_func: Callable[..., str],
+        ordered_placeholders: Optional["list[list[str]]"] = None,
+        seed: Optional[Union[int, seeding.RandomNumberGenerator]] = None,
+    ):
+        r"""Constructor of :class:`MissionSpace` space.
+
+        Args:
+            mission_func (lambda _placeholders(str): _mission(str)): Function that generates a mission string from random placeholders.
+            ordered_placeholders (Optional["list[list[str]]"]): List of lists of placeholders ordered in placing order in the mission function mission_func.
+            seed: seed: The seed for sampling from the space.
+        """
+        # Check that the ordered placeholders and mission function are well defined.
+        if ordered_placeholders is not None:
+            assert (
+                len(ordered_placeholders) == mission_func.__code__.co_argcount
+            ), f"The number of placeholders {len(ordered_placeholders)} is different from the number of parameters in the mission function {mission_func.__code__.co_argcount}."
+            for placeholder_list in ordered_placeholders:
+                assert check_if_no_duplicate(
+                    placeholder_list
+                ), "Make sure that the placeholders don't have any duplicate values."
+        else:
+            assert (
+                mission_func.__code__.co_argcount == 0
+            ), f"If the ordered placeholders are {ordered_placeholders}, the mission function shouldn't have any parameters."
+
+        self.ordered_placeholders = ordered_placeholders
+        self.mission_func = mission_func
+
+        super().__init__(dtype=str, seed=seed)
+
+        # Check that mission_func returns a string
+        sampled_mission = self.sample()
+        assert isinstance(
+            sampled_mission, str
+        ), f"mission_func must return type str not {type(sampled_mission)}"
+
+    def sample(self) -> str:
+        """Sample a random mission string."""
+        if self.ordered_placeholders is not None:
+            placeholders = []
+            for rand_var_list in self.ordered_placeholders:
+                idx = self.np_random.integers(0, len(rand_var_list))
+
+                placeholders.append(rand_var_list[idx])
+
+            return self.mission_func(*placeholders)
+        else:
+            return self.mission_func()
+
+    def contains(self, x: Any) -> bool:
+        """Return boolean specifying if x is a valid member of this space."""
+        # Store a list of all the placeholders from self.ordered_placeholders that appear in x
+        if self.ordered_placeholders is not None:
+            check_placeholder_list = []
+            for placeholder_list in self.ordered_placeholders:
+                for placeholder in placeholder_list:
+                    if placeholder in x:
+                        check_placeholder_list.append(placeholder)
+
+            # Remove duplicates from the list
+            check_placeholder_list = list(set(check_placeholder_list))
+
+            start_id_placeholder = []
+            end_id_placeholder = []
+            # Get the starting and ending id of the identified placeholders with possible duplicates
+            new_check_placeholder_list = []
+            for placeholder in check_placeholder_list:
+                new_start_id_placeholder = [
+                    i for i in range(len(x)) if x.startswith(placeholder, i)
+                ]
+                new_check_placeholder_list += [placeholder] * len(
+                    new_start_id_placeholder
+                )
+                end_id_placeholder += [
+                    start_id + len(placeholder) - 1
+                    for start_id in new_start_id_placeholder
+                ]
+                start_id_placeholder += new_start_id_placeholder
+
+            # Order by starting id the placeholders
+            ordered_placeholder_list = sorted(
+                zip(
+                    start_id_placeholder, end_id_placeholder, new_check_placeholder_list
+                )
+            )
+
+            # Check for repeated placeholders contained in each other
+            remove_placeholder_id = []
+            for i, placeholder_1 in enumerate(ordered_placeholder_list):
+                starting_id = i + 1
+                for j, placeholder_2 in enumerate(
+                    ordered_placeholder_list[starting_id:]
+                ):
+                    # Check if place holder ids overlap and keep the longest
+                    if max(placeholder_1[0], placeholder_2[0]) < min(
+                        placeholder_1[1], placeholder_2[1]
+                    ):
+                        remove_placeholder = min(
+                            placeholder_1[2], placeholder_2[2], key=len
+                        )
+                        if remove_placeholder == placeholder_1[2]:
+                            remove_placeholder_id.append(i)
+                        else:
+                            remove_placeholder_id.append(i + j + 1)
+            for id in remove_placeholder_id:
+                del ordered_placeholder_list[id]
+
+            final_placeholders = [
+                placeholder[2] for placeholder in ordered_placeholder_list
+            ]
+
+            # Check that the identified final placeholders are in the same order as the original placeholders.
+            for orered_placeholder, final_placeholder in zip(
+                self.ordered_placeholders, final_placeholders
+            ):
+                if final_placeholder in orered_placeholder:
+                    continue
+                else:
+                    return False
+            try:
+                mission_string_with_placeholders = self.mission_func(
+                    *final_placeholders
+                )
+            except Exception as e:
+                print(
+                    f"{x} is not contained in MissionSpace due to the following exception: {e}"
+                )
+                return False
+
+            return bool(mission_string_with_placeholders == x)
+
+        else:
+            return bool(self.mission_func() == x)
+
+    def __repr__(self) -> str:
+        """Gives a string representation of this space."""
+        return f"MissionSpace({self.mission_func}, {self.ordered_placeholders})"
+
+    def __eq__(self, other) -> bool:
+        """Check whether ``other`` is equivalent to this instance."""
+        if isinstance(other, MissionSpace):
+
+            # Check that place holder lists are the same
+            if self.ordered_placeholders is not None:
+                # Check length
+                if (len(self.order_placeholder) == len(other.order_placeholder)) and (
+                    all(
+                        set(i) == set(j)
+                        for i, j in zip(self.order_placeholder, other.order_placeholder)
+                    )
+                ):
+                    # Check mission string is the same with dummy space placeholders
+                    test_placeholders = [""] * len(self.order_placeholder)
+                    mission = self.mission_func(*test_placeholders)
+                    other_mission = other.mission_func(*test_placeholders)
+                    return mission == other_mission
+            else:
+
+                # Check that other is also None
+                if other.ordered_placeholders is None:
+
+                    # Check mission string is the same
+                    mission = self.mission_func()
+                    other_mission = other.mission_func()
+                    return mission == other_mission
+
+        # If none of the statements above return then False
+        return False
+
+
 class WorldObj:
 class WorldObj:
     """
     """
     Base class for grid world objects
     Base class for grid world objects
@@ -261,9 +451,7 @@ class Door(WorldObj):
             state = 1
             state = 1
         else:
         else:
             raise ValueError(
             raise ValueError(
-                "There is no possible state encoding for the state:\n -Door Open: {}\n -Door Closed: {}\n -Door Locked: {}".format(
-                    self.is_open, not self.is_open, self.is_locked
-                )
+                f"There is no possible state encoding for the state:\n -Door Open: {self.is_open}\n -Door Closed: {not self.is_open}\n -Door Locked: {self.is_locked}"
             )
             )
 
 
         return (OBJECT_TO_IDX[self.type], COLOR_TO_IDX[self.color], state)
         return (OBJECT_TO_IDX[self.type], COLOR_TO_IDX[self.color], state)
@@ -663,17 +851,21 @@ class MiniGridEnv(gym.Env):
 
 
     def __init__(
     def __init__(
         self,
         self,
+        mission_space: MissionSpace,
         grid_size: int = None,
         grid_size: int = None,
         width: int = None,
         width: int = None,
         height: int = None,
         height: int = None,
         max_steps: int = 100,
         max_steps: int = 100,
         see_through_walls: bool = False,
         see_through_walls: bool = False,
         agent_view_size: int = 7,
         agent_view_size: int = 7,
-        render_mode: str = None,
         highlight: bool = True,
         highlight: bool = True,
         tile_size: int = TILE_PIXELS,
         tile_size: int = TILE_PIXELS,
-        **kwargs
+        **kwargs,
     ):
     ):
+
+        # Initialize mission
+        self.mission = mission_space.sample()
+
         # Can't set both grid_size and width/height
         # Can't set both grid_size and width/height
         if grid_size:
         if grid_size:
             assert width is None and height is None
             assert width is None and height is None
@@ -693,7 +885,7 @@ class MiniGridEnv(gym.Env):
 
 
         # Observations are dictionaries containing an
         # Observations are dictionaries containing an
         # encoding of the grid and a textual 'mission' string
         # encoding of the grid and a textual 'mission' string
-        self.observation_space = spaces.Box(
+        image_observation_space = spaces.Box(
             low=0,
             low=0,
             high=255,
             high=255,
             shape=(self.agent_view_size, self.agent_view_size, 3),
             shape=(self.agent_view_size, self.agent_view_size, 3),
@@ -701,24 +893,12 @@ class MiniGridEnv(gym.Env):
         )
         )
         self.observation_space = spaces.Dict(
         self.observation_space = spaces.Dict(
             {
             {
-                "image": self.observation_space,
+                "image": image_observation_space,
                 "direction": spaces.Discrete(4),
                 "direction": spaces.Discrete(4),
-                "mission": spaces.Text(
-                    max_length=200,
-                    charset=string.ascii_letters + string.digits + " .,!-",
-                ),
+                "mission": mission_space,
             }
             }
         )
         )
 
 
-        # render mode
-        self.render_mode = render_mode
-        render_frame = partial(
-            self._render,
-            highlight=highlight,
-            tile_size=tile_size,
-        )
-        self.renderer = Renderer(self.render_mode, render_frame)
-
         # Range of possible rewards
         # Range of possible rewards
         self.reward_range = (0, 1)
         self.reward_range = (0, 1)
 
 
@@ -763,8 +943,6 @@ class MiniGridEnv(gym.Env):
         # Return first observation
         # Return first observation
         obs = self.gen_obs()
         obs = self.gen_obs()
 
 
-        self.renderer.reset()
-        self.renderer.render_step()
         if not return_info:
         if not return_info:
             return obs
             return obs
         else:
         else:
@@ -1179,7 +1357,6 @@ class MiniGridEnv(gym.Env):
 
 
         obs = self.gen_obs()
         obs = self.gen_obs()
 
 
-        self.renderer.render_step()
         return obs, reward, done, {}
         return obs, reward, done, {}
 
 
     def gen_obs_grid(self, agent_view_size=None):
     def gen_obs_grid(self, agent_view_size=None):
@@ -1258,7 +1435,7 @@ class MiniGridEnv(gym.Env):
 
 
         return img
         return img
 
 
-    def _render(self, mode="human", highlight=True, tile_size=TILE_PIXELS):
+    def render(self, mode="human", highlight=True, tile_size=TILE_PIXELS):
         assert mode in self.metadata["render_modes"]
         assert mode in self.metadata["render_modes"]
         """
         """
         Render the whole-grid human view
         Render the whole-grid human view
@@ -1315,16 +1492,6 @@ class MiniGridEnv(gym.Env):
         else:
         else:
             return img
             return img
 
 
-    def render(self, mode="human", close=False, highlight=True, tile_size=TILE_PIXELS):
-        if close:
-            raise Exception(
-                "Please close the rendering window using env.close(). Closing the rendering window with the render method is no longer allowed."
-            )
-        if self.render_mode is not None:
-            return self.renderer.get_renders()
-        else:
-            return self._render(mode, highlight=highlight, tile_size=tile_size)
-
     def close(self):
     def close(self):
         if self.window:
         if self.window:
             self.window.close()
             self.window.close()

+ 1 - 3
gym_minigrid/wrappers.py

@@ -181,9 +181,7 @@ class RGBImgObsWrapper(ObservationWrapper):
     def observation(self, obs):
     def observation(self, obs):
         env = self.unwrapped
         env = self.unwrapped
 
 
-        rgb_img = env._render(
-            mode="rgb_array", highlight=True, tile_size=self.tile_size
-        )
+        rgb_img = env.render(mode="rgb_array", highlight=True, tile_size=self.tile_size)
 
 
         return {**obs, "image": rgb_img}
         return {**obs, "image": rgb_img}
 
 

+ 51 - 4
tests/test_envs.py

@@ -4,7 +4,7 @@ import pytest
 from gym.envs.registration import EnvSpec
 from gym.envs.registration import EnvSpec
 from gym.utils.env_checker import check_env
 from gym.utils.env_checker import check_env
 
 
-from gym_minigrid.minigrid import Grid
+from gym_minigrid.minigrid import Grid, MissionSpace
 from tests.utils import all_testing_env_specs, assert_equals
 from tests.utils import all_testing_env_specs, assert_equals
 
 
 CHECK_ENV_IGNORE_WARNINGS = [
 CHECK_ENV_IGNORE_WARNINGS = [
@@ -101,11 +101,11 @@ def test_render_modes(spec):
 
 
     for mode in env.metadata.get("render_modes", []):
     for mode in env.metadata.get("render_modes", []):
         if mode != "human":
         if mode != "human":
-            new_env = spec.make(render_mode=mode)
+            new_env = spec.make()
 
 
             new_env.reset()
             new_env.reset()
             new_env.step(new_env.action_space.sample())
             new_env.step(new_env.action_space.sample())
-            new_env.render()
+            new_env.render(mode=mode)
 
 
 
 
 @pytest.mark.parametrize("env_id", ["MiniGrid-DoorKey-6x6-v0"])
 @pytest.mark.parametrize("env_id", ["MiniGrid-DoorKey-6x6-v0"])
@@ -192,7 +192,7 @@ def old_run_test(env_spec):
 
 
 @pytest.mark.parametrize("env_id", ["MiniGrid-Empty-8x8-v0"])
 @pytest.mark.parametrize("env_id", ["MiniGrid-Empty-8x8-v0"])
 def test_interactive_mode(env_id):
 def test_interactive_mode(env_id):
-    env = gym.make(env_id, render_mode="human")
+    env = gym.make(env_id)
     env.reset()
     env.reset()
 
 
     for i in range(0, 100):
     for i in range(0, 100):
@@ -205,3 +205,50 @@ def test_interactive_mode(env_id):
 
 
     # Test the close method
     # Test the close method
     env.close()
     env.close()
+
+
+def test_mission_space():
+
+    # Test placeholders
+    mission_space = MissionSpace(
+        mission_func=lambda color, obj_type: f"Get the {color} {obj_type}.",
+        ordered_placeholders=[["green", "red"], ["ball", "key"]],
+    )
+
+    assert mission_space.contains("Get the green ball.")
+    assert mission_space.contains("Get the red key.")
+    assert not mission_space.contains("Get the purple box.")
+
+    # Test passing inverted placeholders
+    assert not mission_space.contains("Get the key red.")
+
+    # Test passing extra repeated placeholders
+    assert not mission_space.contains("Get the key red key.")
+
+    # Test contained placeholders like "get the" and "go get the". "get the" string is contained in both placeholders.
+    mission_space = MissionSpace(
+        mission_func=lambda get_syntax, obj_type: f"{get_syntax} {obj_type}.",
+        ordered_placeholders=[
+            ["go get the", "get the", "go fetch the", "fetch the"],
+            ["ball", "key"],
+        ],
+    )
+
+    assert mission_space.contains("get the ball.")
+    assert mission_space.contains("go get the key.")
+    assert mission_space.contains("go fetch the ball.")
+
+    # Test repeated placeholders
+    mission_space = MissionSpace(
+        mission_func=lambda get_syntax, color_1, obj_type_1, color_2, obj_type_2: f"{get_syntax} {color_1} {obj_type_1} and the {color_2} {obj_type_2}.",
+        ordered_placeholders=[
+            ["go get the", "get the", "go fetch the", "fetch the"],
+            ["green", "red"],
+            ["ball", "key"],
+            ["green", "red"],
+            ["ball", "key"],
+        ],
+    )
+
+    assert mission_space.contains("get the green key and the green key.")
+    assert mission_space.contains("go fetch the red ball and the green key.")