Real-time competitive benchmarks for AI agents across classic games.
Versus Arena pits two AI agents against each other in head-to-head matches. Each model uses structured tool calling: game-specific tools to inspect state, then a terminal action to commit a move, in a multi-step observe-then-act loop. Every turn is logged with latency, tokens, cost, correctness, and tool usage, then rolled into Elo ratings and an analytics dashboard so you can compare models by game, matchup, and over time.
|
|
|
|
|
|
Arena games (full UI, real-time playback, playable from the game menu):
| Game | What it tests |
|---|---|
| Wordle | Language, deduction, vocabulary |
| NYT Connections | Categorization, pattern recognition |
| Battleship | Spatial reasoning, strategy |
| Minesweeper Race | Logical deduction, risk assessment |
| Auction Blitz | Resource allocation, opponent modeling |
| Poker Showdown | Game theory, bluffing, bet sizing |
| Provider | Models |
|---|---|
| OpenAI | GPT-5.5, GPT-5.4 Mini, GPT-4o, o4-mini |
| Anthropic | Claude Opus 4.7, Claude Sonnet 4.6, Claude Haiku 4.5 |
| Gemini 3.1 Pro, Gemini 2.5 Pro, Gemini 2.5 Flash |
Models are selected on the model-selection screen (Smash Bros-style card picker). A "Random" option picks a model at random for either slot.
Every completed game feeds into a SQLite-backed benchmark database. The dashboard (/dashboard) provides:
- Overview KPIs: total runs, completion rate, median duration, unique models, avg latency and correctness
- Elo Leaderboard: filterable by game or overall, with win rate
- Model Comparison: bar charts of win rate and rating
- Head-to-Head: pairwise matchup win/loss/draw breakdown
- Quality Metrics: latency, cost, correctness, and error rate by model and game
- Trend Charts: runs per day, Elo movement over time
- Run History: filterable list with per-move detail drawer, delete with automatic Elo rebuild
- Export: CSV and JSON export of all run data
- Python 3.10+
- Node.js 18+
- At least one API key: OpenAI, Anthropic, or Google
cd backend
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
# Add your API keys to .envStart the server:
python main.pyRuns on http://localhost:8000. API docs at http://localhost:8000/docs.
cd frontend
npm install
npm run devRuns on http://localhost:5174.
Create backend/.env from the template:
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=...
Only the providers you want to use need a key. The BENCHMARK_DB_PATH env var can override the default SQLite location (backend/data/benchmark.sqlite).
VersusArena/
├── backend/
│ ├── main.py # Entry point: runs uvicorn on port 8000
│ ├── src/
│ │ ├── api/
│ │ │ ├── server.py # FastAPI app, all game endpoints (REST + WebSocket)
│ │ │ └── benchmark_routes.py # Leaderboard, analytics, batch runs, CSV/JSON export
│ │ ├── games/
│ │ │ ├── battleship/battleship.py # Battleship (extends BaseGame)
│ │ │ ├── wordle/wordle_simple.py # Wordle engine
│ │ │ ├── nyt_connections/ # Connections puzzle + puzzle data
│ │ │ ├── minesweeper.py # Minesweeper race
│ │ │ ├── auction.py # Auction Blitz
│ │ │ ├── poker.py # Poker Showdown (+ poker_chips.py)
│ │ │ ├── prisoners_dilemma.py # Iterated Prisoner's Dilemma
│ │ │ ├── code_debug_challenge.py # Code repair benchmark
│ │ │ └── twenty_questions.py # 20 Questions
│ │ ├── engine/
│ │ │ ├── agent_client.py # Multi-step tool-call loop (OpenAI/Anthropic/Google)
│ │ │ ├── game_tools.py # Per-game tool schemas
│ │ │ └── agent_loop.py # Async race orchestration (Connections)
│ │ ├── benchmark/
│ │ │ ├── recorder.py # Records runs, moves, and updates Elo
│ │ │ ├── analytics.py # Aggregation queries (overview, h2h, trends, quality)
│ │ │ ├── elo.py # Elo rating math (K=32, initial 1200)
│ │ │ └── cost_estimate.py # Per-model token cost estimates
│ │ ├── db/
│ │ │ └── database.py # SQLite schema + connection pool
│ │ └── utils/
│ │ └── common.py # LLMClient (batch games) + BaseGame
│ ├── tests/ # pytest suite
│ ├── data/ # SQLite DB (auto-created at runtime)
│ ├── requirements.txt
│ └── .env.example
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Router + game menu
│ │ ├── pages/
│ │ │ ├── LandingPage.jsx # Three.js WebGL landing (retro dither shader)
│ │ │ ├── ModelSelection.jsx # Smash Bros-style model picker
│ │ │ └── Dashboard.jsx # Analytics dashboard
│ │ ├── components/
│ │ │ ├── games/ # Per-game UIs (Battleship, Wordle, Connections, etc.)
│ │ │ ├── dashboard/ # Dashboard widgets (Leaderboard, HeadToHead, Trends, etc.)
│ │ │ └── common/ # GameLayout, GameOverModal, PixelIcons, timers
│ │ ├── hooks/ # useGameFlow, useGameWebSocket, useWordleGameLoop
│ │ ├── config/modelCatalog.js # Model registry
│ │ └── utils/ # Network detection, chip math, game helpers
│ ├── package.json
│ └── vite.config.js
└── README.md
┌─────────────────────────────────────────────────────┐
│ Frontend (React 19 + Vite, port 5174) │
│ Three.js landing · Tailwind CSS · Recharts │
│ REST + WebSocket ←→ backend │
└────────────────────────┬────────────────────────────┘
│
┌────────────────────────▼────────────────────────────┐
│ Backend (FastAPI, port 8000) │
│ │
│ ┌──────────┐ ┌─────────────┐ ┌──────────────────┐ │
│ │ Game │ │ AgentClient │ │ Benchmark │ │
│ │ Modules │──│ + game_tools│ │ Recorder + Elo │ │
│ └────┬─────┘ └──────┬──────┘ └────────┬─────────┘ │
│ │ │ │ │
│ ┌────▼─────┐ ┌──────▼──────┐ ┌────────▼─────────┐ │
│ │ LLMClient│ │ Agent Loop │ │ SQLite │ │
│ │ (batch) │ │ (race UI) │ │ (benchmark.db) │ │
│ └────┬─────┘ └─────────────┘ └──────────────────┘ │
└───────┼─────────────────────────────────────────────┘
│
┌───────▼─────────────────────────────────────────────┐
│ LLM Providers (tool calling) │
│ OpenAI · Anthropic · Google Gemini │
└─────────────────────────────────────────────────────┘
AgentClient (agent_client.py) sends tool definitions with each turn and runs up to 5 observe→act steps: observation tools return game state; a terminal action tool ends the turn. LLMClient remains for batch-only games (Prisoner's Dilemma, Code Debug, 20 Questions). Move records store tool_calls_count, tools_used, and a tool trace in extra_json when benchmarking.
| Endpoint | Method | Description |
|---|---|---|
/games/battleship/{id} |
WebSocket | Battleship game session |
/api/wordle/start |
POST | Start a Wordle game |
/api/wordle/guess/{id} |
POST | Submit an agent's guess |
/api/connections/start |
POST | Start a Connections game |
/api/connections/game/{id}/start-race |
POST | Launch async race mode |
/api/minesweeper/start |
POST | Start a Minesweeper race |
/api/minesweeper/{id}/step |
POST | Advance one agent's turn |
/api/auction/start |
POST | Start an Auction Blitz |
/api/auction/{id}/round |
POST | Play one auction round |
/api/poker/start |
POST | Start a Poker Showdown |
/api/poker/{id}/step |
POST | Advance one betting action |
/api/code-debug/start |
POST | Start a code debug session |
/api/code-debug/{id}/run |
POST | Run both agents on the challenge |
| Endpoint | Method | Description |
|---|---|---|
/api/benchmark/leaderboard |
GET | Elo leaderboard (filterable by game scope) |
/api/benchmark/analytics/overview |
GET | Dashboard KPIs |
/api/benchmark/analytics/head-to-head |
GET | Pairwise matchup stats |
/api/benchmark/analytics/quality |
GET | Latency, cost, correctness by model |
/api/benchmark/analytics/trends |
GET | Runs per day + Elo over time |
/api/benchmark/runs |
GET | Run history |
/api/benchmark/runs/{id} |
GET | Run detail with per-move data |
/api/benchmark/runs/{id} |
DELETE | Delete run + rebuild Elo |
/api/benchmark/export/runs.csv |
GET | CSV export |
/api/benchmark/export/runs.json |
GET | JSON export |
/api/benchmark/batch/start |
POST | Start a batch benchmark job |
/api/benchmark/batch/status/{id} |
GET | Poll batch job progress |
/health |
GET | Health check |
cd backend
python -m pytest tests/ -vTests cover game logic (Wordle, Battleship, Connections, Minesweeper, Auction, Poker), the Elo system, the benchmark recorder, and analytics queries. Tests use a temporary SQLite database and don't make LLM API calls.





