Skip to content

Commit

Permalink
chore: Reset to stable state
Browse files Browse the repository at this point in the history
  • Loading branch information
strakam committed Jan 12, 2025
1 parent 0b9c0b6 commit 4c05741
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 489 deletions.
9 changes: 7 additions & 2 deletions .github/workflows/code-embedder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ name: Code Embedder

on: pull_request

permissions:
contents: write

jobs:
code_embedder:
name: "Code embedder"
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}

- name: Run code embedder
uses: kvankova/code-embedder@v0.4.0
uses: kvankova/code-embedder@v1.1.1
env:
GITHUB_TOKEN: ${{ secrets.CODE_EMBEDDER }}
8 changes: 0 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,6 @@ repos:
types:
- python

- id: mypy
name: mypy
entry: poetry run mypy .
pass_filenames: false
language: system
types:
- python

- id: run-pytest
name: Run tests
entry: make test
Expand Down
58 changes: 35 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,37 @@ pip install -e .
## 🌱 Getting Started
Creating an agent is very simple. Start by subclassing an `Agent` class just like
[`RandomAgent`](./generals/agents/random_agent.py) or [`ExpanderAgent`](./generals/agents/expander_agent.py).
You can specify your agent `id` (name) and `color` and the only thing remaining is to implement the `act` function,
You can specify your agent `id` (name) and the only thing remaining is to implement the `act` function,
that has the signature explained in sections down below.


### Usage Example (🤸 Gymnasium)
### Usage Example (🦁 PettingZoo)
The example loop for running the game looks like this
```python:examples/gymnasium_example.py
import gymnasium as gym

```python:examples/pettingzoo_example.py
from generals.agents import RandomAgent, ExpanderAgent
from generals.envs import PettingZooGenerals

# Initialize agents
agent = RandomAgent()
npc = ExpanderAgent()
random = RandomAgent(id="Random")
expander = ExpanderAgent(id="Expander")

# Store agents in a dictionary
agents = {
random.id: random,
expander.id: expander
}

# Create environment
env = gym.make("gym-generals-v0", agent=agent, npc=npc, render_mode="human")
env = PettingZooGenerals(agent_ids=[random.id, expander.id], to_render=True)
observations, info = env.reset()

observation, info = env.reset()
terminated = truncated = False
while not (terminated or truncated):
action = agent.act(observation)
observation, reward, terminated, truncated, info = env.step(action)
actions = {}
for agent in env.agent_ids:
actions[agent] = agents[agent].act(observations[agent])
# All agents perform their actions
observations, rewards, terminated, truncated, info = env.step(actions)
env.render()

```
Expand All @@ -80,7 +88,7 @@ while not (terminated or truncated):
Grids on which the game is played on are generated via `GridFactory`. You can instantiate the class with desired grid properties, and it will generate
grid with these properties for each run.
```python
import gymnasium as gym
from generals.envs import PettingZooGenerals
from generals import GridFactory

