WIP: removing tiles

This commit is contained in:
Chanumask
2023-10-16 11:06:51 +02:00
parent 3bf3246aeb
commit 3677b7b7dc
37 changed files with 255 additions and 247 deletions

View File

@@ -66,7 +66,8 @@ class Pod(Entity):
def charge_battery(self, battery: Battery):
if battery.charge_level == 1.0:
return c.NOT_VALID
if sum(guest for guest in self.tile.guests if 'agent' in guest.name.lower()) > 1:
# if sum(guest for guest in self.tile.guests if 'agent' in guest.name.lower()) > 1:
if sum(1 for key, val in self.state.entities.pos_dict[self.pos] for guest in val if 'agent' in guest.name.lower()) > 1:
return c.NOT_VALID
valid = battery.do_charge_action(self.charge_rate)
return valid

View File

@@ -1,6 +1,4 @@
from .actions import CleanUp
from .entitites import DirtPile
from .groups import DirtPiles
from .rule_respawn import DirtRespawnRule
from .rule_smear_on_move import DirtSmearOnMove
from .rule_done_on_all_clean import DirtAllCleanDone
from .rules import DirtRespawnRule, DirtSmearOnMove, DirtAllCleanDone

View File

@@ -14,7 +14,7 @@ class CleanUp(Action):
super().__init__(d.CLEAN_UP)
def do(self, entity, state) -> Union[None, ActionResult]:
if dirt := state[d.DIRT].by_pos(entity.pos):
if dirt := next((x for x in state.entities.pos_dict[entity.pos] if "dirt" in x.name.lower()), None):
new_dirt_amount = dirt.amount - state[d.DIRT].clean_amount
if new_dirt_amount <= 0:

View File

@@ -7,7 +7,6 @@ from marl_factory_grid.environment import constants as c
class DirtPiles(PositionMixin, EnvObjects):
_entity = DirtPile
is_blocking_light: bool = False
can_collide: bool = False
@@ -31,27 +30,28 @@ class DirtPiles(PositionMixin, EnvObjects):
self.max_global_amount = max_global_amount
self.max_local_amount = max_local_amount
def spawn(self, then_dirty_tiles, amount) -> bool:
if isinstance(then_dirty_tiles, Floor):
then_dirty_tiles = [then_dirty_tiles]
for tile in then_dirty_tiles:
def spawn(self, then_dirty_positions, amount) -> bool:
# if isinstance(then_dirty_tiles, Floor):
# then_dirty_tiles = [then_dirty_tiles]
for pos in then_dirty_positions:
if not self.amount > self.max_global_amount:
if dirt := self.by_pos(tile.pos):
if dirt := self.by_pos(pos):
new_value = dirt.amount + amount
dirt.set_new_amount(new_value)
else:
dirt = DirtPile(tile, initial_amount=amount, spawn_variation=self.dirt_spawn_r_var)
dirt = DirtPile(pos, initial_amount=amount, spawn_variation=self.dirt_spawn_r_var)
self.add_item(dirt)
else:
return c.NOT_VALID
return c.VALID
def trigger_dirt_spawn(self, state, initial_spawn=False) -> bool:
free_for_dirt = [x for x in state[c.FLOOR]
if len(x.guests) == 0 or (
len(x.guests) == 1 and
isinstance(next(y for y in x.guests), DirtPile))
]
free_for_dirt = [x for x in state.entities.floorlist if len(state.entities.pos_dict[x]) == 1 or (
len(state.entities.pos_dict[x]) == 2 and isinstance(next(y for y in x), DirtPile))]
# free_for_dirt = [x for x in state[c.FLOOR]
# if len(x.guests) == 0 or (
# len(x.guests) == 1 and
# isinstance(next(y for y in x.guests), DirtPile))]
state.rng.shuffle(free_for_dirt)
var = self.dirt_spawn_r_var

View File

