miniworld-maze 1.1.0__py3-none-any.whl → 1.3.0__py3-none-any.whl

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 miniworld-maze might be problematic. Click here for more details.

@@ -1,6 +1,7 @@
1
1
  """TwentyFiveRooms environment implementation."""
2
2
 
3
3
  from ..core import ObservationLevel
4
+ from ..core.constants import TextureThemes
4
5
  from .base_grid_rooms import GridRoomsEnvironment
5
6
 
6
7
 
@@ -78,46 +79,12 @@ class TwentyFiveRooms(GridRoomsEnvironment):
78
79
  (22, 23),
79
80
  (23, 24),
80
81
  ]
81
- default_textures = [
82
- "crimson",
83
- "beanpaste",
84
- "cobaltgreen",
85
- "lightnavyblue",
86
- "skyblue",
87
- "lightcobaltgreen",
88
- "oakbrown",
89
- "copperred",
90
- "lightgray",
91
- "lime",
92
- "turquoise",
93
- "violet",
94
- "beige",
95
- "morningglory",
96
- "silver",
97
- "magenta",
98
- "sunnyyellow",
99
- "blueberry",
100
- "lightbeige",
101
- "seablue",
102
- "lemongrass",
103
- "orchid",
104
- "redbean",
105
- "orange",
106
- "realblueberry",
107
- ]
82
+ default_textures = TextureThemes.TWENTY_FIVE_ROOMS
108
83
 
109
84
  # Initialize goal positions for each room (1 goal per room at center)
110
- goal_positions = []
111
- for i in range(5): # rows
112
- for j in range(5): # columns
113
- center_x = room_size * j + room_size / 2
114
- center_z = room_size * i + room_size / 2
115
- # One goal per room at the center
116
- goal_positions.append(
117
- [
118
- [center_x, 0.0, center_z],
119
- ]
120
- )
85
+ goal_positions = GridRoomsEnvironment._generate_goal_positions(
86
+ 5, room_size, goals_per_room=1
87
+ )
121
88
 
