cogames-agents 0.0.1__tar.gz → 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cogames_agents-0.0.2/MANIFEST.in +7 -0
- cogames_agents-0.0.2/PKG-INFO +66 -0
- cogames_agents-0.0.2/README.md +47 -0
- cogames_agents-0.0.2/pyproject.toml +57 -0
- cogames_agents-0.0.2/setup.py +120 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/cogsguard_roles.py +31 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/cogsguard_teacher.py +223 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/__init__.py +18 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/agents.py +82 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/cogsguard_agents.nim +440 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/common.nim +905 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/ladybug_agent.nim +984 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/nim_agents.nim +61 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/nim_agents.nims +14 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/nimby.lock +3 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/racecar_agents.nim +884 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/random_agents.nim +68 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/test_agents.py +53 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/thinky_agents.nim +717 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/thinky_eval.py +233 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/baseline_agent.py +1039 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/__init__.py +5 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/aligner.py +283 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/debug_agent.py +536 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/miner.py +549 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/options.py +67 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/policy.py +1244 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/scout.py +152 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/scrambler.py +303 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/types.py +369 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/demo_policy.py +245 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/pathfinding.py +126 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/types.py +239 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/unclipping_agent.py +468 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/utils.py +414 -0
- cogames_agents-0.0.2/src/cogames_agents/policy/scripted_registry.py +28 -0
- cogames_agents-0.0.2/src/cogames_agents/py.typed +0 -0
- cogames_agents-0.0.2/src/cogames_agents.egg-info/PKG-INFO +66 -0
- cogames_agents-0.0.2/src/cogames_agents.egg-info/SOURCES.txt +46 -0
- cogames_agents-0.0.2/src/cogames_agents.egg-info/requires.txt +3 -0
- cogames_agents-0.0.2/tests/test_cogsguard_roles.py +151 -0
- cogames_agents-0.0.2/tests/test_scripted_registry.py +19 -0
- cogames_agents-0.0.1/PKG-INFO +0 -10
- cogames_agents-0.0.1/README.md +0 -3
- cogames_agents-0.0.1/pyproject.toml +0 -17
- cogames_agents-0.0.1/src/cogames_agents.egg-info/PKG-INFO +0 -10
- cogames_agents-0.0.1/src/cogames_agents.egg-info/SOURCES.txt +0 -9
- {cogames_agents-0.0.1 → cogames_agents-0.0.2}/setup.cfg +0 -0
- {cogames_agents-0.0.1 → cogames_agents-0.0.2}/src/cogames_agents/__init__.py +0 -0
- {cogames_agents-0.0.1 → cogames_agents-0.0.2}/src/cogames_agents/policy/__init__.py +0 -0
- /cogames_agents-0.0.1/src/cogames_agents/py.typed → /cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/__init__.py +0 -0
- {cogames_agents-0.0.1 → cogames_agents-0.0.2}/src/cogames_agents.egg-info/dependency_links.txt +0 -0
- {cogames_agents-0.0.1 → cogames_agents-0.0.2}/src/cogames_agents.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Include Nim source files for building from source distribution
|
|
2
|
+
recursive-include src/cogames_agents/policy/nim_agents *.nim *.nims
|
|
3
|
+
include src/cogames_agents/policy/nim_agents/nimby.lock
|
|
4
|
+
|
|
5
|
+
# Exclude build artifacts
|
|
6
|
+
prune src/cogames_agents/policy/nim_agents/nimbledeps
|
|
7
|
+
prune src/cogames_agents/policy/nim_agents/bindings/generated
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cogames-agents
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Optional agent policies for CoGames
|
|
5
|
+
Author: Metta AI
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Metta-AI/metta/tree/main/packages/cogames-agents
|
|
8
|
+
Project-URL: Repository, https://github.com/Metta-AI/metta
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Requires-Python: <3.13,>=3.12
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: cogames>=0.0.1
|
|
17
|
+
Requires-Dist: mettagrid==0.2.0.58
|
|
18
|
+
Requires-Dist: numpy>=2.0.0
|
|
19
|
+
|
|
20
|
+
# cogames-agents
|
|
21
|
+
|
|
22
|
+
Optional scripted policies for CoGames. Use them for quick baselines, play/eval smoke tests, or as teacher policies.
|
|
23
|
+
|
|
24
|
+
## Scripted policy registry
|
|
25
|
+
|
|
26
|
+
The registry at `cogames_agents.policy.scripted_registry` maps short names to `metta://policy/...` URIs. Scripted agents
|
|
27
|
+
and teachers share these identifiers, so the same name works for evaluation, play, and `TeacherConfig.policy_uri`.
|
|
28
|
+
|
|
29
|
+
Available scripted policy names:
|
|
30
|
+
|
|
31
|
+
- `baseline`
|
|
32
|
+
- `ladybug`
|
|
33
|
+
- `thinky`
|
|
34
|
+
- `race_car`
|
|
35
|
+
- `nim_random`
|
|
36
|
+
- `cogsguard`
|
|
37
|
+
- `cogsguard_py`
|
|
38
|
+
- `teacher`
|
|
39
|
+
- `miner`
|
|
40
|
+
- `scout`
|
|
41
|
+
- `aligner`
|
|
42
|
+
- `scrambler`
|
|
43
|
+
|
|
44
|
+
Role-specific policies are exposed via role names (miner/scout/aligner/scrambler). For the teacher policy, you can pass
|
|
45
|
+
`role_vibes` as a comma-separated list:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
metta://policy/teacher?role_vibes=miner,scout
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Recipe usage
|
|
52
|
+
|
|
53
|
+
The `recipes.experiment.scripted_agents` recipe accepts the same scripted policy names:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
./tools/run.py recipes.experiment.scripted_agents.play agent=thinky suite=cvc_arena
|
|
57
|
+
./tools/run.py recipes.experiment.scripted_agents.play agent=miner suite=cogsguard
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Included policies
|
|
61
|
+
|
|
62
|
+
- Nim policies (short names; default when both exist): `thinky`, `nim_random`, `race_car`, `ladybug`, `cogsguard`
|
|
63
|
+
- Python scripted policies (use `_py` only when Nim exists): `baseline`, `tiny_baseline`, `ladybug_py`, `cogsguard_py`
|
|
64
|
+
- Core scripted policy (in `cogames`): `starter`
|
|
65
|
+
- Scripted roles: `miner`, `scout`, `aligner`, `scrambler`
|
|
66
|
+
- Teacher wrapper: `teacher` (forces an initial role/vibe, then delegates to the Nim policy)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# cogames-agents
|
|
2
|
+
|
|
3
|
+
Optional scripted policies for CoGames. Use them for quick baselines, play/eval smoke tests, or as teacher policies.
|
|
4
|
+
|
|
5
|
+
## Scripted policy registry
|
|
6
|
+
|
|
7
|
+
The registry at `cogames_agents.policy.scripted_registry` maps short names to `metta://policy/...` URIs. Scripted agents
|
|
8
|
+
and teachers share these identifiers, so the same name works for evaluation, play, and `TeacherConfig.policy_uri`.
|
|
9
|
+
|
|
10
|
+
Available scripted policy names:
|
|
11
|
+
|
|
12
|
+
- `baseline`
|
|
13
|
+
- `ladybug`
|
|
14
|
+
- `thinky`
|
|
15
|
+
- `race_car`
|
|
16
|
+
- `nim_random`
|
|
17
|
+
- `cogsguard`
|
|
18
|
+
- `cogsguard_py`
|
|
19
|
+
- `teacher`
|
|
20
|
+
- `miner`
|
|
21
|
+
- `scout`
|
|
22
|
+
- `aligner`
|
|
23
|
+
- `scrambler`
|
|
24
|
+
|
|
25
|
+
Role-specific policies are exposed via role names (miner/scout/aligner/scrambler). For the teacher policy, you can pass
|
|
26
|
+
`role_vibes` as a comma-separated list:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
metta://policy/teacher?role_vibes=miner,scout
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Recipe usage
|
|
33
|
+
|
|
34
|
+
The `recipes.experiment.scripted_agents` recipe accepts the same scripted policy names:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
./tools/run.py recipes.experiment.scripted_agents.play agent=thinky suite=cvc_arena
|
|
38
|
+
./tools/run.py recipes.experiment.scripted_agents.play agent=miner suite=cogsguard
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Included policies
|
|
42
|
+
|
|
43
|
+
- Nim policies (short names; default when both exist): `thinky`, `nim_random`, `race_car`, `ladybug`, `cogsguard`
|
|
44
|
+
- Python scripted policies (use `_py` only when Nim exists): `baseline`, `tiny_baseline`, `ladybug_py`, `cogsguard_py`
|
|
45
|
+
- Core scripted policy (in `cogames`): `starter`
|
|
46
|
+
- Scripted roles: `miner`, `scout`, `aligner`, `scrambler`
|
|
47
|
+
- Teacher wrapper: `teacher` (forces an initial role/vibe, then delegates to the Nim policy)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools==80.9.0", "wheel==0.45.1"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cogames-agents"
|
|
7
|
+
version = "0.0.2"
|
|
8
|
+
description = "Optional agent policies for CoGames"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12,<3.13"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Metta AI" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: POSIX :: Linux",
|
|
18
|
+
"Operating System :: MacOS",
|
|
19
|
+
]
|
|
20
|
+
urls = { Homepage = "https://github.com/Metta-AI/metta/tree/main/packages/cogames-agents", Repository = "https://github.com/Metta-AI/metta" }
|
|
21
|
+
dependencies = ["cogames>=0.0.1", "mettagrid==0.2.0.58", "numpy>=2.0.0"]
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
where = ["src"]
|
|
25
|
+
include = ["cogames_agents", "cogames_agents.*"]
|
|
26
|
+
|
|
27
|
+
[tool.setuptools]
|
|
28
|
+
include-package-data = true
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.package-data]
|
|
31
|
+
"cogames_agents" = [
|
|
32
|
+
"py.typed",
|
|
33
|
+
# Nim source files for building
|
|
34
|
+
"policy/nim_agents/*.nim",
|
|
35
|
+
"policy/nim_agents/*.nims",
|
|
36
|
+
"policy/nim_agents/nimby.lock",
|
|
37
|
+
# Compiled Nim binaries
|
|
38
|
+
"policy/nim_agents/bindings/generated/*.so",
|
|
39
|
+
"policy/nim_agents/bindings/generated/*.dylib",
|
|
40
|
+
"policy/nim_agents/bindings/generated/*.dll",
|
|
41
|
+
"policy/nim_agents/bindings/generated/*.py",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.uv]
|
|
45
|
+
cache-keys = [
|
|
46
|
+
{ file = "src/cogames_agents/policy/nim_agents/nim_agents.nim" },
|
|
47
|
+
{ file = "src/cogames_agents/policy/nim_agents/nim_agents.nims" },
|
|
48
|
+
{ file = "src/cogames_agents/policy/nim_agents/common.nim" },
|
|
49
|
+
{ file = "src/cogames_agents/policy/nim_agents/random_agents.nim" },
|
|
50
|
+
{ file = "src/cogames_agents/policy/nim_agents/thinky_agents.nim" },
|
|
51
|
+
{ file = "src/cogames_agents/policy/nim_agents/racecar_agents.nim" },
|
|
52
|
+
{ file = "src/cogames_agents/policy/nim_agents/cogsguard_agents.nim" },
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.uv.sources]
|
|
56
|
+
cogames = { workspace = true }
|
|
57
|
+
mettagrid = { workspace = true }
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import platform
|
|
6
|
+
import shutil
|
|
7
|
+
import stat
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
import tempfile
|
|
11
|
+
import urllib.request
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from setuptools import setup
|
|
15
|
+
from setuptools.command.build_py import build_py
|
|
16
|
+
from setuptools.command.develop import develop
|
|
17
|
+
from setuptools.command.install import install
|
|
18
|
+
from setuptools.dist import Distribution
|
|
19
|
+
|
|
20
|
+
NIM_AGENTS_DIR = Path(__file__).parent / "src" / "cogames_agents" / "policy" / "nim_agents"
|
|
21
|
+
NIMBY_LOCK = NIM_AGENTS_DIR / "nimby.lock"
|
|
22
|
+
BINDINGS_DIR = NIM_AGENTS_DIR / "bindings" / "generated"
|
|
23
|
+
REQUIRED_NIM_VERSION = os.environ.get("COGAMES_NIM_VERSION", "2.2.6")
|
|
24
|
+
NIMBY_VERSION = os.environ.get("COGAMES_NIMBY_VERSION", "0.1.13")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _get_nimby_url() -> str | None:
|
|
28
|
+
"""Get the nimby download URL for the current platform, or None if not supported."""
|
|
29
|
+
system = platform.system()
|
|
30
|
+
arch = platform.machine()
|
|
31
|
+
|
|
32
|
+
if system == "Linux" and arch == "x86_64":
|
|
33
|
+
return f"https://github.com/treeform/nimby/releases/download/{NIMBY_VERSION}/nimby-Linux-X64"
|
|
34
|
+
elif system == "Darwin" and arch == "arm64":
|
|
35
|
+
return f"https://github.com/treeform/nimby/releases/download/{NIMBY_VERSION}/nimby-macOS-ARM64"
|
|
36
|
+
elif system == "Darwin" and arch == "x86_64":
|
|
37
|
+
return f"https://github.com/treeform/nimby/releases/download/{NIMBY_VERSION}/nimby-macOS-X64"
|
|
38
|
+
else:
|
|
39
|
+
# For unsupported platforms (e.g., Linux ARM64), nimby must be pre-installed
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _build_nim() -> None:
|
|
44
|
+
system = platform.system()
|
|
45
|
+
arch = platform.machine()
|
|
46
|
+
|
|
47
|
+
nimby_url = _get_nimby_url()
|
|
48
|
+
nim_bin_dir = Path.home() / ".nimby" / "nim" / "bin"
|
|
49
|
+
|
|
50
|
+
if nimby_url is not None:
|
|
51
|
+
# Download and install nimby
|
|
52
|
+
dst = nim_bin_dir / "nimby"
|
|
53
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
54
|
+
nimby = Path(tmp) / "nimby"
|
|
55
|
+
urllib.request.urlretrieve(nimby_url, nimby)
|
|
56
|
+
nimby.chmod(nimby.stat().st_mode | stat.S_IEXEC)
|
|
57
|
+
subprocess.check_call([str(nimby), "use", REQUIRED_NIM_VERSION])
|
|
58
|
+
|
|
59
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
shutil.copy2(nimby, dst)
|
|
61
|
+
|
|
62
|
+
os.environ["PATH"] = f"{dst.parent}{os.pathsep}" + os.environ.get("PATH", "")
|
|
63
|
+
else:
|
|
64
|
+
# For unsupported platforms, assume nim/nimby are pre-installed
|
|
65
|
+
if shutil.which("nim") is None:
|
|
66
|
+
raise RuntimeError(
|
|
67
|
+
f"Nim is not installed and nimby download is not available for {system} {arch}. "
|
|
68
|
+
"Please install Nim manually (https://nim-lang.org/install.html) or build nimby from source."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Ensure nim/nimble binaries installed by nimby are discoverable by subprocesses.
|
|
72
|
+
os.environ["PATH"] = f"{nim_bin_dir}{os.pathsep}" + os.environ.get("PATH", "")
|
|
73
|
+
|
|
74
|
+
# Sync Nim dependencies
|
|
75
|
+
if NIMBY_LOCK.exists():
|
|
76
|
+
if shutil.which("nimby") is not None:
|
|
77
|
+
subprocess.check_call(["nimby", "sync", "-g", str(NIMBY_LOCK)], cwd=NIM_AGENTS_DIR)
|
|
78
|
+
else:
|
|
79
|
+
# nimby not available (unsupported platform with manual Nim install) - use nimble directly
|
|
80
|
+
subprocess.check_call(["nimble", "install", "-y"], cwd=NIM_AGENTS_DIR)
|
|
81
|
+
|
|
82
|
+
# Create output directory for compiled binaries
|
|
83
|
+
BINDINGS_DIR.mkdir(parents=True, exist_ok=True)
|
|
84
|
+
|
|
85
|
+
# Compile Nim agents
|
|
86
|
+
result = subprocess.run(["nim", "c", "nim_agents.nim"], cwd=NIM_AGENTS_DIR, capture_output=True, text=True)
|
|
87
|
+
if result.returncode != 0:
|
|
88
|
+
print(result.stderr, file=sys.stderr)
|
|
89
|
+
print(result.stdout, file=sys.stderr)
|
|
90
|
+
raise RuntimeError(f"Failed to build Nim agents: {result.returncode}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class _EnsureNimMixin:
|
|
94
|
+
def run(self, *args, **kwargs): # type: ignore[override]
|
|
95
|
+
_build_nim()
|
|
96
|
+
super().run(*args, **kwargs) # type: ignore[misc]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class BuildPyCommand(_EnsureNimMixin, build_py): ...
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class DevelopCommand(_EnsureNimMixin, develop): ...
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class InstallCommand(_EnsureNimMixin, install): ...
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class BinaryDistribution(Distribution):
|
|
109
|
+
def has_ext_modules(self) -> bool:
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
setup(
|
|
114
|
+
cmdclass={
|
|
115
|
+
"build_py": BuildPyCommand,
|
|
116
|
+
"develop": DevelopCommand,
|
|
117
|
+
"install": InstallCommand,
|
|
118
|
+
},
|
|
119
|
+
distclass=BinaryDistribution,
|
|
120
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from cogames_agents.policy.scripted_agent.cogsguard.policy import CogsguardPolicy
|
|
4
|
+
from mettagrid.policy.policy_env_interface import PolicyEnvInterface
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class _CogsguardRolePolicy(CogsguardPolicy):
|
|
8
|
+
role_name: str = ""
|
|
9
|
+
|
|
10
|
+
def __init__(self, policy_env_info: PolicyEnvInterface, device: str = "cpu", **_ignored: int) -> None:
|
|
11
|
+
super().__init__(policy_env_info, device=device, **{self.role_name: policy_env_info.num_agents})
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MinerPolicy(_CogsguardRolePolicy):
|
|
15
|
+
short_names = ["miner"]
|
|
16
|
+
role_name = "miner"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ScoutPolicy(_CogsguardRolePolicy):
|
|
20
|
+
short_names = ["scout"]
|
|
21
|
+
role_name = "scout"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AlignerPolicy(_CogsguardRolePolicy):
|
|
25
|
+
short_names = ["aligner"]
|
|
26
|
+
role_name = "aligner"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ScramblerPolicy(_CogsguardRolePolicy):
|
|
30
|
+
short_names = ["scrambler"]
|
|
31
|
+
role_name = "scrambler"
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Sequence
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from cogames_agents.policy.nim_agents.agents import CogsguardAgentsMultiPolicy
|
|
8
|
+
from cogames_agents.policy.scripted_agent.cogsguard.types import Role as CogsguardRole
|
|
9
|
+
from mettagrid.policy.policy import AgentPolicy, MultiAgentPolicy
|
|
10
|
+
from mettagrid.policy.policy_env_interface import PolicyEnvInterface
|
|
11
|
+
from mettagrid.simulator import Action, AgentObservation
|
|
12
|
+
|
|
13
|
+
DEFAULT_ROLE_VIBES = ("miner", "scout", "aligner", "scrambler")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CogsguardTeacherPolicy(MultiAgentPolicy):
|
|
17
|
+
"""Teacher wrapper that forces an initial vibe, then delegates to the Nim policy."""
|
|
18
|
+
|
|
19
|
+
short_names = ["teacher"]
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
policy_env_info: PolicyEnvInterface,
|
|
24
|
+
device: str = "cpu",
|
|
25
|
+
role_vibes: Optional[Sequence[str | CogsguardRole] | str] = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
super().__init__(policy_env_info, device=device)
|
|
28
|
+
self._delegate = CogsguardAgentsMultiPolicy(policy_env_info)
|
|
29
|
+
self._num_agents = policy_env_info.num_agents
|
|
30
|
+
self._action_names = list(policy_env_info.action_names)
|
|
31
|
+
self._action_name_to_index = {name: idx for idx, name in enumerate(self._action_names)}
|
|
32
|
+
self._delegate_agents = [self._delegate.agent_policy(i) for i in range(self._num_agents)]
|
|
33
|
+
|
|
34
|
+
self._episode_feature_id = self._find_feature_id("episode_completion_pct")
|
|
35
|
+
self._last_action_feature_id = self._find_feature_id("last_action")
|
|
36
|
+
|
|
37
|
+
self._role_action_ids = self._resolve_role_actions(role_vibes)
|
|
38
|
+
self._episode_index = [0] * self._num_agents
|
|
39
|
+
self._reset_episode_state()
|
|
40
|
+
|
|
41
|
+
def agent_policy(self, agent_id: int) -> AgentPolicy:
|
|
42
|
+
return _CogsguardTeacherAgentPolicy(self, agent_id)
|
|
43
|
+
|
|
44
|
+
def reset(self) -> None:
|
|
45
|
+
self._delegate.reset()
|
|
46
|
+
self._reset_episode_state()
|
|
47
|
+
|
|
48
|
+
def step_batch(self, raw_observations: np.ndarray, raw_actions: np.ndarray) -> None:
|
|
49
|
+
self._delegate.step_batch(raw_observations, raw_actions)
|
|
50
|
+
if not self._role_action_ids:
|
|
51
|
+
return
|
|
52
|
+
if raw_observations.shape[0] != self._num_agents:
|
|
53
|
+
return
|
|
54
|
+
for agent_id in range(self._num_agents):
|
|
55
|
+
episode_pct = self._extract_episode_pct_raw(raw_observations[agent_id])
|
|
56
|
+
last_action = self._extract_last_action_raw(raw_observations[agent_id])
|
|
57
|
+
forced_action = self._maybe_force_action(agent_id, episode_pct, last_action)
|
|
58
|
+
if forced_action is not None:
|
|
59
|
+
raw_actions[agent_id] = forced_action
|
|
60
|
+
|
|
61
|
+
def _step_single(self, agent_id: int, obs: AgentObservation) -> Action:
|
|
62
|
+
base_action = self._delegate_agents[agent_id].step(obs)
|
|
63
|
+
if not self._role_action_ids:
|
|
64
|
+
return base_action
|
|
65
|
+
episode_pct = self._extract_episode_pct_obs(obs)
|
|
66
|
+
last_action = self._extract_last_action_obs(obs)
|
|
67
|
+
forced_action = self._maybe_force_action(agent_id, episode_pct, last_action)
|
|
68
|
+
if forced_action is None:
|
|
69
|
+
return base_action
|
|
70
|
+
action_name = self._action_names[forced_action]
|
|
71
|
+
return Action(name=action_name)
|
|
72
|
+
|
|
73
|
+
def _extract_episode_pct_raw(self, raw_obs: np.ndarray) -> Optional[int]:
|
|
74
|
+
if self._episode_feature_id is None:
|
|
75
|
+
return None
|
|
76
|
+
for token in raw_obs:
|
|
77
|
+
if token[0] == 255 and token[1] == 255 and token[2] == 255:
|
|
78
|
+
break
|
|
79
|
+
if token[1] == self._episode_feature_id:
|
|
80
|
+
return int(token[2])
|
|
81
|
+
return 0
|
|
82
|
+
|
|
83
|
+
def _extract_episode_pct_obs(self, obs: AgentObservation) -> Optional[int]:
|
|
84
|
+
if self._episode_feature_id is None:
|
|
85
|
+
return None
|
|
86
|
+
for token in obs.tokens:
|
|
87
|
+
if token.feature.name == "episode_completion_pct":
|
|
88
|
+
return token.value
|
|
89
|
+
return 0
|
|
90
|
+
|
|
91
|
+
def _extract_last_action_raw(self, raw_obs: np.ndarray) -> Optional[int]:
|
|
92
|
+
if self._last_action_feature_id is None:
|
|
93
|
+
return None
|
|
94
|
+
for token in raw_obs:
|
|
95
|
+
if token[0] == 255 and token[1] == 255 and token[2] == 255:
|
|
96
|
+
break
|
|
97
|
+
if token[1] == self._last_action_feature_id:
|
|
98
|
+
return int(token[2])
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
def _extract_last_action_obs(self, obs: AgentObservation) -> Optional[int]:
|
|
102
|
+
if self._last_action_feature_id is None:
|
|
103
|
+
return None
|
|
104
|
+
for token in obs.tokens:
|
|
105
|
+
if token.feature.name == "last_action":
|
|
106
|
+
return token.value
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
def _find_feature_id(self, feature_name: str) -> Optional[int]:
|
|
110
|
+
for feature in self.policy_env_info.obs_features:
|
|
111
|
+
if feature.name == feature_name:
|
|
112
|
+
return feature.id
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
def _resolve_role_actions(self, role_vibes: Optional[Sequence[str | CogsguardRole] | str]) -> list[int]:
|
|
116
|
+
change_vibe_actions = [name for name in self._action_names if name.startswith("change_vibe_")]
|
|
117
|
+
if len(change_vibe_actions) <= 1:
|
|
118
|
+
return []
|
|
119
|
+
|
|
120
|
+
available_vibes = [name[len("change_vibe_") :] for name in change_vibe_actions]
|
|
121
|
+
if role_vibes is None:
|
|
122
|
+
role_vibes = [vibe for vibe in DEFAULT_ROLE_VIBES if vibe in available_vibes]
|
|
123
|
+
if not role_vibes:
|
|
124
|
+
role_vibes = [vibe for vibe in available_vibes if vibe != "default"]
|
|
125
|
+
if not role_vibes:
|
|
126
|
+
role_vibes = available_vibes
|
|
127
|
+
else:
|
|
128
|
+
if isinstance(role_vibes, str):
|
|
129
|
+
normalized_vibes = [vibe.strip() for vibe in role_vibes.split(",") if vibe.strip()]
|
|
130
|
+
else:
|
|
131
|
+
normalized_vibes = [vibe.value if isinstance(vibe, CogsguardRole) else str(vibe) for vibe in role_vibes]
|
|
132
|
+
role_vibes = [vibe for vibe in normalized_vibes if vibe in available_vibes]
|
|
133
|
+
if not role_vibes:
|
|
134
|
+
role_vibes = available_vibes
|
|
135
|
+
|
|
136
|
+
role_action_ids = []
|
|
137
|
+
for vibe_name in role_vibes:
|
|
138
|
+
action_name = f"change_vibe_{vibe_name}"
|
|
139
|
+
action_id = self._action_name_to_index.get(action_name)
|
|
140
|
+
if action_id is not None:
|
|
141
|
+
role_action_ids.append(action_id)
|
|
142
|
+
return role_action_ids
|
|
143
|
+
|
|
144
|
+
def _reset_episode_state(self) -> None:
|
|
145
|
+
self._forced_vibe = [False] * self._num_agents
|
|
146
|
+
self._last_episode_pct = [-1] * self._num_agents
|
|
147
|
+
self._step_in_episode = [0] * self._num_agents
|
|
148
|
+
self._last_action_value: list[Optional[int]] = [None] * self._num_agents
|
|
149
|
+
|
|
150
|
+
def _maybe_force_action(
|
|
151
|
+
self,
|
|
152
|
+
agent_id: int,
|
|
153
|
+
episode_pct: Optional[int],
|
|
154
|
+
last_action: Optional[int],
|
|
155
|
+
) -> Optional[int]:
|
|
156
|
+
self._update_episode_state(agent_id, episode_pct, last_action)
|
|
157
|
+
if self._forced_vibe[agent_id] or self._step_in_episode[agent_id] != 0:
|
|
158
|
+
return None
|
|
159
|
+
self._forced_vibe[agent_id] = True
|
|
160
|
+
role_index = (self._episode_index[agent_id] + agent_id) % len(self._role_action_ids)
|
|
161
|
+
return self._role_action_ids[role_index]
|
|
162
|
+
|
|
163
|
+
def _update_episode_state(
|
|
164
|
+
self,
|
|
165
|
+
agent_id: int,
|
|
166
|
+
episode_pct: Optional[int],
|
|
167
|
+
last_action: Optional[int],
|
|
168
|
+
) -> None:
|
|
169
|
+
last_pct = self._last_episode_pct[agent_id]
|
|
170
|
+
if episode_pct is None:
|
|
171
|
+
last_action_seen = self._last_action_value[agent_id]
|
|
172
|
+
if (
|
|
173
|
+
last_action is not None
|
|
174
|
+
and last_action_seen is not None
|
|
175
|
+
and last_action == 0
|
|
176
|
+
and last_action_seen != 0
|
|
177
|
+
and self._step_in_episode[agent_id] > 0
|
|
178
|
+
):
|
|
179
|
+
self._episode_index[agent_id] += 1
|
|
180
|
+
self._step_in_episode[agent_id] = 0
|
|
181
|
+
self._forced_vibe[agent_id] = False
|
|
182
|
+
self._last_episode_pct[agent_id] = 0
|
|
183
|
+
self._last_action_value[agent_id] = last_action
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
if last_pct == -1:
|
|
187
|
+
self._step_in_episode[agent_id] = 0
|
|
188
|
+
else:
|
|
189
|
+
self._step_in_episode[agent_id] += 1
|
|
190
|
+
self._last_episode_pct[agent_id] = 0
|
|
191
|
+
if last_action is not None:
|
|
192
|
+
self._last_action_value[agent_id] = last_action
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
new_episode = False
|
|
196
|
+
if last_pct == -1:
|
|
197
|
+
new_episode = True
|
|
198
|
+
elif episode_pct < last_pct:
|
|
199
|
+
new_episode = True
|
|
200
|
+
elif last_pct > 0 and episode_pct == 0:
|
|
201
|
+
new_episode = True
|
|
202
|
+
|
|
203
|
+
if new_episode:
|
|
204
|
+
if last_pct != -1:
|
|
205
|
+
self._episode_index[agent_id] += 1
|
|
206
|
+
self._step_in_episode[agent_id] = 0
|
|
207
|
+
self._forced_vibe[agent_id] = False
|
|
208
|
+
else:
|
|
209
|
+
self._step_in_episode[agent_id] += 1
|
|
210
|
+
|
|
211
|
+
self._last_episode_pct[agent_id] = episode_pct
|
|
212
|
+
if last_action is not None:
|
|
213
|
+
self._last_action_value[agent_id] = last_action
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class _CogsguardTeacherAgentPolicy(AgentPolicy):
|
|
217
|
+
def __init__(self, parent: CogsguardTeacherPolicy, agent_id: int) -> None:
|
|
218
|
+
super().__init__(parent.policy_env_info)
|
|
219
|
+
self._parent = parent
|
|
220
|
+
self._agent_id = agent_id
|
|
221
|
+
|
|
222
|
+
def step(self, obs: AgentObservation) -> Action:
|
|
223
|
+
return self._parent._step_single(self._agent_id, obs)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Nim-based agent policies for CoGames."""
|
|
2
|
+
|
|
3
|
+
from cogames_agents.policy.nim_agents import agents # noqa: F401
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"RandomAgentsMultiPolicy",
|
|
7
|
+
"ThinkyAgentsMultiPolicy",
|
|
8
|
+
"RaceCarAgentsMultiPolicy",
|
|
9
|
+
"LadyBugAgentsMultiPolicy",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
# Re-export the policy classes for convenience
|
|
13
|
+
from cogames_agents.policy.nim_agents.agents import ( # noqa: F401
|
|
14
|
+
LadyBugAgentsMultiPolicy,
|
|
15
|
+
RaceCarAgentsMultiPolicy,
|
|
16
|
+
RandomAgentsMultiPolicy,
|
|
17
|
+
ThinkyAgentsMultiPolicy,
|
|
18
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Sequence
|
|
5
|
+
|
|
6
|
+
from mettagrid.policy.policy import NimMultiAgentPolicy
|
|
7
|
+
from mettagrid.policy.policy_env_interface import PolicyEnvInterface
|
|
8
|
+
|
|
9
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
10
|
+
bindings_dir = os.path.join(current_dir, "bindings/generated")
|
|
11
|
+
if bindings_dir not in sys.path:
|
|
12
|
+
sys.path.append(bindings_dir)
|
|
13
|
+
|
|
14
|
+
na = importlib.import_module("nim_agents")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def start_measure():
|
|
18
|
+
na.start_measure()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def end_measure():
|
|
22
|
+
na.end_measure()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ThinkyAgentsMultiPolicy(NimMultiAgentPolicy):
|
|
26
|
+
short_names = ["thinky"]
|
|
27
|
+
|
|
28
|
+
def __init__(self, policy_env_info: PolicyEnvInterface, agent_ids: Sequence[int] | None = None):
|
|
29
|
+
super().__init__(
|
|
30
|
+
policy_env_info,
|
|
31
|
+
nim_policy_factory=na.ThinkyPolicy,
|
|
32
|
+
agent_ids=agent_ids,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RandomAgentsMultiPolicy(NimMultiAgentPolicy):
|
|
37
|
+
short_names = ["nim_random"]
|
|
38
|
+
|
|
39
|
+
def __init__(self, policy_env_info: PolicyEnvInterface, agent_ids: Sequence[int] | None = None):
|
|
40
|
+
super().__init__(
|
|
41
|
+
policy_env_info,
|
|
42
|
+
nim_policy_factory=na.RandomPolicy,
|
|
43
|
+
agent_ids=agent_ids,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class RaceCarAgentsMultiPolicy(NimMultiAgentPolicy):
|
|
48
|
+
short_names = ["race_car"]
|
|
49
|
+
|
|
50
|
+
def __init__(self, policy_env_info: PolicyEnvInterface, agent_ids: Sequence[int] | None = None):
|
|
51
|
+
super().__init__(
|
|
52
|
+
policy_env_info,
|
|
53
|
+
nim_policy_factory=na.RaceCarPolicy,
|
|
54
|
+
agent_ids=agent_ids,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class LadyBugAgentsMultiPolicy(NimMultiAgentPolicy):
|
|
59
|
+
short_names = ["ladybug"]
|
|
60
|
+
|
|
61
|
+
def __init__(self, policy_env_info: PolicyEnvInterface, agent_ids: Sequence[int] | None = None):
|
|
62
|
+
super().__init__(
|
|
63
|
+
policy_env_info,
|
|
64
|
+
nim_policy_factory=na.LadybugPolicy,
|
|
65
|
+
agent_ids=agent_ids,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class CogsguardAgentsMultiPolicy(NimMultiAgentPolicy):
|
|
70
|
+
short_names = ["cogsguard"]
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
policy_env_info: PolicyEnvInterface,
|
|
75
|
+
agent_ids: Sequence[int] | None = None,
|
|
76
|
+
**_: object,
|
|
77
|
+
):
|
|
78
|
+
super().__init__(
|
|
79
|
+
policy_env_info,
|
|
80
|
+
nim_policy_factory=na.CogsguardPolicy,
|
|
81
|
+
agent_ids=agent_ids,
|
|
82
|
+
)
|