@@ -1,15 +0,0 @@
from marl_factory_grid.environment import constants as c
from marl_factory_grid.environment.rules import Rule
from marl_factory_grid.utils.results import DoneResult
from marl_factory_grid.modules.clean_up import constants as d, rewards as r
class DirtAllCleanDone(Rule):
def __init__(self):
super().__init__()
def on_check_done(self, state) -> [DoneResult]:
if len(state[d.DIRT]) == 0 and state.curr_step:
return [DoneResult(validity=c.VALID, identifier=self.name, reward=r.CLEAN_UP_ALL)]
return [DoneResult(validity=c.NOT_VALID, identifier=self.name, reward=0)]

View File

@@ -1,28 +0,0 @@
from marl_factory_grid.environment.rules import Rule
from marl_factory_grid.utils.results import TickResult
from marl_factory_grid.modules.clean_up import constants as d
class DirtRespawnRule(Rule):
def __init__(self, spawn_freq=15):
super().__init__()
self.spawn_freq = spawn_freq
self._next_dirt_spawn = spawn_freq
def on_init(self, state, lvl_map) -> str:
state[d.DIRT].trigger_dirt_spawn(state, initial_spawn=True)
return f'Initial Dirt was spawned on: {[x.pos for x in state[d.DIRT]]}'
def tick_step(self, state):
if self._next_dirt_spawn < 0:
pass # No DirtPile Spawn
elif not self._next_dirt_spawn:
validity = state[d.DIRT].trigger_dirt_spawn(state)
return [TickResult(entity=None, validity=validity, identifier=self.name, reward=0)]
self._next_dirt_spawn = self.spawn_freq
else:
self._next_dirt_spawn -= 1
return []

View File

@@ -1,24 +0,0 @@
from marl_factory_grid.environment.rules import Rule
from marl_factory_grid.utils.helpers import is_move
from marl_factory_grid.utils.results import TickResult
from marl_factory_grid.environment import constants as c
from marl_factory_grid.modules.clean_up import constants as d
class DirtSmearOnMove(Rule):
def __init__(self, smear_amount: float = 0.2):
super().__init__()
self.smear_amount = smear_amount
def tick_post_step(self, state):
results = list()
for entity in state.moving_entites:
if is_move(entity.state.identifier) and entity.state.validity == c.VALID:
if old_pos_dirt := state[d.DIRT].by_pos(entity.last_pos):
if smeared_dirt := round(old_pos_dirt.amount * self.smear_amount, 2):
if state[d.DIRT].spawn(entity.tile, amount=smeared_dirt):
results.append(TickResult(identifier=self.name, entity=entity,
reward=0, validity=c.VALID))
return results

View File

@@ -0,0 +1,60 @@
from marl_factory_grid.modules.clean_up import constants as d, rewards as r
from marl_factory_grid.environment import constants as c
from marl_factory_grid.environment.rules import Rule
from marl_factory_grid.utils.helpers import is_move
from marl_factory_grid.utils.results import TickResult
from marl_factory_grid.utils.results import DoneResult
class DirtAllCleanDone(Rule):
def __init__(self):
super().__init__()
def on_check_done(self, state) -> [DoneResult]:
if len(state[d.DIRT]) == 0 and state.curr_step:
return [DoneResult(validity=c.VALID, identifier=self.name, reward=r.CLEAN_UP_ALL)]
return [DoneResult(validity=c.NOT_VALID, identifier=self.name, reward=0)]
class DirtRespawnRule(Rule):
def __init__(self, spawn_freq=15):
super().__init__()
self.spawn_freq = spawn_freq
self._next_dirt_spawn = spawn_freq
def on_init(self, state, lvl_map) -> str:
state[d.DIRT].trigger_dirt_spawn(state, initial_spawn=True)
return f'Initial Dirt was spawned on: {[x.pos for x in state[d.DIRT]]}'
def tick_step(self, state):
if self._next_dirt_spawn < 0:
pass # No DirtPile Spawn
elif not self._next_dirt_spawn:
validity = state[d.DIRT].trigger_dirt_spawn(state)
return [TickResult(entity=None, validity=validity, identifier=self.name, reward=0)]
self._next_dirt_spawn = self.spawn_freq
else:
self._next_dirt_spawn -= 1
return []
class DirtSmearOnMove(Rule):
def __init__(self, smear_amount: float = 0.2):
super().__init__()
self.smear_amount = smear_amount
def tick_post_step(self, state):
results = list()
for entity in state.moving_entites:
if is_move(entity.state.identifier) and entity.state.validity == c.VALID:
if old_pos_dirt := state[d.DIRT].by_pos(entity.last_pos):
if smeared_dirt := round(old_pos_dirt.amount * self.smear_amount, 2):
if state[d.DIRT].spawn(entity.pos, amount=smeared_dirt): # pos statt tile
results.append(TickResult(identifier=self.name, entity=entity,
reward=0, validity=c.VALID))
return results