122
89
  super().__init__(
123
90
  grid_size=5,
@@ -1,5 +1,3 @@
1
1
  """Tools for Nine Rooms environments."""
2
2
 
3
- from .generate_observations import generate_observations, main
4
-
5
- __all__ = ["generate_observations", "main"]
3
+ __all__ = []
@@ -0,0 +1,286 @@
1
+ """Utility functions for miniworld_maze package."""
2
+
3
+ from typing import Tuple, Union
4
+ import numpy as np
5
+
6
+
7
+ def environment_to_pixel_coords(
8
+ env_pos: np.ndarray,
9
+ env_min: np.ndarray,
10
+ env_max: np.ndarray,
11
+ image_size: Union[int, Tuple[int, int]],
12
+ ) -> Tuple[int, int]:
13
+ """
14
+ Convert environment coordinates to pixel coordinates.
15
+
16
+ Args:
17
+ env_pos: Environment position as ndarray [x, z] or [x, y]
18
+ env_min: Environment minimum bounds as ndarray [min_x, min_z] or [min_x, min_y]
19
+ env_max: Environment maximum bounds as ndarray [max_x, max_z] or [max_x, max_y]
20
+ image_size: Image size (width, height) or single size for square image
21
+
22
+ Returns:
23
+ Tuple of (pixel_x, pixel_y) coordinates
24
+ """
25
+ env_x, env_y = env_pos[:2]
26
+ env_min_x, env_min_y = env_min[:2]
27
+ env_max_x, env_max_y = env_max[:2]
28
+
29
+ if isinstance(image_size, int):
30
+ width = height = image_size
31
+ else:
32
+ width, height = image_size
33
+
34
+ # Normalize to [0, 1] range and scale to pixel coordinates
35
+ pixel_x = int((env_x - env_min_x) / (env_max_x - env_min_x) * width)
36
+ pixel_y = int((env_y - env_min_y) / (env_max_y - env_min_y) * height)
37
+
38
+ return pixel_x, pixel_y
39
+
40
+
41
+ def pixel_to_environment_coords(
42
+ pixel_pos: np.ndarray,
43
+ env_min: np.ndarray,
44
+ env_max: np.ndarray,
45
+ image_size: Union[int, Tuple[int, int]],
46
+ ) -> Tuple[float, float]:
47
+ """
48
+ Convert pixel coordinates to environment coordinates.
49
+
50
+ Args:
51
+ pixel_pos: Pixel position as ndarray [x, y]
52
+ env_min: Environment minimum bounds as ndarray [min_x, min_z] or [min_x, min_y]
53
+ env_max: Environment maximum bounds as ndarray [max_x, max_z] or [max_x, max_y]
54
+ image_size: Image size (width, height) or single size for square image
55
+
56
+ Returns:
57
+ Tuple of (env_x, env_y) coordinates
58
+ """
59
+ pixel_x, pixel_y = pixel_pos[:2]
60
+ env_min_x, env_min_y = env_min[:2]
61
+ env_max_x, env_max_y = env_max[:2]
62
+
63
+ if isinstance(image_size, int):
64
+ width = height = image_size
65
+ else:
66
+ width, height = image_size
67
+
68
+ # Convert to normalized [0, 1] range and scale to environment coordinates
69
+ env_x = pixel_x / width * (env_max_x - env_min_x) + env_min_x
70
+ env_y = pixel_y / height * (env_max_y - env_min_y) + env_min_y
71
+
72
+ return env_x, env_y
73
+
74
+
75
+ def clamp_to_bounds(
76
+ value: Union[int, float, np.ndarray],
77
+ min_val: Union[int, float, np.ndarray],
78
+ max_val: Union[int, float, np.ndarray],
79
+ ) -> Union[int, float, np.ndarray]:
80
+ """
81
+ Clamp a value or array of values to specified bounds.
82
+
83
+ Args:
84
+ value: Value(s) to clamp (scalar or ndarray)
85
+ min_val: Minimum bound(s) (scalar or ndarray)
86
+ max_val: Maximum bound(s) (scalar or ndarray)
87
+
88
+ Returns:
89
+ Clamped value(s)
90
+ """
91
+ if isinstance(value, np.ndarray):
92
+ return np.clip(value, min_val, max_val)
93
+ else:
94
+ return max(min_val, min(max_val, value))
95
+
96
+
97
+ def clamp_pixel_coords(
98
+ pixel_x: int, pixel_y: int, image_size: Union[int, Tuple[int, int]]
99
+ ) -> Tuple[int, int]:
100
+ """
101
+ Clamp pixel coordinates to image bounds.
102
+
103
+ Args:
104
+ pixel_x: X pixel coordinate
105
+ pixel_y: Y pixel coordinate
106
+ image_size: Image size (width, height) or single size for square image
107
+
108
+ Returns:
109
+ Tuple of clamped (pixel_x, pixel_y) coordinates
110
+ """
111
+ if isinstance(image_size, int):
112
+ width = height = image_size
113
+ else:
114
+ width, height = image_size
115
+
116
+ clamped_x = max(0, min(width - 1, pixel_x))
117
+ clamped_y = max(0, min(height - 1, pixel_y))
118
+
119
+ return clamped_x, clamped_y
120
+
121
+
122
+ def normalize_coordinates(
123
+ coords: np.ndarray,
124
+ min_bounds: np.ndarray,
125
+ max_bounds: np.ndarray,
126
+ ) -> Tuple[float, float]:
127
+ """
128
+ Normalize coordinates to [0, 1] range based on given bounds.
129
+
130
+ Args:
131
+ coords: Coordinates to normalize as ndarray
132
+ min_bounds: Minimum bounds as ndarray
133
+ max_bounds: Maximum bounds as ndarray
134
+
135
+ Returns:
136
+ Normalized coordinates as (x, y) tuple
137
+ """
138
+ x, y = coords[:2]
139
+ min_x, min_y = min_bounds[:2]
140
+ max_x, max_y = max_bounds[:2]
141
+
142
+ norm_x = (x - min_x) / (max_x - min_x)
143
+ norm_y = (y - min_y) / (max_y - min_y)
144
+
145
+ return norm_x, norm_y
146
+
147
+
148
+ def denormalize_coordinates(
149
+ normalized_coords: np.ndarray,
150
+ min_bounds: np.ndarray,
151
+ max_bounds: np.ndarray,
152
+ ) -> Tuple[float, float]:
153
+ """
154
+ Convert normalized [0, 1] coordinates back to original coordinate space.
155
+
156
+ Args:
157
+ normalized_coords: Normalized coordinates in [0, 1] range as ndarray
158
+ min_bounds: Original minimum bounds as ndarray
159
+ max_bounds: Original maximum bounds as ndarray
160
+
161
+ Returns:
162
+ Denormalized coordinates as (x, y) tuple
163
+ """
164
+ norm_x, norm_y = normalized_coords[:2]
165
+ min_x, min_y = min_bounds[:2]
166
+ max_x, max_y = max_bounds[:2]
167
+
168
+ x = norm_x * (max_x - min_x) + min_x
169
+ y = norm_y * (max_y - min_y) + min_y
170
+
171
+ return x, y
172
+
173
+
174
+ def get_environment_bounds(env) -> Tuple[Tuple[float, float], Tuple[float, float]]:
175
+ """
176
+ Extract environment bounds from an environment object.
177
+
178
+ Args:
179
+ env: Environment object (may be wrapped)
180
+
181
+ Returns:
182
+ Tuple of ((min_x, min_z), (max_x, max_z))
183
+ """
184
+ # Unwrap environment to access base environment
185
+ base_env = env
186
+ while hasattr(base_env, "env"):
187
+ base_env = base_env.env
188
+
189
+ min_bounds = (base_env.min_x, base_env.min_z)
190
+ max_bounds = (base_env.max_x, base_env.max_z)
191
+
192
+ return min_bounds, max_bounds
193
+
194
+
195
+ def calculate_view_size_from_bounds(
196
+ min_bounds: np.ndarray,
197
+ max_bounds: np.ndarray,
198
+ ) -> Tuple[float, float]:
199
+ """
200
+ Calculate view size (width, height) from coordinate bounds.
201
+
202
+ Args:
203
+ min_bounds: Minimum bounds as ndarray (min_x, min_y)
204
+ max_bounds: Maximum bounds as ndarray (max_x, max_y)
205
+
206
+ Returns:
207
+ Tuple of (width, height)
208
+ """
209
+ min_x, min_y = min_bounds[:2]
210
+ max_x, max_y = max_bounds[:2]
211
+
212
+ width = max_x - min_x
213
+ height = max_y - min_y
214
+
215
+ return width, height
216
+
217
+
218
+ def scale_coordinates(
219
+ coords: np.ndarray,
220
+ scale_factor: Union[float, Tuple[float, float]],
221
+ ) -> Tuple[float, float]:
222
+ """
223
+ Scale coordinates by a given factor.
224
+
225
+ Args:
226
+ coords: Coordinates to scale as ndarray
227
+ scale_factor: Scale factor (uniform) or (scale_x, scale_y)
228
+
229
+ Returns:
230
+ Scaled coordinates as (x, y) tuple
231
+ """
232
+ x, y = coords[:2]
233
+
234
+ if isinstance(scale_factor, (tuple, list)):
235
+ scale_x, scale_y = scale_factor
236
+ else:
237
+ scale_x = scale_y = scale_factor
238
+
239
+ return x * scale_x, y * scale_y
240
+
241
+
242
+ def distance_2d(
243
+ pos1: np.ndarray,
244
+ pos2: np.ndarray,
245
+ ) -> float:
246
+ """
247
+ Calculate 2D Euclidean distance between two positions.
248
+
249
+ Args:
250
+ pos1: First position as ndarray (x, y)
251
+ pos2: Second position as ndarray (x, y)
252
+
253
+ Returns:
254
+ Euclidean distance
255
+ """
256
+ x1, y1 = pos1[:2]
257
+ x2, y2 = pos2[:2]
258
+
259
+ return np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
260
+
261
+
262
+ def lerp_2d(
263
+ pos1: np.ndarray,
264
+ pos2: np.ndarray,
265
+ t: float,
266
+ ) -> Tuple[float, float]:
267
+ """
268
+ Linear interpolation between two 2D positions.
269
+
270
+ Args:
271
+ pos1: Start position as ndarray (x, y)
272
+ pos2: End position as ndarray (x, y)
273
+ t: Interpolation parameter [0, 1]
274
+
275
+ Returns:
276
+ Interpolated position as (x, y) tuple
277
+ """
278
+ x1, y1 = pos1[:2]
279
+ x2, y2 = pos2[:2]
280
+
281
+ t = clamp_to_bounds(t, 0.0, 1.0)
282
+
283
+ x = x1 + t * (x2 - x1)
284
+ y = y1 + t * (y2 - y1)
285
+
286
+ return x, y
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: miniworld-maze
3
- Version: 1.1.0
4
- Summary: Multi-room maze environments from the DrStrategy paper for reinforcement learning research
3
+ Version: 1.3.0
4
+ Summary: Multi-room maze environments from the DrStrategy paper. Provides NineRooms-v0, SpiralNineRooms-v0, and TwentyFiveRooms-v0 gymnasium environments.
5
5
  Keywords: reinforcement-learning,environment,gymnasium,multi-room-maze,drstrategy,maze-navigation,partial-observability,3d-environments
6
6
  Author: Tim Joseph
7
7
  Author-email: Tim Joseph <tim@mctigger.com>
@@ -46,9 +46,6 @@ Requires-Dist: opencv-python>=4.5.0,<5.0.0
46
46
  Requires-Dist: pillow>=8.0.0,<11.0.0
47
47
  Requires-Dist: pyopengl>=3.1.0,<4.0.0
48
48
  Requires-Dist: pyglet>=1.5.0,<2.0.0
49
- Requires-Dist: black>=22.0,<25.0 ; extra == 'dev'
50
- Requires-Dist: isort>=5.10,<6.0 ; extra == 'dev'
51
- Requires-Dist: flake8>=4.0,<7.0 ; extra == 'dev'
52
49
  Requires-Dist: ruff>=0.1.0,<1.0.0 ; extra == 'dev'
53
50
  Requires-Dist: build>=0.8.0,<2.0.0 ; extra == 'dev'
54
51
  Requires-Dist: twine>=4.0.0,<6.0.0 ; extra == 'dev'
@@ -83,6 +80,18 @@ pip install miniworld-maze
83
80
 
84
81
  ## Usage
85
82
 
83
+ ### Registered Environments
84
+
85
+ This package registers the following gymnasium environments:
86
+
87
+ | Environment ID | Description | Rooms | Max Steps |
88
+ |---|---|---|---|
89
+ | `NineRooms-v0` | Standard 3×3 grid with adjacent room connections | 9 | 1000 |
90
+ | `SpiralNineRooms-v0` | 3×3 grid with spiral connection pattern | 9 | 1000 |
91
+ | `TwentyFiveRooms-v0` | Large 5×5 grid with complex navigation | 25 | 1000 |
92
+
93
+ All environments use `TOP_DOWN_PARTIAL` observation level and factory default room/door sizes by default.
94
+
86
95
  ### Basic Usage
87
96
 
88
97
  See `examples/basic_usage.py` for a complete working example:
@@ -95,12 +104,13 @@ Basic usage example for miniworld-maze environments.
95
104
  This is a minimal example showing how to create and interact with the environment.
96
105
  """
97
106
 
98
- from miniworld_maze import NineRoomsEnvironmentWrapper
107
+ import gymnasium as gym
108
+ import miniworld_maze # noqa: F401
99
109
 
100
110
 
101
111
  def main():
102
- # Create environment
103
- env = NineRoomsEnvironmentWrapper(variant="NineRooms", size=64)
112
+ # Create environment using gymnasium registry
113
+ env = gym.make("NineRooms-v0", obs_width=64, obs_height=64)
104
114
  obs, info = env.reset()
105
115
 
106
116
  # obs is a dictionary containing:
@@ -212,14 +222,15 @@ Each environment supports three different observation modes:
212
222
  All environments can be customized with the following parameters:
213
223
 
214
224
  ```python
215
- from miniworld_maze import NineRoomsEnvironmentWrapper
216
- from miniworld_maze.core import ObservationLevel
225
+ import gymnasium as gym
226
+ from miniworld_maze import ObservationLevel
227
+ import miniworld_maze # noqa: F401
217
228
 
218
- env = NineRoomsEnvironmentWrapper(
219
- variant="NineRooms", # "NineRooms", "SpiralNineRooms", "TwentyFiveRooms"
229
+ env = gym.make(
230
+ "NineRooms-v0", # Environment variant
220
231
  obs_level=ObservationLevel.TOP_DOWN_PARTIAL, # Observation type
221
- continuous=False, # Use continuous actions
222
- size=64, # Observation image size (64x64)
232
+ obs_width=64, # Observation image width
233
+ obs_height=64, # Observation image height
223
234
  room_size=5, # Size of each room in environment units
224
235
  door_size=2, # Size of doors between rooms
225
236
  agent_mode="empty", # Agent rendering: "empty", "circle", "triangle"
@@ -1,9 +1,9 @@
1
- miniworld_maze/__init__.py,sha256=kz5QqyvwIV_XkjXw_yHRkhqlPSl8X1Usra3DF0Zy4_E,1053
1
+ miniworld_maze/__init__.py,sha256=Dfq3558bJ5kgxDZxPxvlHu1531zfBhtiDfKbVL_4zCE,1430
2
2
  miniworld_maze/core/__init__.py,sha256=5BA4WKXQjrG55TNaEid2JGrnf1KQniJZ1HhRqovM1Q0,293
3
- miniworld_maze/core/constants.py,sha256=GX1pSnaWGOkangYStxaBy7gIQhWxVKIbXnTSd-eA_vs,3962
3
+ miniworld_maze/core/constants.py,sha256=UXqmuvsT9ww1GljW74lWPPP9XxVmvti-8JfM_RBSoWQ,4314
4
4
  miniworld_maze/core/miniworld_gymnasium/README.md,sha256=kkZgkRKBdgixpot3uHuiBFlRIKRFIVBVfXwu68XTEv0,74
5
- miniworld_maze/core/miniworld_gymnasium/__init__.py,sha256=zgqRxYBKT9Jo06kqWusZ6OjHyz25AyW03gkyrpZqY1I,151
6
- miniworld_maze/core/miniworld_gymnasium/base_env.py,sha256=heoJxEw_GGEHSzU75MWgqRkWARY3lgTgwNE9Zp1P_YA,1795
5
+ miniworld_maze/core/miniworld_gymnasium/__init__.py,sha256=ALOqr4SCVk9I9bmxyoU0Du8LUPE_57jIyZuV4QhOcBc,159
6
+ miniworld_maze/core/miniworld_gymnasium/base_env.py,sha256=K2L5r4gG2k4fEknFqRJ7f-b2S0Uj5QhanS-MLi6ncVc,1927
7
7
  miniworld_maze/core/miniworld_gymnasium/entities/__init__.py,sha256=gbMJcFeasvlRSIuXWyOYO-MhW0JKKY5ZNxF5TMsWJ5c,262
8
8
  miniworld_maze/core/miniworld_gymnasium/entities/agent.py,sha256=U3Zt2Hk21rJSdzPCBj46-N930n7XAkA9qo_fKbezn2k,3153
9
9
  miniworld_maze/core/miniworld_gymnasium/entities/base_entity.py,sha256=ox2k3rf64Y2mp4S0mcpNpPUg0QeUuJ7LVoduvBuLXDg,3657
@@ -260,19 +260,18 @@ miniworld_maze/core/miniworld_gymnasium/textures/white_1.png,sha256=wRrgs92I_Ids
260
260
  miniworld_maze/core/miniworld_gymnasium/textures/wood_1.png,sha256=XRZyIN34HFo14olbxRcsHGrzCAFqUlowc6nLR22IFBE,184713
261
261
  miniworld_maze/core/miniworld_gymnasium/textures/wood_2.png,sha256=qSDHB-ZO11JJLQuiQse-0edpbuTg1YO-eIBhdTvNUhc,93121
262
262
  miniworld_maze/core/miniworld_gymnasium/textures/wood_planks_1.png,sha256=E4SNN1s4yOtkLfZFQy905eip6KvDWnnPUrpS82FxMAg,847259
263
- miniworld_maze/core/miniworld_gymnasium/unified_env.py,sha256=oY2YXCZ-ro8kl6ie0h8TEBooWjB6bvmQu42-1tDsrw4,46689
263
+ miniworld_maze/core/miniworld_gymnasium/unified_env.py,sha256=7A4sAmMpopLfKMXtZ4jzbMlFJbzeu9Abv7hGYVbvOIA,46925
264
264
  miniworld_maze/core/miniworld_gymnasium/utils.py,sha256=9cfpg4qYz-Esxvu8nTMPFJc-Tl0TRxTrX6cfg0YuK_o,1007
265
265
  miniworld_maze/core/miniworld_gymnasium/wrappers.py,sha256=cD0nGSJYNU96zoWv63aEiKd986POhtHfGGEpNpRL5ec,122
266
266
  miniworld_maze/core/observation_types.py,sha256=Co8mEIXzIgk0MLx6tqeBd1EE0PuZOL1gbZwobiEde08,1316
267
- miniworld_maze/environments/__init__.py,sha256=6O1G4vlhUSn8OuR46u-t4Wz3de8da76Tz21TsVlzBZo,415
268
- miniworld_maze/environments/base_grid_rooms.py,sha256=kpF6psn0YCfvA-q1vtU2KaqwmfQaPpX0w9jBTxt2qs8,11833
269
- miniworld_maze/environments/factory.py,sha256=g8qYF6UF9DsKExZIHhMsP8PX3b11kvmkLMwE2IpgBxc,3501
270
- miniworld_maze/environments/nine_rooms.py,sha256=0CzUkRy2hJZ4JEEj9E-6xCopseZ1u8iGCfKLaxwATGo,2368
271
- miniworld_maze/environments/spiral_nine_rooms.py,sha256=LB9wKQXp9S8pVd8JhM9zfsc5z4ksxsQxQKoEXrDgPhE,2316
272
- miniworld_maze/environments/twenty_five_rooms.py,sha256=ExF4Mt0B3pkaVQtBF7oaVxdj_9uaAkTFYCe83Lw8Evk,3448
273
- miniworld_maze/tools/__init__.py,sha256=PgkKMO21xnwIfJtD437P_RtkguOHoOSFV1ohsO-n7tc,150
274
- miniworld_maze/tools/generate_observations.py,sha256=-WWoex0KY6PSMs4BenMTepeE4tA2NYZ26jIQL2tYfZY,6781
275
- miniworld_maze-1.1.0.dist-info/WHEEL,sha256=NHRAbdxxzyL9K3IO2LjmlNqKSyPZnKv2BD16YYVKo18,79
276
- miniworld_maze-1.1.0.dist-info/entry_points.txt,sha256=Ue03NHCOQCiJ87tqfJSns29C3Dw02jsZXL_Auzw2pb4,91
277
- miniworld_maze-1.1.0.dist-info/METADATA,sha256=atsdko20fA_iqS8y2muB0dQkLDnmLpP4ppuTuNLfcxA,9270
278
- miniworld_maze-1.1.0.dist-info/RECORD,,
267
+ miniworld_maze/environments/__init__.py,sha256=DKld5MQU7x9eNL6BlxIettA44bCiIn2zIpYECDCNxoQ,331
268
+ miniworld_maze/environments/base_grid_rooms.py,sha256=--tm6t9cHn444xJX9N3Md-4YApK38a2dQi7mnggevbI,14885
269
+ miniworld_maze/environments/factory.py,sha256=Zk26JawsMgSLMgvSnQxFhQCD8yMH76HgqFtogzWFfqY,1333
270
+ miniworld_maze/environments/nine_rooms.py,sha256=Ct96cKtSt1_nLNI5RBUhwqdNUQq1rHfBJ3aB5Igbdow,1794
271
+ miniworld_maze/environments/spiral_nine_rooms.py,sha256=a_pUuv-ghez8h76Z7YsHkQoLXsQ-w9azKLjEjO4uKmA,1749
272
+ miniworld_maze/environments/twenty_five_rooms.py,sha256=MewKPDHDilscQGTT3aGRrSHvo4uFgHHAOrnJMrHaezQ,2598
273
+ miniworld_maze/tools/__init__.py,sha256=XiReXrJIcBKvDVyPZrKPq1mckJs0_WC7q_RmdXXcKHs,55
274
+ miniworld_maze/utils.py,sha256=HTOkfRq72oOC844gVXjWMH1ox7wdSxfCS6oTrWBw05Q,7523
275
+ miniworld_maze-1.3.0.dist-info/WHEEL,sha256=NHRAbdxxzyL9K3IO2LjmlNqKSyPZnKv2BD16YYVKo18,79
276
+ miniworld_maze-1.3.0.dist-info/METADATA,sha256=qKeYDRC9Evc7PtegEs0PVFYy7fMgKlqNZPiFf3GnZkk,9657
277
+ miniworld_maze-1.3.0.dist-info/RECORD,,
@@ -1,199 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Generate comprehensive observations for Nine Rooms environment variants.
4
- Supports: NineRooms, SpiralNineRooms, TwentyFiveRooms
5
- """
6
-
7
- import argparse
8
- import os
9
-
10
- import numpy as np
11
- from PIL import Image
12
-
13
- from ..core import FrameBuffer
14
- from ..environments.factory import NineRoomsEnvironmentWrapper
15
-
16
-
17
- def generate_observations(variant, output_dir=None, high_res_full_views=False):
18
- """Generate comprehensive observations for the specified environment variant."""
19
- if output_dir is None:
20
- output_dir = f"{variant.lower()}_observations"
21
-
22
- os.makedirs(output_dir, exist_ok=True)
23
-
24
- # Create environment
25
- env = NineRoomsEnvironmentWrapper(variant=variant, size=64)
26
-
27
- # Get base environment for direct render access
28
- base_env = getattr(env, "env", getattr(env, "_env", env))
29
- while hasattr(base_env, "env") or hasattr(base_env, "_env"):
30
- if hasattr(base_env, "env"):
31
- base_env = base_env.env
32
- elif hasattr(base_env, "_env"):
33
- base_env = base_env._env
34
- else:
35
- break
36
-
37
- # Reset environment
38
- obs, _ = env.reset(seed=42)
39
-
40
- # Create high-resolution frame buffer if requested
41
- high_res_fb = None
42
- if high_res_full_views:
43
- high_res_fb = FrameBuffer(512, 512, 8)
44
-
45
- # === FULL ENVIRONMENT OBSERVATIONS ===
46
-
47
- # 1. Full view with agent at starting position
48
- if high_res_full_views:
49
- full_view_start = base_env.render_top_view(
50
- frame_buffer=high_res_fb, POMDP=False
51
- )
52
- else:
53
- full_view_start = base_env.render_top_view(POMDP=False)
54
- Image.fromarray(full_view_start).save(f"{output_dir}/full_view_start.png")
55
-
56
- # 2. Full view without agent (clean maze view)
57
- if high_res_full_views:
58
- full_view_clean = base_env.render_top_view(
59
- frame_buffer=high_res_fb, POMDP=False, render_ag=False
60
- )
61
- else:
62
- full_view_clean = base_env.render_top_view(POMDP=False, render_ag=False)
63
- Image.fromarray(full_view_clean).save(f"{output_dir}/full_view_clean.png")
64
-
65
- # 3. Full view with agent in center
66
- center_x = (base_env.min_x + base_env.max_x) / 2
67
- center_z = (base_env.min_z + base_env.max_z) / 2
68
- base_env.place_agent(pos=[center_x, 0.0, center_z])
69
- if high_res_full_views:
70
- full_view_center = base_env.render_top_view(
71
- frame_buffer=high_res_fb, POMDP=False
72
- )
73
- else:
74
- full_view_center = base_env.render_top_view(POMDP=False)
75
- Image.fromarray(full_view_center).save(f"{output_dir}/full_view_center.png")
76
-
77
- # === PARTIAL OBSERVATIONS ===
78
-
79
- # Reset agent to start position
80
- base_env.place_agent(pos=[2.5, 0.0, 2.5])
81
-
82
- # 1. Partial view from starting position
83
- partial_start = base_env.render_top_view(POMDP=True)
84
- Image.fromarray(partial_start).save(f"{output_dir}/partial_start.png")
85
-
86
- # 2. Partial view from center
87
- base_env.place_agent(pos=[center_x, 0.0, center_z])
88
- partial_center = base_env.render_top_view(POMDP=True)
89
- Image.fromarray(partial_center).save(f"{output_dir}/partial_center.png")
90
-
91
- # 3. Partial view from corner/edge
92
- corner_x = base_env.max_x - 2.5
93
- corner_z = base_env.max_z - 2.5
94
- base_env.place_agent(pos=[corner_x, 0.0, corner_z])
95
- partial_corner = base_env.render_top_view(POMDP=True)
96
- Image.fromarray(partial_corner).save(f"{output_dir}/partial_corner.png")
97
-
98
- # 4. Partial view at strategic location
99
- if variant == "NineRooms":
100
- strategic_x, strategic_z = 15.0, 7.5 # Room boundary
101
- elif variant == "SpiralNineRooms":
102
- strategic_x, strategic_z = 22.5, 15.0 # Center of spiral
103
- else: # TwentyFiveRooms
104
- strategic_x, strategic_z = 37.5, 37.5 # Mid-outer area
105
-
106
- base_env.place_agent(pos=[strategic_x, 0.0, strategic_z])
107
- partial_strategic = base_env.render_top_view(POMDP=True)
108
- Image.fromarray(partial_strategic).save(f"{output_dir}/partial_strategic.png")
109
-
110
- # === WRAPPED OBSERVATIONS ===
111
-
112
- # Reset environment
113
- obs, _ = env.reset(seed=42)
114
-
115
- # 1. Standard gymnasium observation
116
- obs_hwc = np.transpose(obs, (1, 2, 0))
117
- Image.fromarray(obs_hwc).save(f"{output_dir}/gymnasium_standard.png")
118
-
119
- # 2. Observations after movement
120
- actions = [2, 2, 1] # move_forward, move_forward, turn_right
121
- action_names = ["move_forward", "move_forward", "turn_right"]
122
-
123
- for i, action in enumerate(actions):
124
- obs, _, _, _, _ = env.step(action)
125
- obs_hwc = np.transpose(obs, (1, 2, 0))
126
- Image.fromarray(obs_hwc).save(
127
- f"{output_dir}/gymnasium_step_{i + 1}_{action_names[i]}.png"
128
- )
129
-
130
- # === RENDER_ON_POS EXAMPLES ===
131
-
132
- # Define test positions based on variant
133
- if variant == "NineRooms":
134
- test_positions = [
135
- [7.5, 0.0, 7.5], # top-middle room center
136
- [37.5, 0.0, 37.5], # bottom-right room center
137
- [22.5, 0.0, 22.5], # environment center
138
- [7.5, 0.0, 22.5], # middle-left room center
139
- ]
140
- elif variant == "SpiralNineRooms":
141
- test_positions = [
142
- [7.5, 0.0, 7.5], # top-left room (spiral start)
143
- [37.5, 0.0, 37.5], # bottom-right room (spiral end)
144
- [22.5, 0.0, 7.5], # top-right room
145
- [7.5, 0.0, 37.5], # bottom-left room
146
- ]
147
- else: # TwentyFiveRooms
148
- test_positions = [
149
- [37.5, 0.0, 37.5], # room (1,1) - near corner
150
- [112.5, 0.0, 112.5], # room (4,4) - far corner
151
- [75.0, 0.0, 75.0], # center room (2,2)
152
- [37.5, 0.0, 112.5], # room (1,4) - opposite corner
153
- ]
154
-
155
- for i, pos in enumerate(test_positions):
156
- render_obs = env.render_on_pos(pos)
157
-
158
- # Convert CHW to HWC for PIL
159
- if len(render_obs.shape) == 3 and render_obs.shape[0] == 3:
160
- render_obs = np.transpose(render_obs, (1, 2, 0))
161
-
162
- Image.fromarray(render_obs).save(f"{output_dir}/render_on_pos_{i + 1}.png")
163
-
164
- env.close()
165
- return output_dir
166
-
167
-
168
- def main():
169
- parser = argparse.ArgumentParser(
170
- description="Generate observations for Nine Rooms environment variants"
171
- )
172
- parser.add_argument(
173
- "variant",
174
- choices=["NineRooms", "SpiralNineRooms", "TwentyFiveRooms"],
175
- help="Environment variant to use",
176
- )
177
- parser.add_argument(
178
- "--output-dir",
179
- type=str,
180
- default=None,
181
- help="Output directory for images (default: {variant}_observations)",
182
- )
183
- parser.add_argument(
184
- "--high-res-full",
185
- action="store_true",
186
- help="Generate 512x512 high-resolution full environment views",
187
- )
188
-
189
- args = parser.parse_args()
190
-
191
- output_dir = generate_observations(
192
- args.variant, args.output_dir, args.high_res_full
193
- )
194
-
195
- return output_dir
196
-
197
-
198
- if __name__ == "__main__":
199
- main()
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- generate-observations = miniworld_maze.tools.generate_observations:main
3
-