From d9d8784338f26c991756356ebbb1019696915461 Mon Sep 17 00:00:00 2001
From: steffen-illium <steffen.illium@ifi.lmu.de>
Date: Thu, 17 Jun 2021 14:27:18 +0200
Subject: [PATCH] Agent Trained on Doors

---
 .../factory/assets/agent_collision.png        | Bin 18857 -> 0 bytes
 environments/factory/base_factory.py          |  19 +++--
 environments/factory/levels/rooms.txt         |   2 +-
 environments/factory/renderer.py              |  18 ++---
 environments/factory/simple_factory.py        |  27 +++++--
 environments/oo_factory/__init__.py           |   0
 environments/oo_factory/_base_factory.py      |  68 ++++++++++++++++++
 environments/utility_classes.py               |  11 ++-
 main.py                                       |   2 +-
 reload_agent.py                               |   6 +-
 10 files changed, 125 insertions(+), 28 deletions(-)
 delete mode 100644 environments/factory/assets/agent_collision.png
 create mode 100644 environments/oo_factory/__init__.py
 create mode 100644 environments/oo_factory/_base_factory.py

diff --git a/environments/factory/assets/agent_collision.png b/environments/factory/assets/agent_collision.png
deleted file mode 100644
index d7f228c7a76cb2d40e08b7a4bc5794a92731cab8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 18857
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
SLdKf_0000<MNUMnLSTYVp{U{j

diff --git a/environments/factory/base_factory.py b/environments/factory/base_factory.py
index 539d60d..7bb131e 100644
--- a/environments/factory/base_factory.py
+++ b/environments/factory/base_factory.py
@@ -22,10 +22,11 @@ class BaseFactory(gym.Env):
     @property
     def observation_space(self):
         agent_slice = self.n_agents if self.omit_agent_slice_in_obs else 0
-        agent_slice = 1 if self.combin_agent_slices_in_obs else agent_slice
+        agent_slice = (self.n_agents - 1) if self.combin_agent_slices_in_obs else agent_slice
         if self.pomdp_radius:
-            return spaces.Box(low=0, high=1, shape=(self._state.shape[0] - agent_slice, self.pomdp_radius * 2 + 1,
-                                                    self.pomdp_radius * 2 + 1), dtype=np.float32)
+            shape = (self._state.shape[0] - agent_slice, self.pomdp_radius * 2 + 1, self.pomdp_radius * 2 + 1)
+            space = spaces.Box(low=0, high=1, shape=shape, dtype=np.float32)
+            return space
         else:
             shape = [x-agent_slice if idx == 0 else x for idx, x in enumerate(self._state.shape)]
             space = spaces.Box(low=0, high=1, shape=shape, dtype=np.float32)
@@ -194,6 +195,14 @@ class BaseFactory(gym.Env):
             if self.done_at_collision and collision_vec.any():
                 done = True
 
+        # Step the door close intervall
+        agents_pos = [agent.pos for agent in self._agent_states]
+        for door_i, door in enumerate(self._door_states):
+            if door.is_open and door.time_to_close and door.pos not in agents_pos:
+                door.time_to_close -= 1
+            elif door.is_open and not door.time_to_close and door.pos not in agents_pos:
+                door.use()
+
         reward, info = self.calculate_reward(self._agent_states)
 
         if self._steps >= self.max_steps:
@@ -256,7 +265,7 @@ class BaseFactory(gym.Env):
         x_new = x + x_diff
         y_new = y + y_diff
 
-        if h.DOORS in self._state_slices.values():
+        if h.DOORS in self._state_slices.values() and self._agent_states[agent_i]._last_pos != (-1, -1):
             door = [door for door in self._door_states if door.pos == (x, y)]
             if door:
                 door = door[0]
@@ -326,7 +335,7 @@ class BaseFactory(gym.Env):
         # Returns: Reward, Info
         raise NotImplementedError
 
-    def render(self):
+    def render(self, mode='human'):
         raise NotImplementedError
 
     def save_params(self, filepath: Path):
diff --git a/environments/factory/levels/rooms.txt b/environments/factory/levels/rooms.txt
index 781de13..43e8193 100644
--- a/environments/factory/levels/rooms.txt
+++ b/environments/factory/levels/rooms.txt
@@ -7,7 +7,7 @@
 ###x#######x###
 #1111##2222222#
 #11111#2222#22#
-#11111D2222222#
+#11111x2222222#
 #11111#2222222#
 #11111#2222222#
 ###############
\ No newline at end of file
diff --git a/environments/factory/renderer.py b/environments/factory/renderer.py
index b134687..fcc01a6 100644
--- a/environments/factory/renderer.py
+++ b/environments/factory/renderer.py
@@ -42,8 +42,6 @@ class Renderer:
         self.font.set_bold(1.0)
         print('Loading System font with pygame.font.Font took', time.time() - now)
 
-
-
     def fill_bg(self):
         self.screen.fill(Renderer.BG_COLOR)
         if self.grid_lines:
@@ -71,9 +69,9 @@ class Renderer:
 
     def load_asset(self, path, factor=1.0):
         s = int(factor*self.cell_size)
-        wall_img = pygame.image.load(path).convert_alpha()
-        wall_img = pygame.transform.smoothscale(wall_img, (s, s))
-        return wall_img
+        asset = pygame.image.load(path).convert_alpha()
+        asset = pygame.transform.smoothscale(asset, (s, s))
+        return asset
 
     def render(self, entities):
         for event in pygame.event.get():
@@ -82,7 +80,10 @@ class Renderer:
                 sys.exit()
         self.fill_bg()
         blits = deque()
-        for entity in entities:
+        for entity in [x for x in entities if 'door' in x.name]:
+            bp = self.blit_params(entity)
+            blits.append(bp)
+        for entity in [x for x in entities if 'door' not in x.name]:
             bp = self.blit_params(entity)
             blits.append(bp)
             if entity.name.lower() == 'agent':
@@ -106,7 +107,6 @@ class Renderer:
         for blit in blits:
             self.screen.blit(**blit)
 
-
         pygame.display.flip()
         self.clock.tick(self.fps)
 
@@ -114,6 +114,6 @@ class Renderer:
 if __name__ == '__main__':
     renderer = Renderer(fps=2, cell_size=40)
     for i in range(15):
-        entity = Entity('agent', [5, i], 1, 'idle', 'idle')
-        renderer.render([entity])
+        entity_1 = Entity('agent', [5, i], 1, 'idle', 'idle')
+        renderer.render([entity_1])
 
diff --git a/environments/factory/simple_factory.py b/environments/factory/simple_factory.py
index 30de773..206febb 100644
--- a/environments/factory/simple_factory.py
+++ b/environments/factory/simple_factory.py
@@ -14,7 +14,7 @@ CLEAN_UP_ACTION = 'clean_up'
 
 
 class DirtProperties(NamedTuple):
-    clean_amount: int = 2            # How much does the robot clean with one action.
+    clean_amount: int = 2            # How much does the robot clean with one actions.
     max_spawn_ratio: float = 0.2       # On max how much tiles does the dirt spawn in percent.
     gain_amount: float = 0.5           # How much dirt does spawn per tile
     spawn_frequency: int = 5         # Spawn Frequency in Steps
@@ -41,7 +41,7 @@ class SimpleFactory(BaseFactory):
         self._renderer = None  # expensive - don't use it when not required !
         super(SimpleFactory, self).__init__(*args, additional_slices=['dirt'], **kwargs)
 
-    def render(self):
+    def render(self, mode='human'):
 
         if not self._renderer:  # lazy init
             height, width = self._state.shape[1:]
@@ -67,7 +67,11 @@ class SimpleFactory(BaseFactory):
         for i, agent in enumerate(self._agent_states):
             name, state = asset_str(agent)
             agents.append(Entity(name, agent.pos, 1, 'none', state, i+1))
-        self._renderer.render(dirt+walls+agents)
+        doors = []
+        for i, door in enumerate(self._door_states):
+            name, state = 'door_open' if door.is_open else 'door_closed', 'blank'
+            agents.append(Entity(name, door.pos, 1, 'none', state, i+1))
+        self._renderer.render(dirt+walls+agents+doors)
 
     def spawn_dirt(self) -> None:
         if not np.argwhere(self._state[DIRT_INDEX] != h.IS_FREE_CELL).shape[0] > self.dirt_properties.max_global_amount:
@@ -156,6 +160,7 @@ class SimpleFactory(BaseFactory):
                     self.print(f'Agent {agent_state.i} just tried to clean up some dirt '
                                f'at {agent_state.pos}, but was unsucsessfull.')
                     info_dict.update({f'agent_{agent_state.i}_failed_action': 1})
+                    info_dict.update({f'agent_{agent_state.i}_failed_dirt_cleanup': 1})
 
             elif self._actions.is_moving_action(agent_state.action):
                 if agent_state.action_valid:
@@ -165,6 +170,17 @@ class SimpleFactory(BaseFactory):
                     # self.print('collision')
                     reward -= 0.01
 
+            elif self._actions.is_door_usage(agent_state.action):
+                if agent_state.action_valid:
+                    reward += 0.1
+                    self.print(f'Agent {agent_state.i} did just use the door at {agent_state.pos}.')
+                    info_dict.update(door_used=1)
+                else:
+                    self.print(f'Agent {agent_state.i} just tried to use a door '
+                               f'at {agent_state.pos}, but was unsucsessfull.')
+                    info_dict.update({f'agent_{agent_state.i}_failed_action': 1})
+                    info_dict.update({f'agent_{agent_state.i}_failed_door_open': 1})
+
             else:
                 info_dict.update(no_op=1)
                 reward -= 0.00
@@ -184,7 +200,7 @@ class SimpleFactory(BaseFactory):
 
 
 if __name__ == '__main__':
-    render = True
+    render = False
 
     move_props = MovementProperties(allow_diagonal_movement=True, allow_square_movement=True)
     dirt_props = DirtProperties()
@@ -193,8 +209,9 @@ if __name__ == '__main__':
                             pomdp_radius=3)
 
     n_actions = factory.action_space.n - 1
+    _ = factory.observation_space
 
-    for epoch in range(100):
+    for epoch in range(10000):
         random_actions = [[random.randint(0, n_actions) for _ in range(factory.n_agents)] for _ in range(200)]
         env_state = factory.reset()
         r = 0
diff --git a/environments/oo_factory/__init__.py b/environments/oo_factory/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/environments/oo_factory/_base_factory.py b/environments/oo_factory/_base_factory.py
new file mode 100644
index 0000000..5fe2091
--- /dev/null
+++ b/environments/oo_factory/_base_factory.py
@@ -0,0 +1,68 @@
+from typing import List, Union
+
+import gym
+
+
+class Entities():
+
+    def __init__(self):
+        pass
+
+
+# noinspection PyAttributeOutsideInit
+class BaseFactory(gym.Env):
+
+    def __enter__(self):
+        return self if self.frames_to_stack == 0 else FrameStack(self, self.frames_to_stack)
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+
+    def __init__(self, level_name='simple', n_agents=1, max_steps=int(5e2), pomdp_radius: Union[None, int] = 0,
+                 movement_properties: MovementProperties = MovementProperties(),
+                 combin_agent_slices_in_obs: bool = False, frames_to_stack=0,
+                 omit_agent_slice_in_obs=False, **kwargs):
+        assert (combin_agent_slices_in_obs != omit_agent_slice_in_obs) or \
+               (not combin_agent_slices_in_obs and not omit_agent_slice_in_obs), \
+            'Both options are exclusive'
+        assert frames_to_stack != 1 and frames_to_stack >= 0, "'frames_to_stack' cannot be negative or 1."
+
+        self.movement_properties = movement_properties
+        self.level_name = level_name
+
+        self.n_agents = n_agents
+        self.max_steps = max_steps
+        self.pomdp_radius = pomdp_radius
+        self.combin_agent_slices_in_obs = combin_agent_slices_in_obs
+        self.omit_agent_slice_in_obs = omit_agent_slice_in_obs
+        self.frames_to_stack = frames_to_stack
+
+        self.done_at_collision = False
+
+        self._state_slices = StateSlices()
+        level_filepath = Path(__file__).parent / h.LEVELS_DIR / f'{self.level_name}.txt'
+        parsed_level = h.parse_level(level_filepath)
+        self._level = h.one_hot_level(parsed_level)
+        parsed_doors = h.one_hot_level(parsed_level, h.DOOR)
+        if parsed_doors.any():
+            self._doors = parsed_doors
+            level_slices = ['level', 'doors']
+            can_use_doors = True
+        else:
+            level_slices = ['level']
+            can_use_doors = False
+        offset = len(level_slices)
+        self._state_slices.register_additional_items([*level_slices,
+                                                      *[f'agent#{i}' for i in range(offset, n_agents + offset)]])
+        if 'additional_slices' in kwargs:
+            self._state_slices.register_additional_items(kwargs.get('additional_slices'))
+        self._zones = Zones(parsed_level)
+
+        self._actions = Actions(self.movement_properties, can_use_doors=can_use_doors)
+        self._actions.register_additional_items(self.additional_actions)
+        self.reset()
+
+
+    def step(self, actions: Union[int, List[int]]):
+        actions = actions if isinstance(actions, list) else [actions]
+        self.entities.step()
\ No newline at end of file
diff --git a/environments/utility_classes.py b/environments/utility_classes.py
index 0449946..2ee27a5 100644
--- a/environments/utility_classes.py
+++ b/environments/utility_classes.py
@@ -108,10 +108,12 @@ class AgentState:
 
 class DoorState:
 
-    def __init__(self, i: int, pos: Tuple[int, int], closed_on_init=True):
+    def __init__(self, i: int, pos: Tuple[int, int], closed_on_init=True, auto_close_interval=10):
         self.i = i
         self.pos = pos
         self._state = self._state = IS_CLOSED if closed_on_init else IS_OPEN
+        self.auto_close_interval = auto_close_interval
+        self.time_to_close = -1
 
     @property
     def is_closed(self):
@@ -126,8 +128,11 @@ class DoorState:
         return self._state
 
     def use(self):
-        self._state: str = IS_CLOSED if self._state == IS_OPEN else IS_OPEN
-
+        if self._state == IS_OPEN:
+            self._state = IS_CLOSED
+        else:
+            self._state = IS_OPEN
+            self.time_to_close = self.auto_close_interval
 
 class Register:
 
diff --git a/main.py b/main.py
index ee04369..92fc8a4 100644
--- a/main.py
+++ b/main.py
@@ -111,7 +111,7 @@ if __name__ == '__main__':
                     kwargs = dict(ent_coef=0.01)
                 elif modeL_type.__name__ in ["RegDQN", "DQN", "QRDQN"]:
                     kwargs = dict(buffer_size=50000,
-                                  learning_starts=25000,
+                                  learning_starts=64,
                                   batch_size=64,
                                   target_update_interval=5000,
                                   exploration_fraction=0.25,
diff --git a/reload_agent.py b/reload_agent.py
index 58f3cce..faffb44 100644
--- a/reload_agent.py
+++ b/reload_agent.py
@@ -14,14 +14,14 @@ warnings.filterwarnings('ignore', category=UserWarning)
 
 if __name__ == '__main__':
 
-    model_name = 'PPO_1623052687'
+    model_name = 'A2C_1623923982'
     run_id = 0
     out_path = Path(__file__).parent / 'debug_out'
     model_path = out_path / model_name
 
     with (model_path / f'env_{model_name}.yaml').open('r') as f:
         env_kwargs = yaml.load(f, Loader=yaml.FullLoader)
-    with SimpleFactory(level_name='rooms', **env_kwargs) as env:
+    with SimpleFactory(**env_kwargs) as env:
 
         # Edit THIS:
         model_files = list(natsorted((model_path / f'{run_id}_{model_name}').rglob('model_*.zip')))
@@ -30,5 +30,3 @@ if __name__ == '__main__':
         model = PPO.load(this_model)
         evaluation_result = evaluate_policy(model, env, n_eval_episodes=100, deterministic=False, render=True)
         print(evaluation_result)
-
-