View File

@@ -9,7 +9,6 @@ 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
@@ -40,9 +39,9 @@ class Destination(Entity):
def leave(self, agent: Agent):
del self._per_agent_times[agent.name]
@property
def is_considered_reached(self):
agent_at_position = any(c.AGENT.lower() in x.name.lower() for x in self.tile.guests_that_can_collide)
def is_considered_reached(self, state):
agent_at_position = any(
c.AGENT.lower() in x.name.lower() for x in state.entities.pos_dict[self.pos] if x.var_can_collide)
return (agent_at_position and not self.dwell_time) or any(x == 0 for x in self._per_agent_times.values())
def agent_is_dwelling(self, agent: Agent):
@@ -68,9 +67,9 @@ class BoundDestination(BoundEntityMixin, Destination):
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)
agent_at_position = any(
self.bound_entity == x for x in self.state.entities.pos_dict[self.pos] if x.var_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])

View File

@@ -1,10 +1,11 @@
from marl_factory_grid.environment.groups.env_objects import EnvObjects
from marl_factory_grid.environment.groups.mixins import PositionMixin, HasBoundMixin
from marl_factory_grid.modules.destinations.entitites import Destination, BoundDestination
from marl_factory_grid.environment import constants as c
from marl_factory_grid.modules.destinations import constants as d
class Destinations(PositionMixin, EnvObjects):
_entity = Destination
is_blocking_light: bool = False
can_collide: bool = False
@@ -15,9 +16,19 @@ class Destinations(PositionMixin, EnvObjects):
def __repr__(self):
return super(Destinations, self).__repr__()
@staticmethod
def trigger_destination_spawn(n_dests, state):
coordinates = state.entities.floorlist[:n_dests]
if destinations := [Destination(pos) for pos in coordinates]:
state[d.DESTINATION].add_items(destinations)
state.print(f'{n_dests} new destinations have been spawned')
return c.VALID
else:
state.print('No Destiantions are spawning, limit is reached.')
return c.NOT_VALID
class BoundDestinations(HasBoundMixin, Destinations):
_entity = BoundDestination
def __init__(self, *args, **kwargs):

View File