grid_factory = GridFactory(
Expand All @@ -89,21 +97,20 @@ grid_factory = GridFactory(
mountain_density=0.2, # Probability of a mountain in a cell
city_density=0.05, # Probability of a city in a cell
general_positions=[(0,3),(5,7)], # Positions of generals (i, j)
padding=True # Whether to pad grids to max_grid_dims with mountains (default=True)
)

# Create environment
env = gym.make(
"gym-generals-v0",
env = PettingZooGenerals(
grid_factory=grid_factory,
...
)
```
You can also specify grids manually, as a string via `options` dict:
```python
import gymnasium as gym
from generals.envs import PettingZooGenerals

env = PettingZooGenerals(agent_ids=[agent1.id, agent2.id])

env = gym.make("gym-generals-v0", ...)
grid = """
.3.#
#..A
Expand All @@ -123,13 +130,14 @@ Grids are created using a string format where:
- numbers `0-9` and `x`, where `x=10`, represent cities, where the number specifies amount of neutral army in the city,
which is calculated as `40 + number`. The reason for `x=10` is that the official game has cities in range `[40, 50]`

> [!TIP]
> Check out [complete example](./examples/complete_example.py) for concrete example in the wild!
## 🔬 Interactive Replays
We can store replays and then analyze them in an interactive fashion. `Replay` class handles replay related functionality.
### Storing a replay
```python
import gymnasium as gym

env = gym.make("gym-generals-v0", ...)
env = ...

options = {"replay_file": "my_replay"}
env.reset(options=options) # The next game will be encoded in my_replay.pkl
Expand Down Expand Up @@ -220,15 +228,19 @@ or create your own connection workflow. Our implementations expect that your age
implemented the required methods.
```python:examples/client_example.py
from generals.remote import autopilot
from generals.agents import ExpanderAgent
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--user_id", type=str, default=...) # Register yourself at generals.io and use this id
parser.add_argument("--lobby_id", type=str, default=...) # The last part of the lobby url
parser.add_argument("--agent_id", type=str, default="Expander") # agent_id should be "registered" in AgentFactory
parser.add_argument("--lobby_id", type=str, default="psyo") # After you create a private lobby, copy last part of the url
if __name__ == "__main__":
args = parser.parse_args()
autopilot(args.agent_id, args.user_id, args.lobby_id)
agent = ExpanderAgent()
autopilot(agent, args.user_id, args.lobby_id)
```
This script will run `ExpanderAgent` in the specified lobby.
## 🙌 Contributing
Expand Down
35 changes: 23 additions & 12 deletions examples/gymnasium_example.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
# type: ignore
import gymnasium as gym
import numpy as np

from generals.agents import RandomAgent, ExpanderAgent
from generals.envs import GymnasiumGenerals

# Initialize agents
agent = RandomAgent()
npc = ExpanderAgent()
agent_names = ["007", "Generalissimo"]

# Create environment
env = gym.make("gym-generals-v0", agent=agent, npc=npc, render_mode="human")
n_envs = 12
envs = gym.vector.AsyncVectorEnv(
[lambda: GymnasiumGenerals(agents=agent_names, truncation=500) for _ in range(n_envs)],
)

observation, info = env.reset()
terminated = truncated = False
while not (terminated or truncated):
action = agent.act(observation)
observation, reward, terminated, truncated, info = env.step(action)
env.render()

observations, infos = envs.reset()
terminated = [False] * len(observations)
truncated = [False] * len(observations)

while True:
agent_actions = [envs.single_action_space.sample() for _ in range(n_envs)]
npc_actions = [envs.single_action_space.sample() for _ in range(n_envs)]

# Stack actions together
actions = np.stack([agent_actions, npc_actions], axis=1)
observations, rewards, terminated, truncated, infos = envs.step(actions)
masks = [np.stack([info[-1] for info in infos[agent_name]]) for agent_name in agent_names]
if any(terminated) or any(truncated):
break
29 changes: 0 additions & 29 deletions examples/vectorized_multiagent_gymnasium.py

This file was deleted.

25 changes: 2 additions & 23 deletions generals/__init__.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,18 @@
from gymnasium.envs.registration import register

from generals.agents.agent import Agent
from generals.core.game import Action
from generals.core.grid import Grid, GridFactory
from generals.core.observation import Observation
from generals.core.replay import Replay
from generals.envs.gymnasium_generals import GymnasiumGenerals
from generals.envs.pettingzoo_generals import PettingZooGenerals

__all__ = [
"Action",
"Agent",
"GridFactory",
"PettingZooGenerals",
"GymnasiumGenerals",
"Grid",
"Replay",
"Observation",
"GeneralsIOClientError",
]


def _register_gym_generals_envs():
register(
id="gym-generals-v0",
entry_point="generals.envs.gymnasium_generals:GymnasiumGenerals",
)

register(
id="gym-generals-image-v0",
entry_point="generals.envs.initializers:gym_image_observations",
)

register(
id="gym-generals-rllib-v0",
entry_point="generals.envs.initializers:gym_rllib",
)


_register_gym_generals_envs()
2 changes: 0 additions & 2 deletions generals/envs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# type: ignore
from generals.envs.gymnasium_generals import GymnasiumGenerals
from generals.envs.multiagent_gymnasium_generals import MultiAgentGymnasiumGenerals
from generals.envs.pettingzoo_generals import PettingZooGenerals

__all__ = [
"PettingZooGenerals",
"GymnasiumGenerals",
"MultiAgentGymnasiumGenerals",
]
Loading

0 comments on commit 4c05741

Please sign in to comment.