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.
Files changed (53) hide show
  1. cogames_agents-0.0.2/MANIFEST.in +7 -0
  2. cogames_agents-0.0.2/PKG-INFO +66 -0
  3. cogames_agents-0.0.2/README.md +47 -0
  4. cogames_agents-0.0.2/pyproject.toml +57 -0
  5. cogames_agents-0.0.2/setup.py +120 -0
  6. cogames_agents-0.0.2/src/cogames_agents/policy/cogsguard_roles.py +31 -0
  7. cogames_agents-0.0.2/src/cogames_agents/policy/cogsguard_teacher.py +223 -0
  8. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/__init__.py +18 -0
  9. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/agents.py +82 -0
  10. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/cogsguard_agents.nim +440 -0
  11. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/common.nim +905 -0
  12. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/ladybug_agent.nim +984 -0
  13. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/nim_agents.nim +61 -0
  14. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/nim_agents.nims +14 -0
  15. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/nimby.lock +3 -0
  16. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/racecar_agents.nim +884 -0
  17. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/random_agents.nim +68 -0
  18. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/test_agents.py +53 -0
  19. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/thinky_agents.nim +717 -0
  20. cogames_agents-0.0.2/src/cogames_agents/policy/nim_agents/thinky_eval.py +233 -0
  21. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/baseline_agent.py +1039 -0
  22. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/__init__.py +5 -0
  23. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/aligner.py +283 -0
  24. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/debug_agent.py +536 -0
  25. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/miner.py +549 -0
  26. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/options.py +67 -0
  27. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/policy.py +1244 -0
  28. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/scout.py +152 -0
  29. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/scrambler.py +303 -0
  30. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/cogsguard/types.py +369 -0
  31. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/demo_policy.py +245 -0
  32. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/pathfinding.py +126 -0
  33. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/types.py +239 -0
  34. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/unclipping_agent.py +468 -0
  35. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_agent/utils.py +414 -0
  36. cogames_agents-0.0.2/src/cogames_agents/policy/scripted_registry.py +28 -0
  37. cogames_agents-0.0.2/src/cogames_agents/py.typed +0 -0
  38. cogames_agents-0.0.2/src/cogames_agents.egg-info/PKG-INFO +66 -0
  39. cogames_agents-0.0.2/src/cogames_agents.egg-info/SOURCES.txt +46 -0
  40. cogames_agents-0.0.2/src/cogames_agents.egg-info/requires.txt +3 -0
  41. cogames_agents-0.0.2/tests/test_cogsguard_roles.py +151 -0
  42. cogames_agents-0.0.2/tests/test_scripted_registry.py +19 -0
  43. cogames_agents-0.0.1/PKG-INFO +0 -10
  44. cogames_agents-0.0.1/README.md +0 -3
  45. cogames_agents-0.0.1/pyproject.toml +0 -17
  46. cogames_agents-0.0.1/src/cogames_agents.egg-info/PKG-INFO +0 -10
  47. cogames_agents-0.0.1/src/cogames_agents.egg-info/SOURCES.txt +0 -9
  48. {cogames_agents-0.0.1 → cogames_agents-0.0.2}/setup.cfg +0 -0
  49. {cogames_agents-0.0.1 → cogames_agents-0.0.2}/src/cogames_agents/__init__.py +0 -0
  50. {cogames_agents-0.0.1 → cogames_agents-0.0.2}/src/cogames_agents/policy/__init__.py +0 -0
  51. /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
  52. {cogames_agents-0.0.1 → cogames_agents-0.0.2}/src/cogames_agents.egg-info/dependency_links.txt +0 -0
  53. {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
+ )