123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- import numpy as np
- from minigrid.core.constants import (
- COLOR_TO_IDX,
- COLORS,
- IDX_TO_COLOR,
- IDX_TO_OBJECT,
- OBJECT_TO_IDX,
- )
- from minigrid.utils.rendering import (
- fill_coords,
- point_in_circle,
- point_in_line,
- point_in_rect,
- )
- class WorldObj:
- """
- Base class for grid world objects
- """
- def __init__(self, type, color):
- assert type in OBJECT_TO_IDX, type
- assert color in COLOR_TO_IDX, color
- self.type = type
- self.color = color
- self.contains = None
- # Initial position of the object
- self.init_pos = None
- # Current position of the object
- self.cur_pos = None
- def can_overlap(self):
- """Can the agent overlap with this?"""
- return False
- def can_pickup(self):
- """Can the agent pick this up?"""
- return False
- def can_contain(self):
- """Can this contain another object?"""
- return False
- def see_behind(self):
- """Can the agent see behind this object?"""
- return True
- def toggle(self, env, pos):
- """Method to trigger/toggle an action this object performs"""
- return False
- def encode(self):
- """Encode the a description of this object as a 3-tuple of integers"""
- return (OBJECT_TO_IDX[self.type], COLOR_TO_IDX[self.color], 0)
- @staticmethod
- def decode(type_idx, color_idx, state):
- """Create an object from a 3-tuple state description"""
- obj_type = IDX_TO_OBJECT[type_idx]
- color = IDX_TO_COLOR[color_idx]
- if obj_type == "empty" or obj_type == "unseen":
- return None
- # State, 0: open, 1: closed, 2: locked
- is_open = state == 0
- is_locked = state == 2
- if obj_type == "wall":
- v = Wall(color)
- elif obj_type == "floor":
- v = Floor(color)
- elif obj_type == "ball":
- v = Ball(color)
- elif obj_type == "key":
- v = Key(color)
- elif obj_type == "box":
- v = Box(color)
- elif obj_type == "door":
- v = Door(color, is_open, is_locked)
- elif obj_type == "goal":
- v = Goal()
- elif obj_type == "lava":
- v = Lava()
- else:
- assert False, "unknown object type in decode '%s'" % obj_type
- return v
- def render(self, r):
- """Draw this object with the given renderer"""
- raise NotImplementedError
- class Goal(WorldObj):
- def __init__(self):
- super().__init__("goal", "green")
- def can_overlap(self):
- return True
- def render(self, img):
- fill_coords(img, point_in_rect(0, 1, 0, 1), COLORS[self.color])
- class Floor(WorldObj):
- """
- Colored floor tile the agent can walk over
- """
- def __init__(self, color="blue"):
- super().__init__("floor", color)
- def can_overlap(self):
- return True
- def render(self, img):
- # Give the floor a pale color
- color = COLORS[self.color] / 2
- fill_coords(img, point_in_rect(0.031, 1, 0.031, 1), color)
- class Lava(WorldObj):
- def __init__(self):
- super().__init__("lava", "red")
- def can_overlap(self):
- return True
- def render(self, img):
- c = (255, 128, 0)
- # Background color
- fill_coords(img, point_in_rect(0, 1, 0, 1), c)
- # Little waves
- for i in range(3):
- ylo = 0.3 + 0.2 * i
- yhi = 0.4 + 0.2 * i
- fill_coords(img, point_in_line(0.1, ylo, 0.3, yhi, r=0.03), (0, 0, 0))
- fill_coords(img, point_in_line(0.3, yhi, 0.5, ylo, r=0.03), (0, 0, 0))
- fill_coords(img, point_in_line(0.5, ylo, 0.7, yhi, r=0.03), (0, 0, 0))
- fill_coords(img, point_in_line(0.7, yhi, 0.9, ylo, r=0.03), (0, 0, 0))
- class Wall(WorldObj):
- def __init__(self, color="grey"):
- super().__init__("wall", color)
- def see_behind(self):
- return False
- def render(self, img):
- fill_coords(img, point_in_rect(0, 1, 0, 1), COLORS[self.color])
- class Door(WorldObj):
- def __init__(self, color, is_open=False, is_locked=False):
- super().__init__("door", color)
- self.is_open = is_open
- self.is_locked = is_locked
- def can_overlap(self):
- """The agent can only walk over this cell when the door is open"""
- return self.is_open
- def see_behind(self):
- return self.is_open
- def toggle(self, env, pos):
- # If the player has the right key to open the door
- if self.is_locked:
- if isinstance(env.carrying, Key) and env.carrying.color == self.color:
- self.is_locked = False
- self.is_open = True
- return True
- return False
- self.is_open = not self.is_open
- return True
- def encode(self):
- """Encode the a description of this object as a 3-tuple of integers"""
- # State, 0: open, 1: closed, 2: locked
- if self.is_open:
- state = 0
- elif self.is_locked:
- state = 2
- # if door is closed and unlocked
- elif not self.is_open:
- state = 1
- else:
- raise ValueError(
- 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)
- def render(self, img):
- c = COLORS[self.color]
- if self.is_open:
- fill_coords(img, point_in_rect(0.88, 1.00, 0.00, 1.00), c)
- fill_coords(img, point_in_rect(0.92, 0.96, 0.04, 0.96), (0, 0, 0))
- return
- # Door frame and door
- if self.is_locked:
- fill_coords(img, point_in_rect(0.00, 1.00, 0.00, 1.00), c)
- fill_coords(img, point_in_rect(0.06, 0.94, 0.06, 0.94), 0.45 * np.array(c))
- # Draw key slot
- fill_coords(img, point_in_rect(0.52, 0.75, 0.50, 0.56), c)
- else:
- fill_coords(img, point_in_rect(0.00, 1.00, 0.00, 1.00), c)
- fill_coords(img, point_in_rect(0.04, 0.96, 0.04, 0.96), (0, 0, 0))
- fill_coords(img, point_in_rect(0.08, 0.92, 0.08, 0.92), c)
- fill_coords(img, point_in_rect(0.12, 0.88, 0.12, 0.88), (0, 0, 0))
- # Draw door handle
- fill_coords(img, point_in_circle(cx=0.75, cy=0.50, r=0.08), c)
- class Key(WorldObj):
- def __init__(self, color="blue"):
- super().__init__("key", color)
- def can_pickup(self):
- return True
- def render(self, img):
- c = COLORS[self.color]
- # Vertical quad
- fill_coords(img, point_in_rect(0.50, 0.63, 0.31, 0.88), c)
- # Teeth
- fill_coords(img, point_in_rect(0.38, 0.50, 0.59, 0.66), c)
- fill_coords(img, point_in_rect(0.38, 0.50, 0.81, 0.88), c)
- # Ring
- fill_coords(img, point_in_circle(cx=0.56, cy=0.28, r=0.190), c)
- fill_coords(img, point_in_circle(cx=0.56, cy=0.28, r=0.064), (0, 0, 0))
- class Ball(WorldObj):
- def __init__(self, color="blue"):
- super().__init__("ball", color)
- def can_pickup(self):
- return True
- def render(self, img):
- fill_coords(img, point_in_circle(0.5, 0.5, 0.31), COLORS[self.color])
- class Box(WorldObj):
- def __init__(self, color, contains=None):
- super().__init__("box", color)
- self.contains = contains
- def can_pickup(self):
- return True
- def render(self, img):
- c = COLORS[self.color]
- # Outline
- fill_coords(img, point_in_rect(0.12, 0.88, 0.12, 0.88), c)
- fill_coords(img, point_in_rect(0.18, 0.82, 0.18, 0.82), (0, 0, 0))
- # Horizontal slit
- fill_coords(img, point_in_rect(0.16, 0.84, 0.47, 0.53), c)
- def toggle(self, env, pos):
- # Replace the box by its contents
- env.grid.set(pos[0], pos[1], self.contains)
- return True
|