@@ -12,12 +12,12 @@ class DestinationReach(Rule):
def __init__(self, n_dests: int = 1, tiles: Union[List, None] = None):
super(DestinationReach, self).__init__()
self.n_dests = n_dests or len(tiles)
self._tiles = tiles
# self._tiles = tiles
def tick_step(self, state) -> List[TickResult]:
for dest in list(state[d.DESTINATION].values()):
if dest.is_considered_reached:
if dest.is_considered_reached(state):
dest.change_parent_collection(state[d.DEST_REACHED])
state.print(f'{dest.name} is reached now, removing...')
else:
@@ -34,7 +34,7 @@ class DestinationReach(Rule):
def tick_post_step(self, state) -> List[TickResult]:
results = list()
for reached_dest in state[d.DEST_REACHED]:
for guest in reached_dest.tile.guests:
for guest in state.entities.pos_dict[reached_dest].values(): # reached_dest.tile.guests:
if guest in state[c.AGENT]:
state.print(f'{guest.name} just reached destination at {guest.pos}')
state[d.DEST_REACHED].delete_env_object(reached_dest)
@@ -79,7 +79,7 @@ class DestinationSpawn(Rule):
def on_init(self, state, lvl_map):
# noinspection PyAttributeOutsideInit
self._dest_spawn_timer = self.spawn_frequency
self.trigger_destination_spawn(self.n_dests, state)
state[d.DESTINATION].trigger_destination_spawn(self.n_dests, state)
pass
def tick_pre_step(self, state) -> List[TickResult]:
@@ -91,13 +91,3 @@ class DestinationSpawn(Rule):
validity = state.rules['DestinationReach'].trigger_destination_spawn(n_dest_spawn, state)
return [TickResult(self.name, validity=validity, entity=None, value=n_dest_spawn)]
@staticmethod
def trigger_destination_spawn(n_dests, state, tiles=None):
tiles = tiles or state[c.FLOOR].empty_tiles[:n_dests]
if destinations := [Destination(tile) for tile in tiles]:
state[d.DESTINATION].add_items(destinations)
state.print(f'{n_dests} new destinations have been spawned')
return c.VALID
else:
state.print('No Destiantions are spawning, limit is reached.')
return c.NOT_VALID

View File

@@ -81,11 +81,11 @@ class Door(Entity):
self._open()
return c.VALID
def tick(self):
if self.is_open and len(self.tile) == 1 and self.time_to_close:
def tick(self, state):
if self.is_open and len(state.entities.pos_dict[self.pos]) == 2 and self.time_to_close:
self.time_to_close -= 1
return c.NOT_VALID
elif self.is_open and not self.time_to_close and len(self.tile) == 1:
elif self.is_open and not self.time_to_close and len(state.entities.pos_dict[self.pos]) == 2:
self.use()
return c.VALID
else:

View File

@@ -14,9 +14,9 @@ class Doors(PositionMixin, EnvObjects):
def __init__(self, *args, **kwargs):
super(Doors, self).__init__(*args, can_collide=True, **kwargs)
def tick_doors(self):
def tick_doors(self, state):
result_dict = dict()
for door in self:
did_tick = door.tick()
did_tick = door.tick(state)
result_dict.update({door.name: did_tick})
return result_dict

View File

@@ -12,7 +12,7 @@ class DoorAutoClose(Rule):
def tick_step(self, state):
if doors := state[d.DOORS]:
doors_tick_result = doors.tick_doors()
doors_tick_result = doors.tick_doors(state)
doors_that_ticked = [key for key, val in doors_tick_result.items() if val]
state.print(f'{doors_that_ticked} were auto-closed'
if doors_that_ticked else 'No Doors were auto-closed')

View File

@@ -23,7 +23,7 @@ class AgentSingleZonePlacementBeta(Rule):
raise ValueError
tiles = [state[c.FLOOR].by_pos(pos) for pos in coordinates]
for agent, tile in zip(agents, tiles):
agent.move(tile)
agent.move(tile, state)
def tick_step(self, state):
return []

View File

