diff --git a/nmmo/core/agent.py b/nmmo/core/agent.py index 8d27a08b2..04fdd5500 100644 --- a/nmmo/core/agent.py +++ b/nmmo/core/agent.py @@ -1,13 +1,7 @@ -from nmmo.lib import colors - - class Agent: policy = 'Neural' - color = colors.Neon.CYAN - pop = 0 - def __init__(self, config, idx): '''Base class for agents @@ -17,7 +11,6 @@ def __init__(self, config, idx): ''' self.config = config self.iden = idx - self.pop = Agent.pop def __call__(self, obs): '''Used by scripted agents to compute actions. Override in subclasses. diff --git a/nmmo/core/config.py b/nmmo/core/config.py index 85816aa90..fb51a5eb7 100644 --- a/nmmo/core/config.py +++ b/nmmo/core/config.py @@ -388,9 +388,6 @@ class Combat: COMBAT_SYSTEM_ENABLED = True '''Game system flag''' - COMBAT_FRIENDLY_FIRE = True - '''Whether agents with the same population index can hit each other''' - COMBAT_SPAWN_IMMUNITY = 20 '''Agents older than this many ticks cannot attack agents younger than this many ticks''' @@ -518,8 +515,8 @@ class Item: ITEM_INVENTORY_CAPACITY = 12 '''Number of inventory spaces''' - ITEM_GIVE_TO_FRIENDLY = True - '''Whether agents with the same population index can give gold/item to each other''' + ITEM_ALLOW_GIFT = True + '''Whether agents can give gold/item to each other''' @property def INVENTORY_N_OBS(self): diff --git a/nmmo/core/env.py b/nmmo/core/env.py index 8e11c2bd7..89ca7370e 100644 --- a/nmmo/core/env.py +++ b/nmmo/core/env.py @@ -18,11 +18,7 @@ class Env(ParallelEnv): - '''Environment wrapper for Neural MMO using the Parallel PettingZoo API - - Neural MMO provides complex environments featuring structured observations/actions, - variably sized agent populations, and long time horizons. Usage in conjunction - with RLlib as demonstrated in the /projekt wrapper is highly recommended.''' + # Environment wrapper for Neural MMO using the Parallel PettingZoo API def __init__(self, config: Default = nmmo.config.Default(), seed=None): @@ -390,14 +386,9 @@ def _compute_rewards(self, agents: List[AgentID], dones: Dict[AgentID, bool]): agent = self.realm.players.get(agent_id) assert agent is not None, f'Agent {agent_id} not found' - infos[agent_id] = {'population': agent.population} - - if agent.diary is None: - rewards[agent_id] = 0 - continue - - rewards[agent_id] = sum(agent.diary.rewards.values()) - infos[agent_id].update(agent.diary.rewards) + if agent.diary is not None: + rewards[agent_id] = sum(agent.diary.rewards.values()) + infos[agent_id].update(agent.diary.rewards) return rewards, infos diff --git a/nmmo/core/log_helper.py b/nmmo/core/log_helper.py index 7322fb01b..bb118f049 100644 --- a/nmmo/core/log_helper.py +++ b/nmmo/core/log_helper.py @@ -146,7 +146,4 @@ def _player_stats(self, player: Agent) -> Dict[str, float]: for achievement in player.diary.achievements: stats["Achievement_{achievement.name}"] = float(achievement.completed) - # Used for SR - stats['PolicyID'] = player.population - return stats diff --git a/nmmo/core/observation.py b/nmmo/core/observation.py index 412c03fab..86385b4fc 100644 --- a/nmmo/core/observation.py +++ b/nmmo/core/observation.py @@ -209,15 +209,11 @@ def _make_attack_mask(self): else: spawn_immunity = np.ones(self.entities.len, dtype=np.int8) - if not self.config.COMBAT_FRIENDLY_FIRE: - population = self.entities.values[:,EntityState.State.attr_name_to_col["population_id"]] - no_friendly_fire = population != agent.population_id # this automatically masks self - else: - # allow friendly fire but no self shooting - no_friendly_fire = np.ones(self.entities.len, dtype=np.int8) - no_friendly_fire[self.entities.index(agent.id)] = 0 # mask self + # allow friendly fire but no self shooting + not_me = np.ones(self.entities.len, dtype=np.int8) + not_me[self.entities.index(agent.id)] = 0 # mask self - return np.concatenate([within_range & no_friendly_fire & spawn_immunity, + return np.concatenate([within_range & not_me & spawn_immunity, np.zeros(self.config.PLAYER_N_OBS - self.entities.len, dtype=np.int8)]) def _make_use_mask(self): @@ -287,10 +283,11 @@ def _make_give_target_mask(self): entities_pos = self.entities.values[:, [EntityState.State.attr_name_to_col["row"], EntityState.State.attr_name_to_col["col"]]] same_tile = utils.linf(entities_pos, (agent.row, agent.col)) == 0 - same_team_not_me = (self.entities.ids != agent.id) & (agent.population_id == \ - self.entities.values[:, EntityState.State.attr_name_to_col["population_id"]]) + not_me = self.entities.ids != self.agent_id + player = (self.entities.values[:,EntityState.State.attr_name_to_col["npc_type"]] == 0) - return np.concatenate([same_tile & same_team_not_me, + return np.concatenate([ + (same_tile & player & not_me), np.zeros(self.config.PLAYER_N_OBS - self.entities.len, dtype=np.int8)]) def _make_give_gold_mask(self): diff --git a/nmmo/core/realm.py b/nmmo/core/realm.py index e77b89401..038f3d75c 100644 --- a/nmmo/core/realm.py +++ b/nmmo/core/realm.py @@ -119,7 +119,7 @@ def packet(self): } @property - def population(self): + def num_players(self): """Number of player agents""" return len(self.players.entities) diff --git a/nmmo/entity/entity.py b/nmmo/entity/entity.py index a081f2d92..0d7f9a410 100644 --- a/nmmo/entity/entity.py +++ b/nmmo/entity/entity.py @@ -14,7 +14,7 @@ EntityState = SerializedState.subclass( "Entity", [ "id", - "population_id", + "npc_type", # 1 - passive, 2 - neutral, 3 - aggressive "row", "col", @@ -48,7 +48,7 @@ EntityState.Limits = lambda config: { **{ "id": (-math.inf, math.inf), - "population_id": (-3, config.PLAYER_POLICIES-1), + "npc_type": (0, 4), "row": (0, config.MAP_SIZE-1), "col": (0, config.MAP_SIZE-1), "damage": (0, math.inf), @@ -211,7 +211,7 @@ def packet(self): # pylint: disable=no-member class Entity(EntityState): - def __init__(self, realm, pos, entity_id, name, color, population_id): + def __init__(self, realm, pos, entity_id, name): super().__init__(realm.datastore, EntityState.Limits(realm.config)) self.realm = realm @@ -222,11 +222,9 @@ def __init__(self, realm, pos, entity_id, name, color, population_id): self.repr = None self.name = name + str(entity_id) - self.color = color self.row.update(pos[0]) self.col.update(pos[1]) - self.population_id.update(population_id) self.id.update(entity_id) self.vision = self.config.PLAYER_VISION_RADIUS @@ -259,10 +257,6 @@ def packet(self): 'name': self.name, 'level': self.attack_level, 'item_level': self.item_level.val, - 'color': self.color.packet(), - 'population': self.population, - # FIXME: Don't know what it does. Previous nmmo entities all returned 1 - # 'self': self.self.val, } return data @@ -338,7 +332,3 @@ def attack_level(self) -> int: mage = self.skills.mage.level.val return int(max(melee, ranged, mage)) - - @property - def population(self): - return self.population_id.val diff --git a/nmmo/entity/entity_manager.py b/nmmo/entity/entity_manager.py index f36ade22c..6dc2dc2ac 100644 --- a/nmmo/entity/entity_manager.py +++ b/nmmo/entity/entity_manager.py @@ -7,7 +7,7 @@ from nmmo.entity.entity import Entity from nmmo.entity.npc import NPC from nmmo.entity.player import Player -from nmmo.lib import colors, spawn +from nmmo.lib import spawn from nmmo.systems import combat @@ -126,7 +126,6 @@ def actions(self, realm): class PlayerManager(EntityGroup): def __init__(self, realm): super().__init__(realm) - self.palette = colors.Palette() self.loader = self.realm.config.PLAYER_LOADER self.agents = None self.spawned = None @@ -137,9 +136,9 @@ def reset(self): self.spawned = OrderedSet() def spawn_individual(self, r, c, idx): - pop, agent = next(self.agents) + agent = next(self.agents) agent = agent(self.config, idx) - player = Player(self.realm, (r, c), agent, self.palette.color(pop), pop) + player = Player(self.realm, (r, c), agent) super().spawn(player) def spawn(self): diff --git a/nmmo/entity/npc.py b/nmmo/entity/npc.py index 01098ae21..c593b64f7 100644 --- a/nmmo/entity/npc.py +++ b/nmmo/entity/npc.py @@ -3,7 +3,6 @@ from nmmo.entity import entity from nmmo.io import action as Action -from nmmo.lib.colors import Neon from nmmo.systems import combat, droptable from nmmo.systems.ai import policy from nmmo.systems import item as Item @@ -46,15 +45,17 @@ def packet(self): return packet +# pylint: disable=no-member class NPC(entity.Entity): - def __init__(self, realm, pos, iden, name, color, pop): - super().__init__(realm, pos, iden, name, color, pop) + def __init__(self, realm, pos, iden, name, npc_type): + super().__init__(realm, pos, iden, name) self.skills = skill.Combat(realm, self) self.realm = realm self.last_action = None self.droptable = None self.spawn_danger = None self.equipment = None + self.npc_type.update(npc_type) def update(self, realm, actions): super().update(realm, actions) @@ -161,21 +162,21 @@ def is_npc(self) -> bool: class Passive(NPC): def __init__(self, realm, pos, iden): - super().__init__(realm, pos, iden, 'Passive', Neon.GREEN, -1) + super().__init__(realm, pos, iden, 'Passive', 1) def decide(self, realm): return policy.passive(realm, self) class PassiveAggressive(NPC): def __init__(self, realm, pos, iden): - super().__init__(realm, pos, iden, 'Neutral', Neon.ORANGE, -2) + super().__init__(realm, pos, iden, 'Neutral', 2) def decide(self, realm): return policy.neutral(realm, self) class Aggressive(NPC): def __init__(self, realm, pos, iden): - super().__init__(realm, pos, iden, 'Hostile', Neon.RED, -3) + super().__init__(realm, pos, iden, 'Hostile', 3) def decide(self, realm): return policy.hostile(realm, self) diff --git a/nmmo/entity/player.py b/nmmo/entity/player.py index ae56826bc..cdef4f878 100644 --- a/nmmo/entity/player.py +++ b/nmmo/entity/player.py @@ -4,11 +4,10 @@ # pylint: disable=no-member class Player(entity.Entity): - def __init__(self, realm, pos, agent, color, pop): - super().__init__(realm, pos, agent.iden, agent.policy, color, pop) + def __init__(self, realm, pos, agent): + super().__init__(realm, pos, agent.iden, agent.policy) self.agent = agent - self.pop = pop self.immortal = realm.config.IMMORTAL # Scripted hooks @@ -37,7 +36,7 @@ def __init__(self, realm, pos, agent, color, pop): @property def serial(self): - return self.population_id, self.ent_id + return self.ent_id @property def is_player(self) -> bool: @@ -92,7 +91,6 @@ def packet(self): data = super().packet() data['entID'] = self.ent_id - data['annID'] = self.population data['resource'] = self.resources.packet() data['skills'] = self.skills.packet() diff --git a/nmmo/io/action.py b/nmmo/io/action.py index d08049fbf..a8799e0e9 100644 --- a/nmmo/io/action.py +++ b/nmmo/io/action.py @@ -258,11 +258,6 @@ def call(realm, entity, style, target): if entity.ent_id == target.ent_id: return None - #ADDED: POPULATION IMMUNITY - if not config.COMBAT_FRIENDLY_FIRE and entity.is_player \ - and entity.population_id.val == target.population_id.val: - return None - #Can't attack out of range if utils.linf(entity.pos, target.pos) > style.attack_range(config): return None @@ -458,10 +453,10 @@ def call(realm, entity, item, target): if item.equipped.val or item.listed_price.val: return - if not (config.ITEM_GIVE_TO_FRIENDLY and - entity.population_id == target.population_id and # the same team + if not (config.ITEM_ALLOW_GIFT and entity.ent_id != target.ent_id and # but not self - utils.linf(entity.pos, target.pos) == 0): # the same tile + target.is_player and + entity.pos == target.pos): # the same tile return if not target.inventory.space: @@ -504,10 +499,10 @@ def call(realm, entity, amount, target): if not (target.is_player and target.alive): return - if not (config.ITEM_GIVE_TO_FRIENDLY and - entity.population_id == target.population_id and # the same team + if not (config.ITEM_ALLOW_GIFT and entity.ent_id != target.ent_id and # but not self - utils.linf(entity.pos, target.pos) == 0): # the same tile + target.is_player and + entity.pos == target.pos): # the same tile return if not isinstance(amount, int): diff --git a/nmmo/lib/spawn.py b/nmmo/lib/spawn.py index c80aae4d3..e4eb5d6b1 100644 --- a/nmmo/lib/spawn.py +++ b/nmmo/lib/spawn.py @@ -4,8 +4,6 @@ class SequentialLoader: '''config.PLAYER_LOADER that spreads out agent populations''' def __init__(self, config): items = config.PLAYERS - for idx, itm in enumerate(items): - itm.policyID = idx self.items = items self.idx = -1 @@ -15,28 +13,7 @@ def __iter__(self): def __next__(self): self.idx = (self.idx + 1) % len(self.items) - return self.idx, self.items[self.idx] - -class TeamLoader: - '''config.PLAYER_LOADER that loads agent populations adjacent''' - def __init__(self, config): - items = config.PLAYERS - self.team_size = config.PLAYER_N // len(items) - - for idx, itm in enumerate(items): - itm.policyID = idx - - self.items = items - self.idx = -1 - - def __iter__(self): - return self - - def __next__(self): - self.idx += 1 - team_idx = self.idx // self.team_size - return team_idx, self.items[team_idx] - + return self.items[self.idx] def spawn_continuous(config): '''Generates spawn positions for new agents diff --git a/nmmo/overlay.py b/nmmo/overlay.py index a03c1ed09..e032887c9 100644 --- a/nmmo/overlay.py +++ b/nmmo/overlay.py @@ -133,9 +133,8 @@ def update(self, obs): '''Computes a count-based exploration map by painting tiles as agents walk over them''' for ent_id, agent in self.realm.realm.players.items(): - pop = agent.population_id.val r, c = agent.pos - self.values[r, c][pop] += 1 + self.values[r, c][ent_id] += 1 def register(self, obs): colors = self.realm.realm.players.palette.colors diff --git a/scripted/baselines.py b/scripted/baselines.py index 8d12dcc02..3a4397b32 100644 --- a/scripted/baselines.py +++ b/scripted/baselines.py @@ -79,17 +79,17 @@ def attack(self): def target_weak(self): '''Target the nearest agent if it is weak''' - if self.closest is None: - return False + # disabled for now + # if self.closest is None: + # return False - selfLevel = self.me.level - targLevel = max(self.closest.melee_level, self.closest.range_level, self.closest.mage_level) - population = self.closest.population_id + # selfLevel = self.me.level + # targLevel = max(self.closest.melee_level, self.closest.range_level, self.closest.mage_level) - if population == -1 or targLevel <= selfLevel <= 5 or selfLevel >= targLevel + 3: - self.target = self.closest - self.targetID = self.closestID - self.targetDist = self.closestDist + # if population == -1 or targLevel <= selfLevel <= 5 or selfLevel >= targLevel + 3: + # self.target = self.closest + # self.targetID = self.closestID + # self.targetDist = self.closestDist def scan_agents(self): '''Scan the nearby area for agents''' diff --git a/scripts/git-pr.sh b/scripts/git-pr.sh index e414f3f26..aa7584c5f 100755 --- a/scripts/git-pr.sh +++ b/scripts/git-pr.sh @@ -29,6 +29,10 @@ git merge origin/$MASTER_BRANCH PRE_GIT_CHECK=$(find . -name pre-git-check.sh) if test -f "$PRE_GIT_CHECK"; then $PRE_GIT_CHECK + if [ $? -ne 0 ]; then + echo "pre-git-check.sh failed. Exiting." + exit 1 + fi else echo "Missing pre-git-check.sh. Exiting." exit 1 diff --git a/tests/action/test_destroy_give_gold.py b/tests/action/test_destroy_give_gold.py index 1eb8f6fd4..b21da3f97 100644 --- a/tests/action/test_destroy_give_gold.py +++ b/tests/action/test_destroy_give_gold.py @@ -88,7 +88,7 @@ def test_destroy(self): # DONE - def test_give_team_tile_npc(self): + def test_give_tile_npc(self): # cannot give to self (should be masked) # cannot give if not on the same tile (should be masked) # cannot give to the other team member (should be masked) @@ -109,11 +109,8 @@ def test_give_team_tile_npc(self): # agent 2: give ammo to agent 2 (invalid: cannot give to self) test_cond[2] = { 'tgt_id': 2, 'item_sig': self.item_sig[2][0], 'ent_mask': False, 'inv_mask': True, 'valid': False } - # agent 3: give ammo to agent 6 (invalid: the same tile but other team) - test_cond[3] = { 'tgt_id': 6, 'item_sig': self.item_sig[3][0], - 'ent_mask': False, 'inv_mask': True, 'valid': False } - # agent 4: give ammo to agent 5 (invalid: the same team but other tile) - test_cond[4] = { 'tgt_id': 5, 'item_sig': self.item_sig[4][0], + # agent 4: give ammo to agent 5 (invalid: other tile) + test_cond[4] = { 'tgt_id': 6, 'item_sig': self.item_sig[4][0], 'ent_mask': False, 'inv_mask': True, 'valid': False } # agent 5: give ammo to npc -1 (invalid, should be masked) test_cond[5] = { 'tgt_id': -1, 'item_sig': self.item_sig[5][0], @@ -245,7 +242,6 @@ def test_give_full_inventory(self): def test_give_gold(self): # cannot give to an npc (should be masked) - # cannot give to the other team member (should be masked) # cannot give to self (should be masked) # cannot give if not on the same tile (should be masked) env = self._setup_env(random_seed=RANDOM_SEED) @@ -257,10 +253,10 @@ def test_give_gold(self): test_cond = {} # NOTE: the below tests rely on the static execution order from 1 to N - # agent 1: give gold to agent 3 (valid: the same team, same tile) + # agent 1: give gold to agent 3 (valid: same tile) test_cond[1] = { 'tgt_id': 3, 'gold': 1, 'ent_mask': True, 'ent_gold': self.init_gold-1, 'tgt_gold': self.init_gold+1 } - # agent 2: give gold to agent 4 (valid: the same team, same tile) + # agent 2: give gold to agent 4 (valid: same tile) test_cond[2] = { 'tgt_id': 4, 'gold': 100, 'ent_mask': True, 'ent_gold': 0, 'tgt_gold': 2*self.init_gold } # agent 3: give gold to npc -1 (invalid: cannot give to npc) @@ -272,11 +268,7 @@ def test_give_gold(self): # tgt_gold is 0 because (2) gave all gold to (4) test_cond[4] = { 'tgt_id': 2, 'gold': -1, 'ent_mask': True, 'ent_gold': 2*self.init_gold, 'tgt_gold': 0 } - # agent 5: give gold to agent 2 (invalid: the same tile but other team) - # tgt_gold is 0 because (2) gave all gold to (4) - test_cond[5] = { 'tgt_id': 2, 'gold': 1, 'ent_mask': False, - 'ent_gold': self.init_gold, 'tgt_gold': 0 } - # agent 6: give gold to agent 4 (invalid: the same team but other tile) + # agent 6: give gold to agent 4 (invalid: the other tile) # tgt_gold is 2*self.init_gold because (4) got 5 gold from (2) test_cond[6] = { 'tgt_id': 4, 'gold': 1, 'ent_mask': False, 'ent_gold': self.init_gold, 'tgt_gold': 2*self.init_gold } diff --git a/tests/entity/test_entity.py b/tests/entity/test_entity.py index 99f3f7f36..848bb7bb1 100644 --- a/tests/entity/test_entity.py +++ b/tests/entity/test_entity.py @@ -15,13 +15,11 @@ class TestEntity(unittest.TestCase): def test_entity(self): realm = MockRealm() entity_id = 123 - population_id = 11 - entity = Entity(realm, (10,20), entity_id, "name", "color", population_id) + entity = Entity(realm, (10,20), entity_id, "name") self.assertEqual(entity.id.val, entity_id) self.assertEqual(entity.row.val, 10) self.assertEqual(entity.col.val, 20) - self.assertEqual(entity.population_id.val, population_id) self.assertEqual(entity.damage.val, 0) self.assertEqual(entity.time_alive.val, 0) self.assertEqual(entity.freeze.val, 0) @@ -44,8 +42,7 @@ def test_entity(self): def test_query_by_ids(self): realm = MockRealm() entity_id = 123 - population_id = 11 - entity = Entity(realm, (10,20), entity_id, "name", "color", population_id) + entity = Entity(realm, (10,20), entity_id, "name") entities = EntityState.Query.by_ids(realm.datastore, [entity_id]) self.assertEqual(len(entities), 1) diff --git a/tests/testhelpers.py b/tests/testhelpers.py index 3c415ed4d..be2236aab 100644 --- a/tests/testhelpers.py +++ b/tests/testhelpers.py @@ -329,16 +329,25 @@ def _check_assert_make_action(self, env, atn, test_cond): self.assertFalse(self._check_ent_mask(ent_obs, atn, ent_id)) # check if the target is masked as expected - self.assertEqual( cond['ent_mask'], - self._check_ent_mask(ent_obs, atn, cond['tgt_id']) ) + self.assertEqual( + cond['ent_mask'], + self._check_ent_mask(ent_obs, atn, cond['tgt_id']), + f"ent_id: {ent_id}, atn: {ent_id}, tgt_id: {cond['tgt_id']}" + ) if atn in [action.Give]: - self.assertEqual( cond['inv_mask'], - self._check_inv_mask(ent_obs, atn, cond['item_sig']) ) + self.assertEqual( + cond['inv_mask'], + self._check_inv_mask(ent_obs, atn, cond['item_sig']), + f"ent_id: {ent_id}, atn: {ent_id}, tgt_id: {cond['item_sig']}" + ) if atn in [action.Buy]: - self.assertEqual( cond['mkt_mask'], - self._check_mkt_mask(ent_obs, cond['item_id']) ) + self.assertEqual( + cond['mkt_mask'], + self._check_mkt_mask(ent_obs, cond['item_id']), + f"ent_id: {ent_id}, atn: {ent_id}, tgt_id: {cond['item_id']}" + ) # append the actions if atn == action.Give: