Metadata-Version: 2.2
Name: beetlelib
Version: 0.1.1
Summary: Żuczki battle-royale engine, bot contract and training library.
Keywords: game,battle-royale,reinforcement-learning,bot,ai-competition
Author: Żuczki
License: MIT
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: Topic :: Games/Entertainment
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: ormsgpack>=1.5
Requires-Dist: numpy>=1.26
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Provides-Extra: backend
Requires-Dist: fastapi>=0.110; extra == "backend"
Requires-Dist: uvicorn[standard]>=0.27; extra == "backend"
Requires-Dist: websockets>=12; extra == "backend"
Requires-Dist: python-multipart>=0.0.9; extra == "backend"
Requires-Dist: sqlalchemy>=2.0; extra == "backend"
Requires-Dist: alembic>=1.13; extra == "backend"
Requires-Dist: httpx>=0.27; extra == "backend"
Requires-Dist: psycopg[binary]>=3.2; extra == "backend"
Provides-Extra: rl
Requires-Dist: torch>=2.0; extra == "rl"
Description-Content-Type: text/markdown

# beetlelib

The canonical **Żuczki** battle-royale engine, bot contract and training library. Use it to
write a bot, train it locally against the native engine, and
submit it to the platform unchanged.

```bash
pip install beetlelib
beetlelib init my-bot
cd my-bot
python run_local.py
```

## Quickstart

```python
import math
from beetlelib import Bot, Action, Control, Env
from beetlelib.bot.examples.greedy_bot import GreedyBot


class MyBot(Bot):
    def __init__(self) -> None:
        self.ready_to_play()  # finish slow setup before signalling ready

    def act(self, observations):
        actions = {}
        for beetle_id, obs in observations.items():
            # steer toward the nearest food
            target = min(obs.points, key=lambda p: (p.x - obs.me.x) ** 2 + (p.y - obs.me.y) ** 2, default=None)
            angle = math.atan2(target.y - obs.me.y, target.x - obs.me.x) if target else obs.me.angle
            actions[beetle_id] = Action(angle, Control.NONE)
        return actions


env = Env(seed=7, opponents=[GreedyBot(), GreedyBot(), GreedyBot()])
obs, done = env.reset(), False
bot = MyBot()
while not done:
    obs, rewards, done = env.step(bot.act(obs))
```

## The contract

One method, shared by every controller (your bot, the baselines, the live human):

```python
def act(self, observations: dict[str, Observation]) -> dict[str, Action]
```

You own a **pair** of two beetles, so you receive two observations (keyed by beetle id) and
return two actions. An `Action` is a steering `target_angle` plus a `Control` flag. An
`Observation` is your beetle's vision-bounded view: itself (`me`), and the nearby `beetles`,
`points`, `rubys`, `obstacles` and `projectiles`.

See `src/template/task.md` (ships with the participant kit) for the full rules, the
observation/action fields, the limits, and how to submit.

## Submit from the command line

Log in once. The password is requested without displaying it and is not saved:

```bash
beetlelib login --server https://example.com --username your-name
```

Then upload `bot.py` and any other Python files it needs:

```bash
beetlelib upload bot.py helpers.py --name my-bot
```

You can pass a directory instead; all `.py` files directly inside it will be uploaded. If
your class is not `MyBot` in `bot.py`, provide its module and class name:

```bash
beetlelib upload my-bot/ --name my-bot \
  --command "python -m beetlelib.bot.runner agent:SmartBot"
```