@@ -1,15 +1,14 @@
from typing import List
from marl_factory_grid.modules.items import constants as i
from marl_factory_grid.environment import constants as c
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, 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
class Items(PositionMixin, EnvObjects):
_entity = Item
is_blocking_light: bool = False
can_collide: bool = False
@@ -17,9 +16,19 @@ class Items(PositionMixin, EnvObjects):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@staticmethod
def trigger_item_spawn(state, n_items, spawn_frequency):
if item_to_spawns := max(0, (n_items - len(state[i.ITEM]))):
floor_list = state.entities.floorlist[:item_to_spawns]
state[i.ITEM].spawn(floor_list)
state.print(f'{item_to_spawns} new items have been spawned; next spawn in {spawn_frequency}') # spawn in self._next_item_spawn ?
return len(floor_list)
else:
state.print('No Items are spawning, limit is reached.')
return 0
class Inventory(IsBoundMixin, EnvObjects):
_accepted_objects = Item
@property
@@ -27,7 +36,7 @@ class Inventory(IsBoundMixin, EnvObjects):
return self.name
def __init__(self, agent: Agent, *args, **kwargs):
super(Inventory, self).__init__(*args, **kwargs)
super(Inventory, self).__init__(*args, **kwargs)
self._collection = None
self.bind(agent)
@@ -47,7 +56,6 @@ class Inventory(IsBoundMixin, EnvObjects):
class Inventories(HasBoundMixin, Objects):
_entity = Inventory
var_can_move = False
@@ -58,7 +66,7 @@ class Inventories(HasBoundMixin, Objects):
self._lazy_eval_transforms = []
def spawn(self, agents):
inventories = [self._entity(agent, self.size,)
inventories = [self._entity(agent, self.size, )
for _, agent in enumerate(agents)]
self.add_items(inventories)
@@ -77,12 +85,22 @@ class Inventories(HasBoundMixin, Objects):
def summarize_states(self, **kwargs):
return [val.summarize_states(**kwargs) for key, val in self.items()]
@staticmethod
def trigger_inventory_spawn(state):
state[i.INVENTORY].spawn(state[c.AGENT])
class DropOffLocations(PositionMixin, EnvObjects):
_entity = DropOffLocation
is_blocking_light: bool = False
can_collide: bool = False
def __init__(self, *args, **kwargs):
super(DropOffLocations, self).__init__(*args, **kwargs)
@staticmethod
def trigger_drop_off_location_spawn(state, n_locations):
empty_tiles = state.entities.floorlist[:n_locations]
do_entites = state[i.DROP_OFF]
drop_offs = [DropOffLocation(tile) for tile in empty_tiles]
do_entites.add_items(drop_offs)

View File

@@ -4,7 +4,6 @@ from marl_factory_grid.environment.rules import Rule
from marl_factory_grid.environment import constants as c
from marl_factory_grid.utils.results import TickResult
from marl_factory_grid.modules.items import constants as i
from marl_factory_grid.modules.items.entitites import DropOffLocation
class ItemRules(Rule):
@@ -19,10 +18,10 @@ class ItemRules(Rule):
self.n_locations = n_locations
def on_init(self, state, lvl_map):
self.trigger_drop_off_location_spawn(state)
state[i.DROP_OFF].trigger_drop_off_location_spawn(state, self.n_locations)
self._next_item_spawn = self.spawn_frequency
self.trigger_inventory_spawn(state)
self.trigger_item_spawn(state)
state[i.INVENTORY].trigger_inventory_spawn(state)
state[i.ITEM].trigger_item_spawn(state, self.n_items, self.spawn_frequency)
def tick_step(self, state):
for item in list(state[i.ITEM].values()):
@@ -34,26 +33,11 @@ class ItemRules(Rule):
pass
if not self._next_item_spawn:
self.trigger_item_spawn(state)
state[i.ITEM].trigger_item_spawn(state, self.n_items, self.spawn_frequency)
else:
self._next_item_spawn = max(0, self._next_item_spawn - 1)
return []
def trigger_item_spawn(self, state):
if item_to_spawns := max(0, (self.n_items - len(state[i.ITEM]))):
empty_tiles = state[c.FLOOR].empty_tiles[:item_to_spawns]
state[i.ITEM].spawn(empty_tiles)
self._next_item_spawn = self.spawn_frequency
state.print(f'{item_to_spawns} new items have been spawned; next spawn in {self._next_item_spawn}')
return len(empty_tiles)
else:
state.print('No Items are spawning, limit is reached.')
return 0
@staticmethod
def trigger_inventory_spawn(state):
state[i.INVENTORY].spawn(state[c.AGENT])
def tick_post_step(self, state) -> List[TickResult]:
for item in list(state[i.ITEM].values()):
if item.auto_despawn >= 1:
@@ -64,7 +48,7 @@ class ItemRules(Rule):
pass
if not self._next_item_spawn:
if spawned_items := self.trigger_item_spawn(state):
if spawned_items := state[i.ITEM].trigger_item_spawn(state, self.n_items, self.spawn_frequency):
return [TickResult(self.name, validity=c.VALID, value=spawned_items, entity=None)]
else:
return [TickResult(self.name, validity=c.NOT_VALID, value=0, entity=None)]
@@ -72,8 +56,3 @@ class ItemRules(Rule):
self._next_item_spawn = max(0, self._next_item_spawn-1)
return []
def trigger_drop_off_location_spawn(self, state):
empty_tiles = state[c.FLOOR].empty_tiles[:self.n_locations]
do_entites = state[i.DROP_OFF]
drop_offs = [DropOffLocation(tile) for tile in empty_tiles]
do_entites.add_items(drop_offs)

View File

@@ -47,9 +47,11 @@ class Machine(Entity):
return c.NOT_VALID
def tick(self):
if self.status == m.STATE_MAINTAIN and any([c.AGENT in x.name for x in self.tile.guests]):
# if self.status == m.STATE_MAINTAIN and any([c.AGENT in x.name for x in self.tile.guests]):
if self.status == m.STATE_MAINTAIN and any([c.AGENT in x.name for x in self.state.entities.pos_dict[self.pos]]):
return TickResult(identifier=self.name, validity=c.VALID, reward=0, entity=self)
elif self.status == m.STATE_MAINTAIN and not any([c.AGENT in x.name for x in self.tile.guests]):
# elif self.status == m.STATE_MAINTAIN and not any([c.AGENT in x.name for x in self.tile.guests]):
elif self.status == m.STATE_MAINTAIN and not any([c.AGENT in x.name for x in self.state.entities.pos_dict[self.pos]]):
self.status = m.STATE_WORK
self.reset_counter()
return None

View File

@@ -61,7 +61,7 @@ class Maintainer(Entity):
self._last.append(self._next.pop())
self._path = self.calculate_route(self._last[-1])
if door := self._door_is_close():
if door := self._door_is_close(state):
if door.is_closed:
# Translate the action_object to an integer to have the same output as any other model
action = do.ACTION_DOOR_USE
@@ -81,15 +81,18 @@ class Maintainer(Entity):
route = nx.shortest_path(self._floortile_graph, self.pos, entity.pos)
return route[1:]
def _door_is_close(self):
def _door_is_close(self, state):
print("doorclose")
try:
return next(y for x in self.tile.neighboring_floor for y in x.guests if do.DOOR in y.name)
# return next(y for x in self.tile.neighboring_floor for y in x.guests if do.DOOR in y.name)
return next(y for x in state.entities.neighboring_positions(self.state.pos) for y in state.entities.pos_dict[x] if do.DOOR in y.name)
except StopIteration:
return None
def _predict_move(self, state):
next_pos = self._path[0]
if len(state[c.FLOOR].by_pos(next_pos).guests_that_can_collide) > 0:
# if len(state[c.FLOOR].by_pos(next_pos).guests_that_can_collide) > 0:
if any(x for x in state.entities.pos_dict[next_pos] if x.var_can_collide) > 0:
action = c.NOOP
else:
next_pos = self._path.pop(0)

View File

@@ -23,5 +23,5 @@ class Maintainers(PositionMixin, EnvObjects):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def spawn(self, tiles: List[Floor], state: Gamestate):
self.add_items([self._entity(state, mc.MACHINES, MachineAction(), tile) for tile in tiles])
def spawn(self, position, state: Gamestate):
self.add_items([self._entity(state, mc.MACHINES, MachineAction(), pos) for pos in position])

View File

@@ -38,7 +38,7 @@ class AgentSingleZonePlacement(Rule):
z_idxs = choices(list(range(len(state[z.ZONES]))), k=n_agents)
for agent in state[c.AGENT]:
agent.move(state[z.ZONES][z_idxs.pop()].random_tile)
agent.move(state[z.ZONES][z_idxs.pop()].random_tile, state)
return []
def tick_step(self, state):