unlock.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. """
  2. Copied and adapted from https://github.com/mila-iqia/babyai.
  3. Levels described in the Baby AI ICLR 2019 submission, with the `Unlock` instruction.
  4. """
  5. from typing import Optional
  6. from minigrid.core.constants import COLOR_NAMES
  7. from minigrid.core.world_object import Ball, Box, Key
  8. from minigrid.envs.babyai.core.roomgrid_level import RoomGridLevel
  9. from minigrid.envs.babyai.core.verifier import ObjDesc, OpenInstr, PickupInstr
  10. class Unlock(RoomGridLevel):
  11. """
  12. Unlock a door.
  13. Competencies: Maze, Open, Unlock. No unblocking.
  14. """
  15. def gen_mission(self):
  16. # Add a locked door to a random room
  17. id = self._rand_int(0, self.num_cols)
  18. jd = self._rand_int(0, self.num_rows)
  19. door, pos = self.add_door(id, jd, locked=True)
  20. locked_room = self.get_room(id, jd)
  21. # Add the key to a different room
  22. while True:
  23. ik = self._rand_int(0, self.num_cols)
  24. jk = self._rand_int(0, self.num_rows)
  25. if ik is id and jk is jd:
  26. continue
  27. self.add_object(ik, jk, "key", door.color)
  28. break
  29. # With 50% probability, ensure that the locked door is the only
  30. # door of that color
  31. if self._rand_bool():
  32. colors = list(filter(lambda c: c is not door.color, COLOR_NAMES))
  33. self.connect_all(door_colors=colors)
  34. else:
  35. self.connect_all()
  36. # Add distractors to all but the locked room.
  37. # We do this to speed up the reachability test,
  38. # which otherwise will reject all levels with
  39. # objects in the locked room.
  40. for i in range(self.num_cols):
  41. for j in range(self.num_rows):
  42. if i is not id or j is not jd:
  43. self.add_distractors(i, j, num_distractors=3, all_unique=False)
  44. # The agent must be placed after all the object to respect constraints
  45. while True:
  46. self.place_agent()
  47. start_room = self.room_from_pos(*self.agent_pos)
  48. # Ensure that we are not placing the agent in the locked room
  49. if start_room is locked_room:
  50. continue
  51. break
  52. self.check_objs_reachable()
  53. self.instrs = OpenInstr(ObjDesc(door.type, door.color))
  54. class UnlockLocal(RoomGridLevel):
  55. """
  56. Fetch a key and unlock a door
  57. (in the current room)
  58. """
  59. def __init__(self, distractors=False, **kwargs):
  60. self.distractors = distractors
  61. super().__init__(**kwargs)
  62. def gen_mission(self):
  63. door, _ = self.add_door(1, 1, locked=True)
  64. self.add_object(1, 1, "key", door.color)
  65. if self.distractors:
  66. self.add_distractors(1, 1, num_distractors=3)
  67. self.place_agent(1, 1)
  68. self.instrs = OpenInstr(ObjDesc(door.type))
  69. class KeyInBox(RoomGridLevel):
  70. """
  71. Unlock a door. Key is in a box (in the current room).
  72. """
  73. def __init__(self, **kwargs):
  74. super().__init__(**kwargs)
  75. def gen_mission(self):
  76. door, _ = self.add_door(1, 1, locked=True)
  77. # Put the key in the box, then place the box in the room
  78. key = Key(door.color)
  79. box = Box(self._rand_color(), key)
  80. self.place_in_room(1, 1, box)
  81. self.place_agent(1, 1)
  82. self.instrs = OpenInstr(ObjDesc(door.type))
  83. class UnlockPickup(RoomGridLevel):
  84. """
  85. Unlock a door, then pick up a box in another room
  86. """
  87. def __init__(self, distractors=False, max_steps: Optional[int] = None, **kwargs):
  88. self.distractors = distractors
  89. room_size = 6
  90. if max is None:
  91. max_steps = 8 * room_size**2
  92. super().__init__(
  93. num_rows=1, num_cols=2, room_size=6, max_steps=max_steps, **kwargs
  94. )
  95. def gen_mission(self):
  96. # Add a random object to the room on the right
  97. obj, _ = self.add_object(1, 0, kind="box")
  98. # Make sure the two rooms are directly connected by a locked door
  99. door, _ = self.add_door(0, 0, 0, locked=True)
  100. # Add a key to unlock the door
  101. self.add_object(0, 0, "key", door.color)
  102. if self.distractors:
  103. self.add_distractors(num_distractors=4)
  104. self.place_agent(0, 0)
  105. self.instrs = PickupInstr(ObjDesc(obj.type, obj.color))
  106. class BlockedUnlockPickup(RoomGridLevel):
  107. """
  108. Unlock a door blocked by a ball, then pick up a box
  109. in another room
  110. """
  111. def __init__(self, max_steps: Optional[int] = None, **kwargs):
  112. room_size = 6
  113. if max_steps is None:
  114. max_steps = 16 * room_size**2
  115. super().__init__(
  116. num_rows=1, num_cols=2, room_size=room_size, max_steps=max_steps, **kwargs
  117. )
  118. def gen_mission(self):
  119. # Add a box to the room on the right
  120. obj, _ = self.add_object(1, 0, kind="box")
  121. # Make sure the two rooms are directly connected by a locked door
  122. door, pos = self.add_door(0, 0, 0, locked=True)
  123. # Block the door with a ball
  124. color = self._rand_color()
  125. self.grid.set(pos[0] - 1, pos[1], Ball(color))
  126. # Add a key to unlock the door
  127. self.add_object(0, 0, "key", door.color)
  128. self.place_agent(0, 0)
  129. self.instrs = PickupInstr(ObjDesc(obj.type))
  130. class UnlockToUnlock(RoomGridLevel):
  131. """
  132. Unlock a door A that requires to unlock a door B before
  133. """
  134. def __init__(self, max_steps: Optional[int] = None, **kwargs):
  135. room_size = 6
  136. if max_steps is None:
  137. max_steps = 30 * room_size**2
  138. super().__init__(
  139. num_rows=1, num_cols=3, room_size=room_size, max_steps=max_steps, **kwargs
  140. )
  141. def gen_mission(self):
  142. colors = self._rand_subset(COLOR_NAMES, 2)
  143. # Add a door of color A connecting left and middle room
  144. self.add_door(0, 0, door_idx=0, color=colors[0], locked=True)
  145. # Add a key of color A in the room on the right
  146. self.add_object(2, 0, kind="key", color=colors[0])
  147. # Add a door of color B connecting middle and right room
  148. self.add_door(1, 0, door_idx=0, color=colors[1], locked=True)
  149. # Add a key of color B in the middle room
  150. self.add_object(1, 0, kind="key", color=colors[1])
  151. obj, _ = self.add_object(0, 0, kind="ball")
  152. self.place_agent(1, 0)
  153. self.instrs = PickupInstr(ObjDesc(obj.type))