text: import numpy as np
  from multiagent.core import World, Agent, Landmark
  from multiagent.scenario import BaseScenario
  
  
  class Scenario(BaseScenario):
      def make_world(self, num_agents=1, num_landmarks=1, num_obstacles=2, 
          fixed_obstacles=True):
          world = World()
          # set any world properties first
          world.dim_c = 2
          world = self.setup_world(world, num_agents, num_landmarks, num_obstacles)
          world.fixed_obstacles = fixed_obstacles
          self.reset_world(world)
          return world
  
      # called only once while initializing the world    
      def setup_world(self, world, num_agents=1, num_landmarks=1, num_obstacles=2):
          world.num_agents = num_agents
          world.num_landmarks = num_landmarks
          world.num_obstacles = num_obstacles
  
          world.agents = [Agent() for i in range(world.num_agents)]
          for i, agent in enumerate(world.agents):
              agent.name = 'agent %d' % i
              agent.collide = True
              agent.silent = True
              agent.size = 0.10
          # add landmarks
          world.landmarks = [Landmark() for i in range(world.num_landmarks)]
          for i, landmark in enumerate(world.landmarks):
              landmark.name = 'landmark %d' % i
              landmark.collide = False
              landmark.movable = False
              landmark.size = 0.10
  
          # TODO: currently obstacle is also set as a landmark    
          '''
          obstacles = [Landmark() for i in range(world.num_obstacles)]
          poses = [[-0.25,0.25],[0.25,-0.25]]
          for i, obstacle in enumerate(obstacles):    
              obstacle.name = 'obstacle %d' % i
              obstacle.collide = True
              obstacle.movable = False    
              # hand crafted positions
              obstacle.size = 0.15
              obstacle.state.p_pos = np.array(poses[i])
              obstacle.state.p_vel = np.zeros(world.dim_p)
          '''
          obstacles = self.custom_obstacles_layout(world)
          # TODO: for sake of physics simulation (obstacles are considered obstacles)    
          world.landmarks = world.landmarks + obstacles
          return world    
  
      def reset_world(self, world):
          # random properties for agents
          for i, agent in enumerate(world.agents):
              agent.color = np.array([0.35, 0.35, 0.85])
          # random properties for landmarks
          for i, landmark in enumerate(world.landmarks):
              if i < world.num_landmarks:
                  landmark.color = np.array([0.25, 0.25, 0.25])
              # obstacle color
              else:
                  landmark.color = np.array([0.9, 0.2, 0.8])
  
          # set random initial states
          for agent in world.agents:
              agent.state.p_pos = np.random.uniform(-1, +1, world.dim_p)
              agent.state.p_vel = np.zeros(world.dim_p)
              agent.state.c = np.zeros(world.dim_c)
  
          self.reset_landmarks(world)    
          #for i, landmark in enumerate(world.landmarks[:world.num_landmarks]):
              #landmark.state.p_pos = np.random.uniform(-1, +1, world.dim_p)
              #landmark.state.p_vel = np.zeros(world.dim_p)
  
  
      def reset_landmarks(self, world):
          for i, landmark in enumerate(world.landmarks[:world.num_landmarks]):
              landmark.state.p_vel = np.zeros(world.dim_p)
              found = False
              while not found:            
                  landmark.state.p_pos = np.random.uniform(-1, +1, world.dim_p)
                  found = self.valid_landmark(world, landmark)
  
              
      def valid_landmark(self, world, landmark):
          for i, obstacle in enumerate(world.landmarks[world.num_landmarks:]):
              dist = np.linalg.norm(landmark.state.p_pos - obstacle.state.p_pos)
              if dist < landmark.size + obstacle.size:
                  return False
          return True        
  
  
      def custom_obstacles_layout(self, world):
          # draw a custom layout of circular obstacles (for now)
  
          # assert num_obstacles = 6
          obstacles = [Landmark() for i in range(world.num_obstacles)]
          poses = [[-.35,-.3],[-.35,0],[-.35,.3],[.35,-.3],[.35,0],[.35,.3]]
          for i, obstacle in enumerate(obstacles):    
              obstacle.name = 'obstacle %d' % i
              obstacle.collide = True
              obstacle.movable = False    
              # hand crafted positions
              obstacle.size = 0.15
              obstacle.state.p_pos = np.array(poses[i])
              obstacle.state.p_vel = np.zeros(world.dim_p)
          return obstacles
  
  
  
  
      def benchmark_data(self, agent, world):
          rew = 0
          collisions = 0
          occupied_landmarks = 0
          min_dists = 0
          for l in world.landmarks:
              dists = [np.sqrt(np.sum(np.square(a.state.p_pos - l.state.p_pos))) for a in world.agents]
              min_dists += min(dists)
              rew -= min(dists)
              if min(dists) < 0.1:
                  occupied_landmarks += 1
          if agent.collide:
              for a in world.agents:
                  if self.is_collision(a, agent):
                      rew -= 1
                      collisions += 1
          return (rew, collisions, min_dists, occupied_landmarks)
  
  
      def is_collision(self, agent1, agent2):
          delta_pos = agent1.state.p_pos - agent2.state.p_pos
          dist = np.sqrt(np.sum(np.square(delta_pos)))
          dist_min = agent1.size + agent2.size
          return True if dist < dist_min else False
  
      def reward(self, agent, world):
          # Agents are rewarded based on minimum agent distance to each landmark, penalized for collisions
          rew = 0
          for l in world.landmarks:
              dists = [np.sqrt(np.sum(np.square(a.state.p_pos - l.state.p_pos))) for a in world.agents]
              rew -= min(dists)
          if agent.collide:
              for a in world.agents:
                  if self.is_collision(a, agent):
                      rew -= 1
              # NOTE - it seems that due to self collision, rew is decreased by 1
              rew += 1        
          return rew
  
      def observation(self, agent, world):
          # get positions of all entities in this agent's reference frame
          entity_pos = []
          for entity in world.landmarks:  # world.entities:
              entity_pos.append(entity.state.p_pos - agent.state.p_pos)
          # entity colors
          entity_color = []
          for entity in world.landmarks:  # world.entities:
              entity_color.append(entity.color)
          # communication of all other agents
          comm = []
          other_pos = []
          for other in world.agents:
              if other is agent: continue
              comm.append(other.state.c)
              other_pos.append(other.state.p_pos - agent.state.p_pos)
          return np.concatenate([agent.state.p_vel] + [agent.state.p_pos] + entity_pos + other_pos + comm)
  