From 061586d2a07f7d2af92ed3569eb6812803523518 Mon Sep 17 00:00:00 2001 From: kywch Date: Wed, 14 Jun 2023 10:35:45 +0000 Subject: [PATCH 1/5] return dummy obs and reward for the dead agents --- nmmo/core/env.py | 123 +++++++++++++++------------------- nmmo/core/observation.py | 48 ++++++++----- tests/core/test_env.py | 29 +++++--- tests/task/test_predicates.py | 2 - tests/testhelpers.py | 2 +- 5 files changed, 106 insertions(+), 98 deletions(-) diff --git a/nmmo/core/env.py b/nmmo/core/env.py index 84370431..16d091bc 100644 --- a/nmmo/core/env.py +++ b/nmmo/core/env.py @@ -37,6 +37,7 @@ def __init__(self, self.possible_agents = list(range(1, config.PLAYER_N + 1)) self._dead_agents = set() self._episode_stats = defaultdict(lambda: defaultdict(float)) + self._dead_this_tick = None self.scripted_agents = OrderedSet() self._gamestate_generator = GameStateGenerator(self.realm, self.config) @@ -149,6 +150,7 @@ def reset(self, map_id=None, seed=None, options=None, self.realm.reset(map_id) self._dead_agents = set() self._episode_stats.clear() + self._dead_this_tick = {} # check if there are scripted agents for eid, ent in self.realm.players.items(): @@ -278,20 +280,20 @@ def step(self, actions: Dict[int, Dict[str, Dict[str, Any]]]): # we don't need _deserialize_scripted_actions() anymore actions = self._validate_actions(actions) # Execute actions - self.realm.step(actions) + self._dead_this_tick = self.realm.step(actions) dones = {} for eid in self.possible_agents: - if eid not in self.realm.players or self.realm.tick >= self.config.HORIZON: - if eid not in self._dead_agents: - self._dead_agents.add(eid) - self._episode_stats[eid]["death_tick"] = self.realm.tick - dones[eid] = True + if eid in self._dead_this_tick or self.realm.tick >= self.config.HORIZON: + self._dead_agents.add(eid) + self._episode_stats[eid]["death_tick"] = self.realm.tick + dones[eid] = True - # Store the observations, since actions reference them + # Generate obs for each agent in self.agents self.obs = self._compute_observations() gym_obs = {a: o.to_gym() for a,o in self.obs.items()} - rewards, infos = self._compute_rewards(self.obs.keys(), dones) + # Generate rewards, infos for each agent in self.agents + rewards, infos = self._compute_rewards() for k,r in rewards.items(): self._episode_stats[k]['reward'] += r @@ -357,62 +359,47 @@ def _compute_scripted_agent_actions(self, actions: Dict[int, Dict[str, Dict[str, return actions def _compute_observations(self): - '''Neural MMO Observation API - - Args: - agents: List of agents to return observations for. If None, returns - observations for all agents - - Returns: - obs: Dictionary of observations for each agent - obs[agent_id] = { - "Entity": [e1, e2, ...], - "Task": [encoded_task], - "Tile": [t1, t2, ...], - "Inventory": [i1, i2, ...], - "Market": [m1, m2, ...], - "ActionTargets": { - "Attack": [a1, a2, ...], - "Sell": [s1, s2, ...], - "Buy": [b1, b2, ...], - "Move": [m1, m2, ...], - } - ''' - + '''Create an Observation object for each agent in self.agents''' obs = {} + market = Item.Query.for_sale(self.realm.datastore) # the same for all agents - market = Item.Query.for_sale(self.realm.datastore) + # dummy obs + dummy_tiles = np.zeros((1, len(Tile.State.attr_name_to_col))) + dummy_entities = np.zeros((1, len(Entity.State.attr_name_to_col))) + dummy_inventory = np.zeros((1, len(Item.State.attr_name_to_col))) + dummy_market = np.zeros((1, len(Item.State.attr_name_to_col))) for agent_id in self.agents: - agent = self.realm.players.get(agent_id) - agent_r = agent.row.val - agent_c = agent.col.val - - visible_entities = Entity.Query.window( - self.realm.datastore, - agent_r, agent_c, - self.config.PLAYER_VISION_RADIUS - ) - visible_tiles = Tile.Query.window( - self.realm.datastore, - agent_r, agent_c, - self.config.PLAYER_VISION_RADIUS) - - inventory = Item.Query.owned_by(self.realm.datastore, agent_id) - - # NOTE: the tasks for each agent is in self.agent_task_map, and task embeddings are - # available in each task instance, via task.embedding - # CHECK ME: do we pass in self.agent_task_map[agent_id], - # so that we can include task embedding in the obs? - obs[agent_id] = Observation(self.config, - self.realm.tick, - agent_id, - visible_tiles, - visible_entities, - inventory, market) + if agent_id not in self.realm.players: + # return dummy obs for the agents in dead_this_tick + obs[agent_id] = Observation(self.config, self.realm.tick, agent_id, + dummy_tiles, dummy_entities, dummy_inventory, dummy_market) + else: + agent = self.realm.players.get(agent_id) + agent_r = agent.row.val + agent_c = agent.col.val + + visible_entities = Entity.Query.window( + self.realm.datastore, + agent_r, agent_c, + self.config.PLAYER_VISION_RADIUS + ) + visible_tiles = Tile.Query.window( + self.realm.datastore, + agent_r, agent_c, + self.config.PLAYER_VISION_RADIUS) + + inventory = Item.Query.owned_by(self.realm.datastore, agent_id) + + # NOTE: the tasks for each agent is in self.agent_task_map, and task embeddings are + # available in each task instance, via task.embedding + # CHECK ME: do we want to pass in self.agent_task_map[agent_id], + # so that we can include task embedding in the obs? + obs[agent_id] = Observation(self.config, self.realm.tick, agent_id, + visible_tiles, visible_entities, inventory, market) return obs - def _compute_rewards(self, agents: List[AgentID], dones: Dict[AgentID, bool]): + def _compute_rewards(self): '''Computes the reward for the specified agent Override this method to create custom reward functions. You have full @@ -428,24 +415,22 @@ def _compute_rewards(self, agents: List[AgentID], dones: Dict[AgentID, bool]): entity identified by ent_id. ''' # Initialization - infos = {agent_id: {'task': {}} for agent_id in agents} + infos = {agent_id: {'task': {}} for agent_id in self.agents} rewards = defaultdict(int) - agents = set(agents) - reward_cache = {} # Compute Rewards and infos self.game_state = self._gamestate_generator.generate(self.realm, self.obs) for task in self.tasks: - if task in reward_cache: - task_rewards, task_infos = reward_cache[task] - else: - task_rewards, task_infos = task.compute_rewards(self.game_state) - reward_cache[task] = (task_rewards, task_infos) + task_rewards, task_infos = task.compute_rewards(self.game_state) for agent_id, reward in task_rewards.items(): - if agent_id in agents and agent_id not in dones: + if agent_id in self.agents: rewards[agent_id] = rewards.get(agent_id,0) + reward infos[agent_id]['task'][task.name] = task_infos[agent_id] # progress + # Make sure the dead agents return the rewards of -1 + for agent_id in self._dead_this_tick: + rewards[agent_id] = -1 + return rewards, infos ############################################################################ @@ -458,7 +443,9 @@ def render(self, mode='human'): @property def agents(self) -> List[AgentID]: '''For conformity with the PettingZoo API only; rendering is external''' - return list(set(self.realm.players.keys()) - self._dead_agents) + # "current" agents, which return obs: both alive and dead_this_tick + agents = set(list(self.realm.players.keys()) + list(self._dead_this_tick.keys())) + return list(agents) def close(self): '''For conformity with the PettingZoo API only; rendering is external''' diff --git a/nmmo/core/observation.py b/nmmo/core/observation.py index ad05a0b2..9967a168 100644 --- a/nmmo/core/observation.py +++ b/nmmo/core/observation.py @@ -55,7 +55,8 @@ def __init__(self, self.entities = BasicObs(entities[0:config.PLAYER_N_OBS], EntityState.State.attr_name_to_col["id"]) - if config.COMBAT_SYSTEM_ENABLED: + agent = self.agent() + if config.COMBAT_SYSTEM_ENABLED and agent is not None: latest_combat_tick = self.agent().latest_combat_tick self.agent_in_combat = False if latest_combat_tick == 0 else \ (current_tick - latest_combat_tick) < config.COMBAT_STATUS_DURATION @@ -112,31 +113,42 @@ def entity(self, entity_id): def agent(self): return self.entity(self.agent_id) - def to_gym(self): - '''Convert the observation to a format that can be used by OpenAI Gym''' - - tiles = np.zeros((self.config.MAP_N_OBS, self.tiles.shape[1])) - tiles[:self.tiles.shape[0],:] = self.tiles - - entities = np.zeros((self.config.PLAYER_N_OBS, self.entities.values.shape[1])) - entities[:self.entities.values.shape[0],:] = self.entities.values - + def get_empty_obs(self): gym_obs = { "CurrentTick": np.array([self.current_tick]), "AgentId": np.array([self.agent_id]), - "Tile": tiles, - "Entity": entities, + "Tile": np.zeros((self.config.MAP_N_OBS, self.tiles.shape[1])), + "Entity": np.zeros((self.config.PLAYER_N_OBS, self.entities.values.shape[1])), } if self.config.ITEM_SYSTEM_ENABLED: - inventory = np.zeros((self.config.INVENTORY_N_OBS, self.inventory.values.shape[1])) - inventory[:self.inventory.values.shape[0],:] = self.inventory.values - gym_obs["Inventory"] = inventory + gym_obs["Inventory"] = np.zeros((self.config.INVENTORY_N_OBS, + self.inventory.values.shape[1])) + + if self.config.EXCHANGE_SYSTEM_ENABLED: + gym_obs["Market"] = np.zeros((self.config.MARKET_N_OBS, + self.market.values.shape[1])) + + if self.config.PROVIDE_ACTION_TARGETS: + gym_obs["ActionTargets"] = None + + return gym_obs + + def to_gym(self): + '''Convert the observation to a format that can be used by OpenAI Gym''' + gym_obs = self.get_empty_obs() + if self.agent() is None: + # return empty obs for the dead agents + return gym_obs + + gym_obs['Tile'][:self.tiles.shape[0],:] = self.tiles + gym_obs['Entity'][:self.entities.values.shape[0],:] = self.entities.values + + if self.config.ITEM_SYSTEM_ENABLED: + gym_obs["Inventory"][:self.inventory.values.shape[0],:] = self.inventory.values if self.config.EXCHANGE_SYSTEM_ENABLED: - market = np.zeros((self.config.MARKET_N_OBS, self.market.values.shape[1])) - market[:self.market.values.shape[0],:] = self.market.values - gym_obs["Market"] = market + gym_obs["Market"][:self.market.values.shape[0],:] = self.market.values if self.config.PROVIDE_ACTION_TARGETS: gym_obs["ActionTargets"] = self._make_action_targets() diff --git a/tests/core/test_env.py b/tests/core/test_env.py index 1ddeb677..22f816a8 100644 --- a/tests/core/test_env.py +++ b/tests/core/test_env.py @@ -2,6 +2,7 @@ from typing import List import random +import numpy as np from tqdm import tqdm import nmmo @@ -51,19 +52,29 @@ def test_observations(self): ] for player_id, player_obs in obs.items(): - self._validate_tiles(player_obs, self.env.realm) - self._validate_entitites( - player_id, player_obs, self.env.realm, entity_locations) - self._validate_inventory(player_id, player_obs, self.env.realm) - self._validate_market(player_obs, self.env.realm) - obs, _, dones, _ = self.env.step({}) - - # make sure dead agents return proper dones=True - self.assertEqual(len(self.env.agents), len(self.env.realm.players)) + if player_id in self.env.realm.players: # alive agents + self._validate_tiles(player_obs, self.env.realm) + self._validate_entitites( + player_id, player_obs, self.env.realm, entity_locations) + self._validate_inventory(player_id, player_obs, self.env.realm) + self._validate_market(player_obs, self.env.realm) + else: + # the obs of dead agents are dummy, all zeros + self.assertEqual(np.sum(player_obs['Tile']), 0) + self.assertEqual(np.sum(player_obs['Entity']), 0) + self.assertEqual(np.sum(player_obs['Inventory']), 0) + self.assertEqual(np.sum(player_obs['Market']), 0) + + obs, rewards, dones, _ = self.env.step({}) + + # make sure dead agents return proper dones=True, dummy obs, and -1 reward + self.assertEqual(len(self.env.agents), + len(self.env.realm.players) + len(self.env._dead_this_tick)) self.assertEqual(len(self.env.possible_agents), len(self.env.realm.players) + len(self.env._dead_agents)) if len(self.env._dead_agents) > len(dead_agents): for dead_id in self.env._dead_agents - dead_agents: + self.assertEqual(rewards[dead_id], -1) self.assertTrue(dones[dead_id]) dead_agents.add(dead_id) diff --git a/tests/task/test_predicates.py b/tests/task/test_predicates.py index f2f61f0e..695716f7 100644 --- a/tests/task/test_predicates.py +++ b/tests/task/test_predicates.py @@ -126,8 +126,6 @@ def test_tickge_stay_alive_rip(self): # make sure that dead players not in the realm nor the datastore self.assertTrue(ent_id not in env.realm.players) self.assertTrue(ent_id not in entities) - # CHECK ME: dead agents are also not in infos - self.assertTrue(ent_id not in infos) # TickGE_5 is true. Agents 1-3 are dead, so # StayAlive(1,3) and StayAlive(3,4) are false, StayAlive(4) is true diff --git a/tests/testhelpers.py b/tests/testhelpers.py index 678cedaf..68ecff4a 100644 --- a/tests/testhelpers.py +++ b/tests/testhelpers.py @@ -384,7 +384,7 @@ def profile_env_step(action_target=True, tasks=None, condition=None): ('env.realm.step():', lambda: env.realm.step({})), ('env._compute_observations():', lambda: env._compute_observations()), ('obs.to_gym(), ActionTarget:', lambda: {a: o.to_gym() for a,o in obs.items()}), - ('env._compute_rewards():', lambda: env._compute_rewards(obs.keys(), {})) + ('env._compute_rewards():', lambda: env._compute_rewards()) ] if condition: From 04e3bc63bf53082aedda457395cfd4ccd3de5dc4 Mon Sep 17 00:00:00 2001 From: kywch Date: Wed, 14 Jun 2023 12:21:44 +0000 Subject: [PATCH 2/5] env returns obs, rewards, dones, infos for all env.agents --- nmmo/core/env.py | 19 +++++++++++-------- tests/core/test_env.py | 7 ++++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/nmmo/core/env.py b/nmmo/core/env.py index 16d091bc..f125d879 100644 --- a/nmmo/core/env.py +++ b/nmmo/core/env.py @@ -282,11 +282,13 @@ def step(self, actions: Dict[int, Dict[str, Dict[str, Any]]]): # Execute actions self._dead_this_tick = self.realm.step(actions) dones = {} - for eid in self.possible_agents: - if eid in self._dead_this_tick or self.realm.tick >= self.config.HORIZON: - self._dead_agents.add(eid) - self._episode_stats[eid]["death_tick"] = self.realm.tick - dones[eid] = True + for agent_id in self.agents: + if agent_id in self._dead_this_tick or self.realm.tick >= self.config.HORIZON: + self._dead_agents.add(agent_id) + self._episode_stats[agent_id]["death_tick"] = self.realm.tick + dones[agent_id] = True + else: + dones[agent_id] = False # Generate obs for each agent in self.agents self.obs = self._compute_observations() @@ -297,14 +299,14 @@ def step(self, actions: Dict[int, Dict[str, Dict[str, Any]]]): for k,r in rewards.items(): self._episode_stats[k]['reward'] += r - # When the episode ends, add the episode stats to the info of one of - # the last dagents + # When the episode ends, add the episode stats to the info of the last agents if len(self._dead_agents) == len(self.possible_agents): for agent_id, stats in self._episode_stats.items(): if agent_id not in infos: infos[agent_id] = {} infos[agent_id]["episode_stats"] = stats + # NOTE: all obs, rewards, dones, infos have data for each agent in self.agents return gym_obs, rewards, dones, infos def _validate_actions(self, actions: Dict[int, Dict[str, Dict[str, Any]]]): @@ -417,13 +419,14 @@ def _compute_rewards(self): # Initialization infos = {agent_id: {'task': {}} for agent_id in self.agents} rewards = defaultdict(int) + agents = set(self.agents) # Compute Rewards and infos self.game_state = self._gamestate_generator.generate(self.realm, self.obs) for task in self.tasks: task_rewards, task_infos = task.compute_rewards(self.game_state) for agent_id, reward in task_rewards.items(): - if agent_id in self.agents: + if agent_id in agents: rewards[agent_id] = rewards.get(agent_id,0) + reward infos[agent_id]['task'][task.name] = task_infos[agent_id] # progress diff --git a/tests/core/test_env.py b/tests/core/test_env.py index 22f816a8..18bd7471 100644 --- a/tests/core/test_env.py +++ b/tests/core/test_env.py @@ -65,13 +65,18 @@ def test_observations(self): self.assertEqual(np.sum(player_obs['Inventory']), 0) self.assertEqual(np.sum(player_obs['Market']), 0) - obs, rewards, dones, _ = self.env.step({}) + obs, rewards, dones, infos = self.env.step({}) # make sure dead agents return proper dones=True, dummy obs, and -1 reward self.assertEqual(len(self.env.agents), len(self.env.realm.players) + len(self.env._dead_this_tick)) self.assertEqual(len(self.env.possible_agents), len(self.env.realm.players) + len(self.env._dead_agents)) + for agent_id in self.env.agents: + self.assertTrue(agent_id in obs) + self.assertTrue(agent_id in rewards) + self.assertTrue(agent_id in dones) + self.assertTrue(agent_id in infos) if len(self.env._dead_agents) > len(dead_agents): for dead_id in self.env._dead_agents - dead_agents: self.assertEqual(rewards[dead_id], -1) From 1cb6038f66a3e064fdaa8fb285f76bd7a88f6dd4 Mon Sep 17 00:00:00 2001 From: kywch Date: Wed, 14 Jun 2023 13:21:41 +0000 Subject: [PATCH 3/5] quick fixes --- nmmo/core/env.py | 4 ++-- nmmo/task/game_state.py | 8 +++++--- tests/core/test_env.py | 8 ++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/nmmo/core/env.py b/nmmo/core/env.py index f125d879..9f679346 100644 --- a/nmmo/core/env.py +++ b/nmmo/core/env.py @@ -417,9 +417,9 @@ def _compute_rewards(self): entity identified by ent_id. ''' # Initialization - infos = {agent_id: {'task': {}} for agent_id in self.agents} - rewards = defaultdict(int) agents = set(self.agents) + infos = {agent_id: {'task': {}} for agent_id in agents} + rewards = defaultdict(int) # Compute Rewards and infos self.game_state = self._gamestate_generator.generate(self.realm, self.obs) diff --git a/nmmo/task/game_state.py b/nmmo/task/game_state.py index fb57ba4c..305c3179 100644 --- a/nmmo/task/game_state.py +++ b/nmmo/task/game_state.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Dict, List, Tuple, MutableMapping +from typing import Dict, List, Tuple, MutableMapping, Set from dataclasses import dataclass from copy import deepcopy from abc import ABC, abstractmethod @@ -31,7 +31,7 @@ class GameState: config: Config spawn_pos: Dict[int, Tuple[int, int]] # ent_id: (row, col) of all spawned agents - alive_agents: List[int] # of alive agents' ent_id (for convenience) + alive_agents: Set[int] # of alive agents' ent_id (for convenience) env_obs: Dict[int, Observation] # env passes the obs of only alive agents entity_data: np.ndarray # a copied, whole Entity ds table @@ -192,12 +192,14 @@ def __init__(self, realm: Realm, config: Config): def generate(self, realm: Realm, env_obs: Dict[int, Observation]) -> GameState: # copy the datastore, by running astype entity_all = EntityState.Query.table(realm.datastore).astype(np.int16) + alive_agents = entity_all[:, EntityAttr["id"]] + alive_agents = set(alive_agents[alive_agents > 0]) return GameState( current_tick = realm.tick, config = self.config, spawn_pos = self.spawn_pos, - alive_agents = list(entity_all[:, EntityAttr["id"]]), + alive_agents = alive_agents, env_obs = env_obs, entity_data = entity_all, item_data = ItemState.Query.table(realm.datastore).astype(np.int16), diff --git a/tests/core/test_env.py b/tests/core/test_env.py index 18bd7471..244bfc05 100644 --- a/tests/core/test_env.py +++ b/tests/core/test_env.py @@ -83,6 +83,14 @@ def test_observations(self): self.assertTrue(dones[dead_id]) dead_agents.add(dead_id) + # check dead and alive + entity_all = EntityState.Query.table(self.env.realm.datastore).astype(np.int16) + alive_agents = entity_all[:, Entity.State.attr_name_to_col["id"]] + alive_agents = set(alive_agents[alive_agents > 0]) + for agent_id in alive_agents: + self.assertTrue(agent_id in self.env.realm.players) + self.assertTrue(agent_id not in self.env._dead_agents) + def _validate_tiles(self, obs, realm: Realm): for tile_obs in obs["Tile"]: tile_obs = TileState.parse_array(tile_obs) From 222c79510e84437f1d1725bddcae004a4c49b673 Mon Sep 17 00:00:00 2001 From: kywch Date: Wed, 14 Jun 2023 13:59:28 +0000 Subject: [PATCH 4/5] made dummy obs zero length --- nmmo/core/env.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nmmo/core/env.py b/nmmo/core/env.py index 9f679346..4c65708d 100644 --- a/nmmo/core/env.py +++ b/nmmo/core/env.py @@ -366,10 +366,10 @@ def _compute_observations(self): market = Item.Query.for_sale(self.realm.datastore) # the same for all agents # dummy obs - dummy_tiles = np.zeros((1, len(Tile.State.attr_name_to_col))) - dummy_entities = np.zeros((1, len(Entity.State.attr_name_to_col))) - dummy_inventory = np.zeros((1, len(Item.State.attr_name_to_col))) - dummy_market = np.zeros((1, len(Item.State.attr_name_to_col))) + dummy_tiles = np.zeros((0, len(Tile.State.attr_name_to_col))) + dummy_entities = np.zeros((0, len(Entity.State.attr_name_to_col))) + dummy_inventory = np.zeros((0, len(Item.State.attr_name_to_col))) + dummy_market = np.zeros((0, len(Item.State.attr_name_to_col))) for agent_id in self.agents: if agent_id not in self.realm.players: From 7a35c250af89b63eb8697383773f2c4817e1f92e Mon Sep 17 00:00:00 2001 From: kywch Date: Wed, 14 Jun 2023 21:04:53 +0000 Subject: [PATCH 5/5] make dummy obs at reset and use it --- nmmo/core/env.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/nmmo/core/env.py b/nmmo/core/env.py index 4c65708d..f678a7fe 100644 --- a/nmmo/core/env.py +++ b/nmmo/core/env.py @@ -2,6 +2,7 @@ import random from typing import Any, Dict, List, Callable from collections import defaultdict +from copy import copy from ordered_set import OrderedSet import gym @@ -33,6 +34,7 @@ def __init__(self, self.config = config self.realm = realm.Realm(config) self.obs = None + self._dummy_obs = None self.possible_agents = list(range(1, config.PLAYER_N + 1)) self._dead_agents = set() @@ -157,6 +159,7 @@ def reset(self, map_id=None, seed=None, options=None, if isinstance(ent.agent, Scripted): self.scripted_agents.add(eid) + self._dummy_obs = self._make_dummy_obs() self.obs = self._compute_observations() self._gamestate_generator = GameStateGenerator(self.realm, self.config) @@ -360,22 +363,26 @@ def _compute_scripted_agent_actions(self, actions: Dict[int, Dict[str, Dict[str, return actions - def _compute_observations(self): - '''Create an Observation object for each agent in self.agents''' - obs = {} - market = Item.Query.for_sale(self.realm.datastore) # the same for all agents - - # dummy obs + def _make_dummy_obs(self): dummy_tiles = np.zeros((0, len(Tile.State.attr_name_to_col))) dummy_entities = np.zeros((0, len(Entity.State.attr_name_to_col))) dummy_inventory = np.zeros((0, len(Item.State.attr_name_to_col))) dummy_market = np.zeros((0, len(Item.State.attr_name_to_col))) + return Observation(self.config, self.realm.tick, 0, + dummy_tiles, dummy_entities, dummy_inventory, dummy_market) + + def _compute_observations(self): + '''Create an Observation object for each agent in self.agents''' + obs = {} + market = Item.Query.for_sale(self.realm.datastore) # the same for all agents for agent_id in self.agents: if agent_id not in self.realm.players: # return dummy obs for the agents in dead_this_tick - obs[agent_id] = Observation(self.config, self.realm.tick, agent_id, - dummy_tiles, dummy_entities, dummy_inventory, dummy_market) + dummy_obs = copy(self._dummy_obs) + dummy_obs.current_tick = self.realm.tick + dummy_obs.agent_id = agent_id + obs[agent_id] = dummy_obs else: agent = self.realm.players.get(agent_id) agent_r = agent.row.val