Python SDK for running bots on Game AI Arena, supports FlipFour, FlipFlop3x3/5x5, Amoeba
Project description
Game AI Arena SDK
Python SDK for running bots on Game AI Arena.
Installation
pip install game-ai-arena-sdk
Configuration
Set the matchmaker URL via environment variable or pass directly:
# Environment variable (recommended)
export MATCHMAKER_URL=ws://<SERVER_ADDRESS>:9000/matchmaking/ws
# Or for tunnel mode
export MATCHMAKER_URL=wss://<YOUR_TUNNEL>.trycloudflare.com/matchmaking/ws
Get the correct URL from your admin.
Getting Your Credentials
Option A: Swagger UI
- Open
http://<SERVER>:9000/docsin your browser - Use
/api/auth/signupto create an account - Click Authorize and enter your access token
- Use
/api/bots/POST to create a bot (save thebot_api_key- shown once!) - Use
/api/bots/GET to find your bot'sid
Option B: curl
# Set your server URL (get this from your admin)
SERVER="http://<SERVER_ADDRESS>:9000"
# 1. Create account
curl -X POST $SERVER/api/auth/signup \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "username": "yourname", "password": "SecurePass123"}'
# Response: {"access_token": "eyJhbG...", ...}
TOKEN="paste_your_access_token_here"
# 2. Create a bot
curl -X POST $SERVER/api/bots/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"bot_name": "MyBot", "game_type": "flipflop_3x3"}'
# Response: {"message": "Bot created successfully", "bot_api_key": "..."}
# Save the bot_api_key - it's only shown once!
# 3. Get your bot ID (query your user's bots)
USER_ID="your-user-id" # from signup response or /api/profile
curl -X POST $SERVER/api/users/$USER_ID/bots \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{}'
# Find your bot in the response, copy its "id" field
Quick Start
import random
from game_ai_arena_sdk import Bot, start, GameType, Move, GameStateLoop
class MyBot(Bot):
async def on_move(self, state: GameStateLoop) -> Move:
piece = random.choice(state.legal_moves)
dest = random.choice(piece.valid_moves)
return Move(from_pos=piece.pos, to_pos=dest)
bot = MyBot(bot_id="YOUR_BOT_ID", api_key="YOUR_API_KEY")
# Uses MATCHMAKER_URL env var, or pass explicitly:
start(bot, GameType.FLIPFLOP_3X3)
# Or: start(bot, GameType.FLIPFLOP_3X3, matchmaker_url="ws://...")
GameStateLoop
Received in on_move:
| Field | Description |
|---|---|
board |
Board string representation |
my_side |
Your side ("white" / "black") |
current_turn |
Whose turn it is |
legal_moves |
List of PieceMovesInfo you can move |
Each PieceMovesInfo has name, pos, and valid_moves.
Hooks
All optional except on_move:
async def on_move(self, state: GameStateLoop) -> Move # Required
async def on_ready(self) -> None # Connected to matchmaker
async def on_queue_entry(self) -> None # Joined queue
async def on_queue_exit(self) -> None # Leaving queue
async def on_match_found(self, match_id: str) -> None # Matched with opponent
async def on_room_joined(self, room_id: str) -> None # Joined game room
async def on_game_start(self, state: GameStateLoop) -> None
async def on_game_end(self, winner: str | None, state: GameStateEnd) -> None
async def on_disconnect(self, reason: str) -> None # Disconnected
Multiple Bots
import asyncio
from game_ai_arena_sdk import run
async def main():
await asyncio.gather(
run(MyBot("id1", "key1"), GameType.FLIPFLOP_3X3),
run(MyBot("id2", "key2"), GameType.FLIPFLOP_3X3),
)
asyncio.run(main())
Note: Bots owned by the same user cannot be matched against each other. To test two bots locally, they must belong to different user accounts.
FlipFour Notes
FlipFour has two move types: placement (from hand) and movement (on board).
When placing a piece from hand, you must specify the side ("+" for orthogonal, "x" for diagonal):
# Placement: piece from hand onto the board
Move(from_pos="HAND", to_pos="C3", side="+") # place as orthogonal
Move(from_pos="HAND", to_pos="C3", side="x") # place as diagonal
# Movement: existing piece on board (side flips automatically)
Move(from_pos="A1", to_pos="A4") # no side needed
In legal_moves, hand placements appear as two entries with pos="HAND":
PieceMovesInfo(name="+", pos="HAND", valid_moves=[...])-- place as+PieceMovesInfo(name="x", pos="HAND", valid_moves=[...])-- place asx
Board pieces appear with their current side and position:
PieceMovesInfo(name="+", pos="C3", valid_moves=["A3", "B3", ...])
class MyFlipFourBot(Bot):
async def on_move(self, state: GameStateLoop) -> Move:
piece = random.choice(state.legal_moves)
target = random.choice(piece.valid_moves)
side = piece.name if piece.pos == "HAND" else None
return Move(from_pos=piece.pos, to_pos=target, side=side)
start(bot, GameType.FLIPFOUR)
Game Types
| Type | Enum | Description |
|---|---|---|
| FlipFlop 3x3 | FLIPFLOP_3X3 |
3x3 board, rook/bishop pieces |
| FlipFlop 5x5 | FLIPFLOP_5X5 |
5x5 board, rook/bishop pieces |
| FlipFour | FLIPFOUR |
5x5 board, 4 pieces per player, line-of-four wins |
| Amoeba | AMOEBA |
Hexagonal grid, kernel capture wins |
Amoeba Notes
Amoeba is played on a hexagonal board (radius 3) using axial coordinates (x,y,z where x+y+z=0).
Key mechanics:
- Each player has 1 kernel + 10 standard pieces
- Pieces can stack on top of each other; the top piece controls the stack
- Stack height determines mandatory move distance (a stack of 3 must move exactly 3 cells)
- Win by capturing the opponent's kernel (landing on it) or when opponent has no legal moves
- Threefold repetition triggers a stack-count tiebreaker (not automatic draw)
Splitting moves:
When moving a stack, you can optionally split it by setting splitting=True. This spreads pieces along the movement path instead of keeping them together:
# Normal move: entire stack moves together
Move(from_pos="0,0,0", to_pos="2,-1,-1")
# Split move: pieces spread along the path
Move(from_pos="0,0,0", to_pos="2,-1,-1", splitting=True)
Example bot:
class MyAmoebaBot(Bot):
async def on_move(self, state: GameStateLoop) -> Move:
# Pick a piece with valid moves
pieces_with_moves = [p for p in state.legal_moves if p.valid_moves]
piece = random.choice(pieces_with_moves)
target = random.choice(piece.valid_moves)
# Optionally split the stack (random choice here)
should_split = random.choice([True, False])
return Move(from_pos=piece.pos, to_pos=target, splitting=should_split)
start(bot, GameType.AMOEBA)
Coordinates:
Positions use axial coordinates as comma-separated strings: "x,y,z" (e.g., "0,0,0", "-1,2,-1").
In legal_moves, each PieceMovesInfo shows the piece name, its position, and valid destination coordinates.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file game_ai_arena_sdk-0.4.0.tar.gz.
File metadata
- Download URL: game_ai_arena_sdk-0.4.0.tar.gz
- Upload date:
- Size: 7.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c01f89bd24810d13402a803bcf9a5c609a61ebf690c4623c64071796a8128b34
|
|
| MD5 |
cf0b5225f58bdcf9512cf09b1f03ec41
|
|
| BLAKE2b-256 |
47847b51d55ff7d2cb1b95f018224ac794b7b4ab7a38a0ef93c26ef581ac4131
|
File details
Details for the file game_ai_arena_sdk-0.4.0-py3-none-any.whl.
File metadata
- Download URL: game_ai_arena_sdk-0.4.0-py3-none-any.whl
- Upload date:
- Size: 16.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aaf3f3b1ddbd517dd8c0c782b7dcfb943d07f7f6b2814954830b1d388375c225
|
|
| MD5 |
b54b8aea063dbb48e770931e84a372d7
|
|
| BLAKE2b-256 |
403b74beca2fb906c9e33b6d9b6ff7dcacfac7b07a280acbca2b8a81623659a0
|