From 9135a69da6dbfb6de51808432972876ca37cd38c Mon Sep 17 00:00:00 2001 From: Steffen Illium Date: Wed, 12 Jul 2023 15:59:21 +0200 Subject: [PATCH] New Szenario "Two_Rooms_One_Door" --- .../{ => configs}/default_config.yaml | 0 .../configs/two_rooms_one_door.yaml | 47 ++++++++++++ marl_factory_grid/environment/constants.py | 2 +- marl_factory_grid/environment/entity/agent.py | 16 +++++ marl_factory_grid/environment/entity/mixin.py | 2 +- .../environment/entity/object.py | 8 ++- .../environment/entity/wall_floor.py | 4 -- marl_factory_grid/environment/factory.py | 9 ++- .../environment/groups/global_entities.py | 8 ++- .../environment/groups/mixins.py | 2 +- .../environment/groups/objects.py | 5 +- marl_factory_grid/environment/groups/utils.py | 4 +- marl_factory_grid/modules/batteries/groups.py | 4 +- .../modules/destinations/actions.py | 4 +- .../modules/destinations/constants.py | 1 + .../modules/destinations/entitites.py | 25 +++++++ .../modules/destinations/groups.py | 12 +++- marl_factory_grid/modules/doors/entitites.py | 2 +- marl_factory_grid/modules/doors/groups.py | 6 -- marl_factory_grid/modules/items/groups.py | 4 +- .../modules/levels/two_rooms.txt | 13 ++++ marl_factory_grid/modules/zones/entitites.py | 4 ++ marl_factory_grid/modules/zones/groups.py | 12 ++++ marl_factory_grid/modules/zones/rules.py | 71 +++++++++++++++---- marl_factory_grid/utils/states.py | 3 + 25 files changed, 223 insertions(+), 45 deletions(-) rename marl_factory_grid/{ => configs}/default_config.yaml (100%) create mode 100644 marl_factory_grid/configs/two_rooms_one_door.yaml create mode 100644 marl_factory_grid/modules/levels/two_rooms.txt diff --git a/marl_factory_grid/default_config.yaml b/marl_factory_grid/configs/default_config.yaml similarity index 100% rename from marl_factory_grid/default_config.yaml rename to marl_factory_grid/configs/default_config.yaml diff --git a/marl_factory_grid/configs/two_rooms_one_door.yaml b/marl_factory_grid/configs/two_rooms_one_door.yaml new file mode 100644 index 0000000..7bc3cef --- /dev/null +++ b/marl_factory_grid/configs/two_rooms_one_door.yaml @@ -0,0 +1,47 @@ +Agents: + Wolfgang: + Actions: + - Move8 + - Noop + - DestAction + - DoorUse + Observations: + - Walls + - Other + - Doors + - BoundDestination + Sigmund: + Actions: + - Move8 + - Noop + - DestAction + - DoorUse + Observations: + - Combined: + - Other + - Walls + - BoundDestination + - Doors +Entities: + BoundDestinations: {} + ReachedDestinations: {} + Doors: {} + GlobalPositions: {} + Zones: {} + +General: + env_seed: 69 + individual_rewards: true + level_name: two_rooms + pomdp_r: 3 + verbose: false + +Rules: + Collision: + done_at_collisions: false + AssignGlobalPositions: {} + DoorAutoClose: + close_frequency: 10 + ZoneInit: {} + AgentSingleZonePlacement: {} + IndividualDestinationZonePlacement: {} diff --git a/marl_factory_grid/environment/constants.py b/marl_factory_grid/environment/constants.py index cc494f4..d229766 100644 --- a/marl_factory_grid/environment/constants.py +++ b/marl_factory_grid/environment/constants.py @@ -4,7 +4,7 @@ DEFAULTS = 'Defaults' SELF = 'Self' PLACEHOLDER = 'Placeholder' FLOOR = 'Floor' # Identifier of Floor-objects and groups (groups). -FLOORS = 'Floors' # Identifier of Floor-objects and groups (groups). +FLOORS = 'Floors' # Identifier of Floor-objects and groups (groups). WALL = 'Wall' # Identifier of Wall-objects and groups (groups). WALLS = 'Walls' # Identifier of Wall-objects and groups (groups). LEVEL = 'Level' # Identifier of Level-objects and groups (groups). diff --git a/marl_factory_grid/environment/entity/agent.py b/marl_factory_grid/environment/entity/agent.py index 73b9feb..206be49 100644 --- a/marl_factory_grid/environment/entity/agent.py +++ b/marl_factory_grid/environment/entity/agent.py @@ -12,6 +12,22 @@ from marl_factory_grid.environment import constants as c class Agent(Entity): + @property + def var_is_blocking_light(self): + return False + + @property + def var_can_move(self): + return True + + @property + def var_is_blocking_pos(self): + return False + + @property + def var_has_position(self): + return True + @property def obs_tag(self): return self.name diff --git a/marl_factory_grid/environment/entity/mixin.py b/marl_factory_grid/environment/entity/mixin.py index 6d21575..7148504 100644 --- a/marl_factory_grid/environment/entity/mixin.py +++ b/marl_factory_grid/environment/entity/mixin.py @@ -9,7 +9,7 @@ class BoundEntityMixin: @property def name(self): - return f'{self.__class__.__name__}({self._bound_entity.name})' + return f'{self.__class__.__name__}({self.bound_entity.name})' def belongs_to_entity(self, entity): return entity == self.bound_entity diff --git a/marl_factory_grid/environment/entity/object.py b/marl_factory_grid/environment/entity/object.py index 0c65552..c546567 100644 --- a/marl_factory_grid/environment/entity/object.py +++ b/marl_factory_grid/environment/entity/object.py @@ -21,7 +21,7 @@ class Object: def name(self): if self._str_ident is not None: return f'{self.__class__.__name__}[{self._str_ident}]' - return f'{self.__class__.__name__}#{self.identifier_int}' + return f'{self.__class__.__name__}#{self.u_int}' @property def identifier(self): @@ -30,10 +30,14 @@ class Object: else: return self.name + def reset_uid(self): + self._u_idx = defaultdict(lambda: 0) + return True + def __init__(self, str_ident: Union[str, None] = None, **kwargs): self._observers = [] self._str_ident = str_ident - self.identifier_int = self._identify_and_count_up() + self.u_int = self._identify_and_count_up() self._collection = None if kwargs: diff --git a/marl_factory_grid/environment/entity/wall_floor.py b/marl_factory_grid/environment/entity/wall_floor.py index 7ad8ed4..0b1c127 100644 --- a/marl_factory_grid/environment/entity/wall_floor.py +++ b/marl_factory_grid/environment/entity/wall_floor.py @@ -30,10 +30,6 @@ class Floor(EnvObject): def var_is_blocking_light(self): return False - @property - def neighboring_floor_pos(self): - return [x.pos for x in self.neighboring_floor] - @property def neighboring_floor(self): if self._neighboring_floor: diff --git a/marl_factory_grid/environment/factory.py b/marl_factory_grid/environment/factory.py index c09fd55..89ba3e2 100644 --- a/marl_factory_grid/environment/factory.py +++ b/marl_factory_grid/environment/factory.py @@ -78,12 +78,19 @@ class Factory(gym.Env): return self.state.entities[item] def reset(self) -> (dict, dict): + if hasattr(self, 'state'): + for entity_group in self.state.entities: + try: + entity_group[0].reset_uid() + except (AttributeError, TypeError): + pass + self.state = None # Init entity: entities = self.map.do_init() - # Grab all rules: + # Grab all )rules: rules = self.conf.load_rules() # Agents diff --git a/marl_factory_grid/environment/groups/global_entities.py b/marl_factory_grid/environment/groups/global_entities.py index b17e32e..445725a 100644 --- a/marl_factory_grid/environment/groups/global_entities.py +++ b/marl_factory_grid/environment/groups/global_entities.py @@ -41,6 +41,9 @@ class Entities(Objects): val.add_observer(self) return self + def __contains__(self, item): + return item in self._data + def __delitem__(self, name): assert_str = 'This group of entity does not exist in this collection!' assert any([key for key in name.keys() if key in self.keys()]), assert_str @@ -51,7 +54,10 @@ class Entities(Objects): @property def obs_pairs(self): - return [y for x in self for y in x.obs_pairs] + try: + return [y for x in self for y in x.obs_pairs] + except AttributeError: + print('OhOh (debug me)') def by_pos(self, pos: (int, int)): return self.pos_dict[pos] diff --git a/marl_factory_grid/environment/groups/mixins.py b/marl_factory_grid/environment/groups/mixins.py index 44e4ab9..8bf47e3 100644 --- a/marl_factory_grid/environment/groups/mixins.py +++ b/marl_factory_grid/environment/groups/mixins.py @@ -82,7 +82,7 @@ class IsBoundMixin: # noinspection PyUnresolvedReferences,PyTypeChecker -class HasBoundedMixin: +class HasBoundMixin: @property def obs_pairs(self): diff --git a/marl_factory_grid/environment/groups/objects.py b/marl_factory_grid/environment/groups/objects.py index 5182d18..7e8e542 100644 --- a/marl_factory_grid/environment/groups/objects.py +++ b/marl_factory_grid/environment/groups/objects.py @@ -37,7 +37,7 @@ class Objects: def __init__(self, *args, **kwargs): self._data = defaultdict(lambda: None) - self._observers = list() + self._observers = [self] self.pos_dict = defaultdict(list) def __len__(self): @@ -52,6 +52,7 @@ class Objects: assert self._data[item.name] is None, f'{item.name} allready exists!!!' self._data.update({item.name: item}) item.set_collection(self) + # self.notify_add_entity(item) for observer in self.observers: observer.notify_add_entity(item) return self @@ -96,8 +97,6 @@ class Objects: except StopIteration: return None - - def __getitem__(self, item): if isinstance(item, (int, np.int64, np.int32)): if item < 0: diff --git a/marl_factory_grid/environment/groups/utils.py b/marl_factory_grid/environment/groups/utils.py index fd1d8e8..49138cd 100644 --- a/marl_factory_grid/environment/groups/utils.py +++ b/marl_factory_grid/environment/groups/utils.py @@ -4,7 +4,7 @@ import numpy as np from marl_factory_grid.environment.entity.util import GlobalPosition from marl_factory_grid.environment.groups.env_objects import EnvObjects -from marl_factory_grid.environment.groups.mixins import PositionMixin, HasBoundedMixin +from marl_factory_grid.environment.groups.mixins import PositionMixin, HasBoundMixin from marl_factory_grid.environment.groups.objects import Objects from marl_factory_grid.modules.zones import Zone from marl_factory_grid.utils import helpers as h @@ -35,7 +35,7 @@ class Combined(PositionMixin, EnvObjects): return [(name, None) for name in self.names] -class GlobalPositions(HasBoundedMixin, EnvObjects): +class GlobalPositions(HasBoundMixin, EnvObjects): _entity = GlobalPosition is_blocking_light = False, diff --git a/marl_factory_grid/modules/batteries/groups.py b/marl_factory_grid/modules/batteries/groups.py index ad24eab..1eeb58e 100644 --- a/marl_factory_grid/modules/batteries/groups.py +++ b/marl_factory_grid/modules/batteries/groups.py @@ -1,9 +1,9 @@ from marl_factory_grid.environment.groups.env_objects import EnvObjects -from marl_factory_grid.environment.groups.mixins import PositionMixin, HasBoundedMixin +from marl_factory_grid.environment.groups.mixins import PositionMixin, HasBoundMixin from marl_factory_grid.modules.batteries.entitites import ChargePod, Battery -class Batteries(HasBoundedMixin, EnvObjects): +class Batteries(HasBoundMixin, EnvObjects): _entity = Battery is_blocking_light: bool = False diff --git a/marl_factory_grid/modules/destinations/actions.py b/marl_factory_grid/modules/destinations/actions.py index 05c4955..a0529b5 100644 --- a/marl_factory_grid/modules/destinations/actions.py +++ b/marl_factory_grid/modules/destinations/actions.py @@ -13,7 +13,9 @@ class DestAction(Action): super().__init__(d.DESTINATION) def do(self, entity, state) -> Union[None, ActionResult]: - if destination := state[d.DESTINATION].by_pos(entity.pos): + dest_entities = d.DESTINATION if d.DESTINATION in state else d.BOUNDDESTINATION + assert dest_entities + if destination := state[dest_entities].by_pos(entity.pos): valid = destination.do_wait_action(entity) state.print(f'{entity.name} just waited at {entity.pos}') else: diff --git a/marl_factory_grid/modules/destinations/constants.py b/marl_factory_grid/modules/destinations/constants.py index c2c0da4..2e2fa14 100644 --- a/marl_factory_grid/modules/destinations/constants.py +++ b/marl_factory_grid/modules/destinations/constants.py @@ -1,6 +1,7 @@ # Destination Env DESTINATION = 'Destinations' +BOUNDDESTINATION = 'BoundDestinations' DEST_SYMBOL = 1 DEST_REACHED_REWARD = 0.5 DEST_REACHED = 'ReachedDestinations' diff --git a/marl_factory_grid/modules/destinations/entitites.py b/marl_factory_grid/modules/destinations/entitites.py index 86b6506..a696877 100644 --- a/marl_factory_grid/modules/destinations/entitites.py +++ b/marl_factory_grid/modules/destinations/entitites.py @@ -3,12 +3,19 @@ from collections import defaultdict from marl_factory_grid.environment.entity.agent import Agent from marl_factory_grid.environment.entity.entity import Entity from marl_factory_grid.environment import constants as c +from marl_factory_grid.environment.entity.mixin import BoundEntityMixin from marl_factory_grid.utils.render import RenderEntity from marl_factory_grid.modules.destinations import constants as d class Destination(Entity): + var_can_move = False + var_can_collide = False + var_has_position = True + var_is_blocking_pos = False + var_is_blocking_light = False + @property def any_agent_has_dwelled(self): return bool(len(self._per_agent_times)) @@ -49,3 +56,21 @@ class Destination(Entity): def render(self): return RenderEntity(d.DESTINATION, self.pos) + + +class BoundDestination(BoundEntityMixin, Destination): + + @property + def encoding(self): + return d.DEST_SYMBOL + + def __init__(self, entity, *args, **kwargs): + self.bind_to(entity) + super().__init__(*args, **kwargs) + + + @property + def is_considered_reached(self): + agent_at_position = any(self.bound_entity == x for x in self.tile.guests_that_can_collide) + return (agent_at_position and not self.dwell_time) \ + or any(x == 0 for x in self._per_agent_times[self.bound_entity.name]) diff --git a/marl_factory_grid/modules/destinations/groups.py b/marl_factory_grid/modules/destinations/groups.py index a4079d4..813530f 100644 --- a/marl_factory_grid/modules/destinations/groups.py +++ b/marl_factory_grid/modules/destinations/groups.py @@ -1,6 +1,6 @@ from marl_factory_grid.environment.groups.env_objects import EnvObjects -from marl_factory_grid.environment.groups.mixins import PositionMixin -from marl_factory_grid.modules.destinations.entitites import Destination +from marl_factory_grid.environment.groups.mixins import PositionMixin, HasBoundMixin +from marl_factory_grid.modules.destinations.entitites import Destination, BoundDestination class Destinations(PositionMixin, EnvObjects): @@ -16,6 +16,14 @@ class Destinations(PositionMixin, EnvObjects): return super(Destinations, self).__repr__() +class BoundDestinations(HasBoundMixin, Destinations): + + _entity = BoundDestination + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + class ReachedDestinations(Destinations): _entity = Destination is_blocking_light = False diff --git a/marl_factory_grid/modules/doors/entitites.py b/marl_factory_grid/modules/doors/entitites.py index 36933ae..d06c8d3 100644 --- a/marl_factory_grid/modules/doors/entitites.py +++ b/marl_factory_grid/modules/doors/entitites.py @@ -72,7 +72,7 @@ class Door(Entity): def render(self): name, state = 'door_open' if self.is_open else 'door_closed', 'blank' - return RenderEntity(name, self.pos, 1, 'none', state, self.identifier_int + 1) + return RenderEntity(name, self.pos, 1, 'none', state, self.u_int + 1) def use(self): if self._status == d.STATE_OPEN: diff --git a/marl_factory_grid/modules/doors/groups.py b/marl_factory_grid/modules/doors/groups.py index 55f21b3..2c02aaf 100644 --- a/marl_factory_grid/modules/doors/groups.py +++ b/marl_factory_grid/modules/doors/groups.py @@ -14,12 +14,6 @@ class Doors(PositionMixin, EnvObjects): def __init__(self, *args, **kwargs): super(Doors, self).__init__(*args, can_collide=True, **kwargs) - def get_near_position(self, position: (int, int)) -> Union[None, Door]: - try: - return next(door for door in self if position in door.tile.neighboring_floor_pos) - except StopIteration: - return None - def tick_doors(self): result_dict = dict() for door in self: diff --git a/marl_factory_grid/modules/items/groups.py b/marl_factory_grid/modules/items/groups.py index 0812b47..8e45cd3 100644 --- a/marl_factory_grid/modules/items/groups.py +++ b/marl_factory_grid/modules/items/groups.py @@ -2,7 +2,7 @@ from typing import List from marl_factory_grid.environment.groups.env_objects import EnvObjects from marl_factory_grid.environment.groups.objects import Objects -from marl_factory_grid.environment.groups.mixins import PositionMixin, IsBoundMixin, HasBoundedMixin +from marl_factory_grid.environment.groups.mixins import PositionMixin, IsBoundMixin, HasBoundMixin from marl_factory_grid.environment.entity.wall_floor import Floor from marl_factory_grid.environment.entity.agent import Agent from marl_factory_grid.modules.items.entitites import Item, DropOffLocation @@ -46,7 +46,7 @@ class Inventory(IsBoundMixin, EnvObjects): self._collection = collection -class Inventories(HasBoundedMixin, Objects): +class Inventories(HasBoundMixin, Objects): _entity = Inventory var_can_move = False diff --git a/marl_factory_grid/modules/levels/two_rooms.txt b/marl_factory_grid/modules/levels/two_rooms.txt new file mode 100644 index 0000000..92a36b8 --- /dev/null +++ b/marl_factory_grid/modules/levels/two_rooms.txt @@ -0,0 +1,13 @@ +############### +#111111#222222# +#111111#222222# +#111111#222222# +#111111#222222# +#111111#222222# +#111111D222222# +#111111#222222# +#111111#222222# +#111111#222222# +#111111#222222# +#111111#222222# +############### \ No newline at end of file diff --git a/marl_factory_grid/modules/zones/entitites.py b/marl_factory_grid/modules/zones/entitites.py index cd5aa21..82c9cb5 100644 --- a/marl_factory_grid/modules/zones/entitites.py +++ b/marl_factory_grid/modules/zones/entitites.py @@ -12,6 +12,10 @@ from marl_factory_grid.modules.doors import constants as d class Zone(Object): + @property + def positions(self): + return [x.pos for x in self.tiles] + def __init__(self, tiles: List[Floor], *args, **kwargs): super(Zone, self).__init__(*args, **kwargs) self.tiles = tiles diff --git a/marl_factory_grid/modules/zones/groups.py b/marl_factory_grid/modules/zones/groups.py index a26b3a4..e706a29 100644 --- a/marl_factory_grid/modules/zones/groups.py +++ b/marl_factory_grid/modules/zones/groups.py @@ -10,3 +10,15 @@ class Zones(Objects): def __init__(self, *args, **kwargs): super(Zones, self).__init__(*args, can_collide=True, **kwargs) + + def by_pos(self, pos): + return self.pos_dict[pos] + + def notify_add_entity(self, entity: Zone): + self.pos_dict.update({key: [entity] for key in entity.positions}) + return True + + def notify_del_entity(self, entity: Zone): + for pos in entity.positions: + self.pos_dict[pos].remove(entity) + return True diff --git a/marl_factory_grid/modules/zones/rules.py b/marl_factory_grid/modules/zones/rules.py index 8df8be2..b3178ba 100644 --- a/marl_factory_grid/modules/zones/rules.py +++ b/marl_factory_grid/modules/zones/rules.py @@ -1,26 +1,38 @@ -from random import choices +from random import choices, choice -from marl_factory_grid.environment.rules import Rule -from marl_factory_grid.environment import constants as c -from marl_factory_grid.modules.zones import Zone -from . import constants as z +from . import constants as z, Zone +from ..destinations import constants as d +from ..destinations.entitites import BoundDestination +from ...environment.rules import Rule +from ...environment import constants as c + + +class ZoneInit(Rule): + + def __init__(self): + super().__init__() + + def on_init(self, state, lvl_map): + zones = [] + z_idx = 1 + + while z_idx: + zone_positions = lvl_map.get_coordinates_for_symbol(z_idx) + if len(zone_positions): + zones.append(Zone([state[c.FLOOR].by_pos(pos) for pos in zone_positions])) + z_idx += 1 + else: + z_idx = 0 + state[z.ZONES].add_items(zones) + return [] class AgentSingleZonePlacement(Rule): - def __init__(self, n_zones=3): + def __init__(self): super().__init__() - self.n_zones = n_zones def on_init(self, state, lvl_map): - zones = [] - - for z_idx in range(1, self.n_zones): - zone_positions = lvl_map.get_coordinates_for_symbol(z_idx) - assert len(zone_positions) - zones.append(Zone([state[c.FLOOR].by_pos(pos) for pos in zone_positions])) - state[z.ZONES].add_items(zones) - n_agents = len(state[c.AGENT]) assert len(state[z.ZONES]) >= n_agents @@ -31,3 +43,32 @@ class AgentSingleZonePlacement(Rule): def tick_step(self, state): return [] + + +class IndividualDestinationZonePlacement(Rule): + + def __init__(self): + super().__init__() + + def on_init(self, state, lvl_map): + for agent in state[c.AGENT]: + self.trigger_destination_spawn(agent, state) + pass + return [] + + def tick_step(self, state): + return [] + + @staticmethod + def trigger_destination_spawn(agent, state): + agent_zones = state[z.ZONES].by_pos(agent.pos) + other_zones = [x for x in state[z.ZONES] if x not in agent_zones] + already_has_destination = True + while already_has_destination: + tile = choice(other_zones).random_tile + if state[d.BOUNDDESTINATION].by_pos(tile.pos) is None: + already_has_destination = False + destination = BoundDestination(agent, tile) + state[d.BOUNDDESTINATION].add_item(destination) + continue + return c.VALID diff --git a/marl_factory_grid/utils/states.py b/marl_factory_grid/utils/states.py index 4eff5a7..d8ccc10 100644 --- a/marl_factory_grid/utils/states.py +++ b/marl_factory_grid/utils/states.py @@ -76,6 +76,9 @@ class Gamestate(object): def __iter__(self): return iter(e for e in self.entities.values()) + def __contains__(self, item): + return item in self.entities + def __repr__(self): return f'{self.__class__.__name__}({len(self.entities)} Entitites @ Step {self.curr_step})'