mirror of
https://github.com/illiumst/marl-factory-grid.git
synced 2025-07-04 16:41:36 +02:00
Doors now Working,
Pypi install adjustments.
This commit is contained in:
1
MANIFEST.in
Normal file
1
MANIFEST.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
global-include *.txt *.png *.jpg
|
@ -3,14 +3,11 @@
|
|||||||
Tackling emergent dysfunctions (EDYs) in cooperation with Fraunhofer-IKS
|
Tackling emergent dysfunctions (EDYs) in cooperation with Fraunhofer-IKS
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
1. Make sure to install `virtualenv` using `pip install virtualenv`
|
Just install this environment by `pip install marl-factory-grid`.
|
||||||
2. Create a new virtual environment `virtualenv venv`
|
|
||||||
3. Activate the virtual environment `source venv/bin/activate`
|
|
||||||
4. Install the required dependencies `pip install -r requirements.txt`
|
|
||||||
##
|
|
||||||
|
|
||||||
## First Steps
|
## First Steps
|
||||||
|
|
||||||
|
|
||||||
### Quickstart
|
### Quickstart
|
||||||
Most of the env. objects (entites, rules and assets) can be loaded automatically.
|
Most of the env. objects (entites, rules and assets) can be loaded automatically.
|
||||||
Just define what your environment needs in a *yaml*-configfile like:
|
Just define what your environment needs in a *yaml*-configfile like:
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
import itertools
|
||||||
from random import choice
|
from random import choice
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
import networkx as nx
|
||||||
from networkx.algorithms.approximation import traveling_salesman as tsp
|
from networkx.algorithms.approximation import traveling_salesman as tsp
|
||||||
|
|
||||||
from environment.utils.helpers import points_to_graph
|
|
||||||
|
|
||||||
from modules.doors import constants as do
|
from modules.doors import constants as do
|
||||||
from environment import constants as c
|
from environment import constants as c
|
||||||
@ -15,6 +16,41 @@ from abc import abstractmethod, ABC
|
|||||||
future_planning = 7
|
future_planning = 7
|
||||||
|
|
||||||
|
|
||||||
|
def points_to_graph(coordiniates_or_tiles, allow_euclidean_connections=True, allow_manhattan_connections=True):
|
||||||
|
"""
|
||||||
|
Given a set of coordinates, this function contructs a non-directed graph, by conncting adjected points.
|
||||||
|
There are three combinations of settings:
|
||||||
|
Allow all neigbors: Distance(a, b) <= sqrt(2)
|
||||||
|
Allow only manhattan: Distance(a, b) == 1
|
||||||
|
Allow only euclidean: Distance(a, b) == sqrt(2)
|
||||||
|
|
||||||
|
|
||||||
|
:param coordiniates_or_tiles: A set of coordinates.
|
||||||
|
:type coordiniates_or_tiles: Tiles
|
||||||
|
:param allow_euclidean_connections: Whether to regard diagonal adjected cells as neighbors
|
||||||
|
:type: bool
|
||||||
|
:param allow_manhattan_connections: Whether to regard directly adjected cells as neighbors
|
||||||
|
:type: bool
|
||||||
|
|
||||||
|
:return: A graph with nodes that are conneceted as specified by the parameters.
|
||||||
|
:rtype: nx.Graph
|
||||||
|
"""
|
||||||
|
assert allow_euclidean_connections or allow_manhattan_connections
|
||||||
|
if hasattr(coordiniates_or_tiles, 'positions'):
|
||||||
|
coordiniates_or_tiles = coordiniates_or_tiles.positions
|
||||||
|
possible_connections = itertools.combinations(coordiniates_or_tiles, 2)
|
||||||
|
graph = nx.Graph()
|
||||||
|
for a, b in possible_connections:
|
||||||
|
diff = np.linalg.norm(np.asarray(a)-np.asarray(b))
|
||||||
|
if allow_manhattan_connections and allow_euclidean_connections and diff <= np.sqrt(2):
|
||||||
|
graph.add_edge(a, b)
|
||||||
|
elif not allow_manhattan_connections and allow_euclidean_connections and diff == np.sqrt(2):
|
||||||
|
graph.add_edge(a, b)
|
||||||
|
elif allow_manhattan_connections and not allow_euclidean_connections and diff == 1:
|
||||||
|
graph.add_edge(a, b)
|
||||||
|
return graph
|
||||||
|
|
||||||
|
|
||||||
class TSPBaseAgent(ABC):
|
class TSPBaseAgent(ABC):
|
||||||
|
|
||||||
def __init__(self, state, agent_i, static_problem: bool = True):
|
def __init__(self, state, agent_i, static_problem: bool = True):
|
||||||
|
0
environment/assets/agent/__init__.py
Normal file
0
environment/assets/agent/__init__.py
Normal file
@ -57,7 +57,7 @@ class BaseFactory(gym.Env):
|
|||||||
self.conf = FactoryConfigParser(self._config_file)
|
self.conf = FactoryConfigParser(self._config_file)
|
||||||
# Attribute Assignment
|
# Attribute Assignment
|
||||||
self.level_filepath = Path(__file__).parent.parent / h.LEVELS_DIR / f'{self.conf.level_name}.txt'
|
self.level_filepath = Path(__file__).parent.parent / h.LEVELS_DIR / f'{self.conf.level_name}.txt'
|
||||||
self._renderer = None # expensive - don't use it when not required !
|
self._renderer = None # expensive - don't use; unless required !
|
||||||
|
|
||||||
parsed_entities = self.conf.load_entities()
|
parsed_entities = self.conf.load_entities()
|
||||||
self.map = LevelParser(self.level_filepath, parsed_entities, self.conf.pomdp_r)
|
self.map = LevelParser(self.level_filepath, parsed_entities, self.conf.pomdp_r)
|
||||||
@ -173,7 +173,7 @@ class BaseFactory(gym.Env):
|
|||||||
if not self._renderer: # lazy init
|
if not self._renderer: # lazy init
|
||||||
from environment.utils.renderer import Renderer
|
from environment.utils.renderer import Renderer
|
||||||
global Renderer
|
global Renderer
|
||||||
self._renderer = Renderer(self.map.level_shape, view_radius=self.conf.pomdp_r, fps=20)
|
self._renderer = Renderer(self.map.level_shape, view_radius=self.conf.pomdp_r, fps=10)
|
||||||
|
|
||||||
render_entities = self.state.entities.render()
|
render_entities = self.state.entities.render()
|
||||||
if self.conf.pomdp_r:
|
if self.conf.pomdp_r:
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
from os import PathLike
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@ -20,9 +22,10 @@ class FactoryConfigParser(object):
|
|||||||
default_actions = [c.MOVE8, c.NOOP]
|
default_actions = [c.MOVE8, c.NOOP]
|
||||||
default_observations = [c.WALLS, c.AGENTS]
|
default_observations = [c.WALLS, c.AGENTS]
|
||||||
|
|
||||||
def __init__(self, config_path):
|
def __init__(self, config_path, custom_modules_path: Union[None, PathLike] = None):
|
||||||
self.config_path = Path(config_path)
|
self.config_path = Path(config_path)
|
||||||
self.config = yaml.safe_load(config_path.open())
|
self.custom_modules_path = Path(config_path) if custom_modules_path is not None else custom_modules_path
|
||||||
|
self.config = yaml.safe_load(self.config_path.open())
|
||||||
self.do_record = False
|
self.do_record = False
|
||||||
|
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item):
|
||||||
@ -67,7 +70,12 @@ class FactoryConfigParser(object):
|
|||||||
entities.extend(x for x in self.entities if x != c.DEFAULTS)
|
entities.extend(x for x in self.entities if x != c.DEFAULTS)
|
||||||
|
|
||||||
for entity in entities:
|
for entity in entities:
|
||||||
|
try:
|
||||||
folder_path = MODULE_PATH if entity not in self.default_entites else DEFAULT_PATH
|
folder_path = MODULE_PATH if entity not in self.default_entites else DEFAULT_PATH
|
||||||
|
folder_path = (Path(__file__) / '..' / '..' / '..' / folder_path)
|
||||||
|
entity_class = locate_and_import_class(entity, folder_path)
|
||||||
|
except AttributeError:
|
||||||
|
folder_path = self.custom_modules_path
|
||||||
entity_class = locate_and_import_class(entity, folder_path)
|
entity_class = locate_and_import_class(entity, folder_path)
|
||||||
entity_kwargs = self.entities.get(entity, {})
|
entity_kwargs = self.entities.get(entity, {})
|
||||||
entity_symbol = entity_class.symbol if hasattr(entity_class, 'symbol') else None
|
entity_symbol = entity_class.symbol if hasattr(entity_class, 'symbol') else None
|
||||||
@ -86,11 +94,15 @@ class FactoryConfigParser(object):
|
|||||||
parsed_actions = list()
|
parsed_actions = list()
|
||||||
for action in actions:
|
for action in actions:
|
||||||
folder_path = MODULE_PATH if action not in base_env_actions else DEFAULT_PATH
|
folder_path = MODULE_PATH if action not in base_env_actions else DEFAULT_PATH
|
||||||
|
try:
|
||||||
class_or_classes = locate_and_import_class(action, folder_path)
|
class_or_classes = locate_and_import_class(action, folder_path)
|
||||||
|
except AttributeError:
|
||||||
|
class_or_classes = locate_and_import_class(action, self.custom_modules_path)
|
||||||
try:
|
try:
|
||||||
parsed_actions.extend(class_or_classes)
|
parsed_actions.extend(class_or_classes)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
parsed_actions.append(class_or_classes)
|
parsed_actions.append(class_or_classes)
|
||||||
|
|
||||||
parsed_actions = [x() for x in parsed_actions]
|
parsed_actions = [x() for x in parsed_actions]
|
||||||
|
|
||||||
# Observation
|
# Observation
|
||||||
@ -114,7 +126,10 @@ class FactoryConfigParser(object):
|
|||||||
|
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
folder_path = MODULE_PATH if rule not in self.default_rules else DEFAULT_PATH
|
folder_path = MODULE_PATH if rule not in self.default_rules else DEFAULT_PATH
|
||||||
|
try:
|
||||||
rule_class = locate_and_import_class(rule, folder_path)
|
rule_class = locate_and_import_class(rule, folder_path)
|
||||||
|
except AttributeError:
|
||||||
|
rule_class = locate_and_import_class(rule, self.custom_modules_path)
|
||||||
rule_kwargs = self.rules.get(rule, {})
|
rule_kwargs = self.rules.get(rule, {})
|
||||||
rules_classes.update({rule: {'class': rule_class, 'kwargs': rule_kwargs}})
|
rules_classes.update({rule: {'class': rule_class, 'kwargs': rule_kwargs}})
|
||||||
return rules_classes
|
return rules_classes
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import importlib
|
import importlib
|
||||||
import itertools
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pathlib import PurePath, Path
|
from pathlib import PurePath, Path
|
||||||
from typing import Union, Dict, List
|
from typing import Union, Dict, List
|
||||||
|
|
||||||
import networkx as nx
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import ArrayLike
|
from numpy.typing import ArrayLike
|
||||||
|
|
||||||
@ -210,58 +209,25 @@ def asset_str(agent):
|
|||||||
return c.AGENT, 'idle'
|
return c.AGENT, 'idle'
|
||||||
|
|
||||||
|
|
||||||
def points_to_graph(coordiniates_or_tiles, allow_euclidean_connections=True, allow_manhattan_connections=True):
|
|
||||||
"""
|
|
||||||
Given a set of coordinates, this function contructs a non-directed graph, by conncting adjected points.
|
|
||||||
There are three combinations of settings:
|
|
||||||
Allow all neigbors: Distance(a, b) <= sqrt(2)
|
|
||||||
Allow only manhattan: Distance(a, b) == 1
|
|
||||||
Allow only euclidean: Distance(a, b) == sqrt(2)
|
|
||||||
|
|
||||||
|
|
||||||
:param coordiniates_or_tiles: A set of coordinates.
|
|
||||||
:type coordiniates_or_tiles: Tiles
|
|
||||||
:param allow_euclidean_connections: Whether to regard diagonal adjected cells as neighbors
|
|
||||||
:type: bool
|
|
||||||
:param allow_manhattan_connections: Whether to regard directly adjected cells as neighbors
|
|
||||||
:type: bool
|
|
||||||
|
|
||||||
:return: A graph with nodes that are conneceted as specified by the parameters.
|
|
||||||
:rtype: nx.Graph
|
|
||||||
"""
|
|
||||||
assert allow_euclidean_connections or allow_manhattan_connections
|
|
||||||
if hasattr(coordiniates_or_tiles, 'positions'):
|
|
||||||
coordiniates_or_tiles = coordiniates_or_tiles.positions
|
|
||||||
possible_connections = itertools.combinations(coordiniates_or_tiles, 2)
|
|
||||||
graph = nx.Graph()
|
|
||||||
for a, b in possible_connections:
|
|
||||||
diff = np.linalg.norm(np.asarray(a)-np.asarray(b))
|
|
||||||
if allow_manhattan_connections and allow_euclidean_connections and diff <= np.sqrt(2):
|
|
||||||
graph.add_edge(a, b)
|
|
||||||
elif not allow_manhattan_connections and allow_euclidean_connections and diff == np.sqrt(2):
|
|
||||||
graph.add_edge(a, b)
|
|
||||||
elif allow_manhattan_connections and not allow_euclidean_connections and diff == 1:
|
|
||||||
graph.add_edge(a, b)
|
|
||||||
return graph
|
|
||||||
|
|
||||||
|
|
||||||
def locate_and_import_class(class_name, folder_path: Union[str, PurePath] = ''):
|
def locate_and_import_class(class_name, folder_path: Union[str, PurePath] = ''):
|
||||||
"""Locate an object by name or dotted path, importing as necessary."""
|
"""Locate an object by name or dotted path, importing as necessary."""
|
||||||
import sys
|
import sys
|
||||||
sys.path.append("..")
|
sys.path.append("..")
|
||||||
folder_path = Path(folder_path)
|
folder_path = Path(folder_path).resolve()
|
||||||
module_paths = [x for x in folder_path.rglob('*.py') if x.is_file() and '__init__' not in x.name]
|
module_paths = [x.resolve() for x in folder_path.rglob('*.py') if x.is_file() and '__init__' not in x.name]
|
||||||
# possible_package_path = folder_path / '__init__.py'
|
# possible_package_path = folder_path / '__init__.py'
|
||||||
# package = str(possible_package_path) if possible_package_path.exists() else None
|
# package = str(possible_package_path) if possible_package_path.exists() else None
|
||||||
all_found_modules = list()
|
all_found_modules = list()
|
||||||
|
package_pos = next(idx for idx, x in enumerate(Path(__file__).resolve().parts) if x == 'environment')
|
||||||
for module_path in module_paths:
|
for module_path in module_paths:
|
||||||
mod = importlib.import_module('.'.join([x.replace('.py', '') for x in module_path.parts]))
|
module_parts = [x.replace('.py', '') for idx, x in enumerate(module_path.parts) if idx >= package_pos]
|
||||||
|
mod = importlib.import_module('.'.join(module_parts))
|
||||||
all_found_modules.extend([x for x in dir(mod) if not(x.startswith('__') or len(x) < 2 or x.isupper())
|
all_found_modules.extend([x for x in dir(mod) if not(x.startswith('__') or len(x) < 2 or x.isupper())
|
||||||
and x not in ['Entity', 'NamedTuple', 'List', 'Rule', 'Union', 'random', 'Floor'
|
and x not in ['Entity', 'NamedTuple', 'List', 'Rule', 'Union', 'random', 'Floor'
|
||||||
'TickResult', 'ActionResult', 'Action', 'Agent', 'deque',
|
'TickResult', 'ActionResult', 'Action', 'Agent', 'deque',
|
||||||
'BoundEntityMixin', 'RenderEntity', 'TemplateRule', 'defaultdict',
|
'BoundEntityMixin', 'RenderEntity', 'TemplateRule', 'defaultdict',
|
||||||
'is_move', 'Objects', 'PositionMixin', 'IsBoundMixin', 'EnvObject',
|
'is_move', 'Objects', 'PositionMixin', 'IsBoundMixin', 'EnvObject',
|
||||||
'EnvObjects',]])
|
'EnvObjects']])
|
||||||
try:
|
try:
|
||||||
model_class = mod.__getattribute__(class_name)
|
model_class = mod.__getattribute__(class_name)
|
||||||
return model_class
|
return model_class
|
||||||
|
@ -191,6 +191,9 @@ class RayCaster:
|
|||||||
self.ray_targets = self.build_ray_targets()
|
self.ray_targets = self.build_ray_targets()
|
||||||
self.obs_shape_cube = np.array([self.pomdp_r, self.pomdp_r])
|
self.obs_shape_cube = np.array([self.pomdp_r, self.pomdp_r])
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'{self.__class__.__name__}({self.agent.name})'
|
||||||
|
|
||||||
def build_ray_targets(self):
|
def build_ray_targets(self):
|
||||||
north = np.array([0, -1])*self.pomdp_r
|
north = np.array([0, -1])*self.pomdp_r
|
||||||
thetas = [np.deg2rad(deg) for deg in np.linspace(-self.degs // 2, self.degs // 2, self.n_rays)[::-1]]
|
thetas = [np.deg2rad(deg) for deg in np.linspace(-self.degs // 2, self.degs // 2, self.n_rays)[::-1]]
|
||||||
@ -202,12 +205,9 @@ class RayCaster:
|
|||||||
rot_M = np.unique(np.round(rot_M @ north), axis=0)
|
rot_M = np.unique(np.round(rot_M @ north), axis=0)
|
||||||
return rot_M.astype(int)
|
return rot_M.astype(int)
|
||||||
|
|
||||||
@staticmethod
|
def ray_block_cache(self, cache_dict, key, callback, ents):
|
||||||
def ray_block_cache(cache_dict, key, callback, ents):
|
|
||||||
if key not in cache_dict:
|
if key not in cache_dict:
|
||||||
cache_dict[key] = callback()
|
cache_dict[key] = callback()
|
||||||
if any(True for e in ents.pos_dict[key] if e.is_blocking_light) and not cache_dict[key]:
|
|
||||||
print()
|
|
||||||
return cache_dict[key]
|
return cache_dict[key]
|
||||||
|
|
||||||
def visible_entities(self, entities):
|
def visible_entities(self, entities):
|
||||||
@ -222,14 +222,23 @@ class RayCaster:
|
|||||||
entities_hit = entities.pos_dict[(x, y)]
|
entities_hit = entities.pos_dict[(x, y)]
|
||||||
hits = self.ray_block_cache(cache_blocking,
|
hits = self.ray_block_cache(cache_blocking,
|
||||||
(x, y),
|
(x, y),
|
||||||
lambda: any(True for e in entities_hit if e.is_blocking_light),
|
lambda: any(e.is_blocking_light for e in entities_hit),
|
||||||
entities)
|
entities)
|
||||||
|
|
||||||
diag_hits = all([
|
try:
|
||||||
|
d = next(x for x in entities_hit if 'Door' in x.name)
|
||||||
|
if d.pos in entities.pos_dict.keys():
|
||||||
|
if d.is_closed and not entities.pos_dict[d.pos]:
|
||||||
|
print()
|
||||||
|
except StopIteration:
|
||||||
|
pass
|
||||||
|
|
||||||
|
diag_hits = any([
|
||||||
self.ray_block_cache(
|
self.ray_block_cache(
|
||||||
cache_blocking,
|
cache_blocking,
|
||||||
key,
|
key,
|
||||||
lambda: all(False for e in entities.pos_dict[key] if not e.is_blocking_light),
|
# lambda: all(False for e in entities.pos_dict[key] if not e.is_blocking_light),
|
||||||
|
lambda: any(e.is_blocking_light for e in entities.pos_dict[key]),
|
||||||
entities)
|
entities)
|
||||||
for key in ((x, y-cy), (x-cx, y))
|
for key in ((x, y-cy), (x-cx, y))
|
||||||
]) if (cx != 0 and cy != 0) else False
|
]) if (cx != 0 and cy != 0) else False
|
||||||
@ -238,13 +247,6 @@ class RayCaster:
|
|||||||
if hits or diag_hits:
|
if hits or diag_hits:
|
||||||
break
|
break
|
||||||
rx, ry = x, y
|
rx, ry = x, y
|
||||||
try:
|
|
||||||
d = next(x for x in visible if 'Door' in x.name)
|
|
||||||
v = [x for x in visible if tuple(np.subtract(x.pos, d.pos)) in [(1, 0), (0, 1), (-1, 0), (0, -1)] and x.name.startswith('Floor')]
|
|
||||||
if len(v) > 2:
|
|
||||||
pass
|
|
||||||
except StopIteration:
|
|
||||||
pass
|
|
||||||
return visible
|
return visible
|
||||||
|
|
||||||
def get_rays(self):
|
def get_rays(self):
|
||||||
|
@ -3,12 +3,12 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
import pygame
|
import pygame
|
||||||
from typing import Tuple, Union
|
from typing import Tuple, Union
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import torch
|
|
||||||
|
|
||||||
from environment.utils.render import RenderEntity
|
from environment.utils.render import RenderEntity
|
||||||
|
|
||||||
AGENT: str = 'agent'
|
AGENT: str = 'agent'
|
||||||
@ -133,7 +133,8 @@ class Renderer:
|
|||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
self.clock.tick(self.fps)
|
self.clock.tick(self.fps)
|
||||||
rgb_obs = pygame.surfarray.array3d(self.screen)
|
rgb_obs = pygame.surfarray.array3d(self.screen)
|
||||||
return torch.from_numpy(rgb_obs).permute(2, 0, 1)
|
return np.transpose(rgb_obs, (2, 0, 1))
|
||||||
|
# return torch.from_numpy(rgb_obs).permute(2, 0, 1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -29,8 +29,9 @@ Entities:
|
|||||||
Agents:
|
Agents:
|
||||||
Wolfgang:
|
Wolfgang:
|
||||||
Actions:
|
Actions:
|
||||||
- Move8
|
- Noop
|
||||||
- DoorUse
|
- Noop
|
||||||
|
- Noop
|
||||||
- CleanUp
|
- CleanUp
|
||||||
Observations:
|
Observations:
|
||||||
- Self
|
- Self
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
numpy~=1.24.3
|
|
||||||
tqdm~=4.60.0
|
|
||||||
pandas~=1.2.3
|
|
||||||
seaborn~=0.12.2
|
|
||||||
matplotlib>=3.3.4
|
|
||||||
pygame~=2.0.1
|
|
||||||
networkx>=2.6.3
|
|
||||||
simplejson~=3.17.2
|
|
||||||
PyYAML~=5.3.1
|
|
||||||
natsort~=7.1.1
|
|
||||||
torch~=1.10.0
|
|
||||||
gymnasium~=0.28.1
|
|
21
setup.py
21
setup.py
@ -4,29 +4,34 @@ this_directory = Path(__file__).parent
|
|||||||
long_description = (this_directory / "README.md").read_text()
|
long_description = (this_directory / "README.md").read_text()
|
||||||
|
|
||||||
|
|
||||||
setup(name='Marl-Neon-Grid',
|
setup(name='Marl-Factory-Grid',
|
||||||
version='0.1.4.4',
|
version='0.0.5',
|
||||||
description='A collection of MARL gridworlds to study coordination and cooperation.',
|
description='A framework to research MARL agents in various setings.',
|
||||||
author='Robert Müller',
|
author='Steffen Illium',
|
||||||
author_email='steffen.illium@ifi.lmu.de',
|
author_email='steffen.illium@ifi.lmu.de',
|
||||||
url='https://github.com/romue404/marl-neon-grid',
|
url='https://github.com/illiumst/marl-factory-grid/import',
|
||||||
license='MIT',
|
license='MIT',
|
||||||
keywords=[
|
keywords=[
|
||||||
'artificial intelligence',
|
'artificial intelligence',
|
||||||
'pytorch',
|
'pytorch',
|
||||||
'multiagent reinforcement learning',
|
'multiagent reinforcement learning',
|
||||||
'simulation'
|
'simulation',
|
||||||
|
'emergence',
|
||||||
|
'gymnasium',
|
||||||
|
'environment',
|
||||||
|
|
||||||
],
|
],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 4 - Beta',
|
'Development Status :: 4 - Beta',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'Topic :: Scientific/Engineering :: Artificial Intelligence',
|
'Topic :: Scientific/Engineering :: Artificial Intelligence',
|
||||||
'License :: OSI Approved :: MIT License',
|
'License :: OSI Approved :: MIT License',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.11',
|
||||||
],
|
],
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
packages=find_packages(exclude=['examples']),
|
packages=find_packages(exclude=['examples']),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=['numpy', 'pygame>=2.0', 'numba>=0.56', 'gymnasium>=0.26', 'seaborn', 'pandas']
|
install_requires=['numpy', 'pygame>=2.0', 'numba>=0.56', 'gymnasium>=0.26', 'seaborn', 'pandas',
|
||||||
|
'pyyaml', 'networkx', 'torch', 'tqdm']
|
||||||
)
|
)
|
Reference in New Issue
Block a user