Docs / Bot Guide

Bot Writing Guide

Structure

Your bot must define a class named Player in a file called main.py:

from megagem import Controller

class Player:
    def __init__(self, players: int, player: int):
        """Called once at game start."""
        self.players = players  # total players in game
        self.player = player    # your 0-based index

    def main(self, ct: Controller) -> tuple[int, int]:
        """Called each turn. Return (bid, reveal_index)."""
        # Bid up to 5 coins on gem auctions
        bid = 0
        if ct.is_gem_auction():
            bid = min(5, ct.get_balance())

        # Reveal first card in hand
        reveal = 0
        return bid, reveal

Bots can span multiple files. Upload a .zip containing main.py plus any helpers, model weights, or data files:

my_bot/
├── main.py          ← entry point, defines Player
├── strategy.py      ← helper module
└── weights/
    └── model.npy    ← pre-trained weights

Sibling modules are importable directly: import strategy works as expected.

Available Packages

The following packages are available in the sandbox in addition to the Python standard library:

  • megagem — the game engine and Controller API
  • numpy
  • pandas
  • scipy
  • tensorflow (CPU only) and keras
Key Controller Methods
  • ct.get_balance() → your current coins
  • ct.is_gem_auction() → True if gems are on offer
  • ct.gems_on_offer() → list of gems you'd win
  • ct.win_effect(bid) → (delta_balance, delta_adj, gems) if you won at this bid
  • ct.missions_if_i_win() → missions you'd claim by winning
  • ct.mission_bonus_if_i_win() → bonus coins from claiming missions
  • ct.beats_on_tie(other) → True if you'd win a tie against player other
Full Controller API →
Limits
  • • Upload size: configurable by admin (default 256 KB for .py, increase for zips with weights)
  • • Per-turn time limit: configurable by admin (default 500ms)
  • • Per-game total budget: configurable by admin (default 10s)
  • • Memory limit enforced at the container level
  • • Exceeding limits → your turn defaults to (0, 0), game continues
  • • No network access; no filesystem writes outside your process
Debugging your bot

Use print() freely — your bot's stdout is captured and displayed in the match log as stdout, separate from actual exceptions.

def main(self, ct):
    print(f"my balance: {ct.get_balance()}, gems: {ct.gems_on_offer()}")
    bid = min(5, ct.get_balance())
    return bid, 0

Where to find logs after a match:

  1. Go to Matches and open your match.
  2. In the Games table, expand bot stdout on any game row to see your print() output.
  3. If your bot raised an uncaught exception, a separate bot errors section appears in red.
  4. Click Logs ↓ to download the full log file (both stdout and errors) as JSON.
  5. In the Replay viewer, stdout and error panels appear above the scrubber if logs exist.

Tips:

  • • Print output is per-game, not per-turn — add a turn counter if you need turn-level tracing.
  • • Output is capped at 64 KB per bot per game; excess is silently dropped.
  • • Uncaught exceptions cause your bot to return (0, 0) for that turn; the game continues.
  • • Time-limit violations (TLE) are flagged as errors, not stdout.
Uploading

Via web: Go to My Bots, enter your bot name, select a .py file or a .zip containing your bot directory, click Upload.

Via CLI:

pip install megagem --extra-index-url https://megagem.hrvt.dev/simple/
megagem auth --server https://megagem.hrvt.dev

# Upload a single file
megagem push main.py --name my-bot --activate

# Upload a whole directory (zipped automatically)
megagem push my_bot/ --name my-bot --activate

# Upload a pre-made zip
megagem push my_bot.zip --name my-bot --activate