LLM Bot
A GUI client that bridges any LLM provider to the Triumvirate chess server. Version 2.2. View on GitHub →
When building prompts for LLM-powered bots, always prefer Triumvirate v4.0 notation over classical server notation (A1–L12). Generative models understand hexagonal board geometry significantly better with sector-ring-depth coordinates (e.g.,
W2/R2.3) than with the classical notation that has irregular bridges and gaps between segments. The LLM Bot client supports both notations natively and includes built-in converters. See the full Triumvirate v4.0 specification for complete mapping tables and coordinate formulas.
Overview
Triumvirate LLM Bot v2.2 is an autonomous, fully graphical client-bot for playing three-player chess. On each turn the bot:
- Gets the board state from the server
- Builds a multi-layered, configurable prompt
- Sends the request to the chosen LLM
- Parses the response and extracts a valid chess move
- Sends the move to the server
After pressing Start, the bot plays fully automatically until the game ends.
Key Features
- 6+ LLM providers (Ollama, OpenAI, Anthropic, OpenRouter, LM Studio, custom URL)
- 3 response formats (simple text, JSON, JSON with reasoning)
- Retry with escalation on illegal moves + random fallback
- Full per-move cost tracking (OpenRouter pricing)
- Move tracing: every move saved as JSON with complete audit trail
- Multi-bot mode: run N bots in parallel with different models
- NiceGUI desktop/web interface
Installation & Setup
# Clone the repository
git clone https://github.com/fortser/Triumvirate_LLMbot.git
cd Triumvirate_LLMbot/examples/decomp
# Install dependencies
pip install -r requirements.txt
# Set API keys (optional, depending on provider)
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-...
export OPENROUTER_API_KEY=sk-or-...
# Run (desktop window)
python main.py
# Run as web server
python main.py --web --port 8090
# Run headless (no GUI)
python main.py --headless
Command-Line Options
| Flag | Description |
|---|---|
--web | Launch as web server instead of desktop window |
--host HOST | Web server host (default: 0.0.0.0) |
--port PORT | Web server port (default: 8090) |
--settings FILE | Path to separate config file |
--headless | No GUI, console output only |
--bots N | Run N parallel bots (multi-bot mode) |
--models M1 M2 | Explicit model list for multi-bot |
--models-pool FILE | JSON file with model pool for multi-bot |
--start-delay N | Seconds between bot launches |
LLM Providers
| Provider | Base URL | API Key Env |
|---|---|---|
| Ollama | http://localhost:11434/v1 | Not required |
| OpenAI | https://api.openai.com/v1 | OPENAI_API_KEY |
| Anthropic | Native protocol (not OpenAI-compatible) | ANTHROPIC_API_KEY |
| OpenRouter | https://openrouter.ai/api/v1 | OPENROUTER_API_KEY |
| LM Studio | http://localhost:1234/v1 | Not required |
| Custom URL | Any OpenAI-compatible endpoint | Configurable |
Prompt System
The bot builds prompts from configurable templates stored in prompts/:
system_prompt.txt— System message (chess rules, role description)user_prompt_template.txt— User message template with placeholdersformat_simple.txt— Instructions for simple text responseformat_json.txt— Instructions for JSON responseformat_json_thinking.txt— Instructions for JSON with reasoning
Template Placeholders
| Placeholder | Replaced With |
|---|---|
| Current move number |
| Your color (white/black/red) |
| Dict of legal moves |
| Board state (piece list) |
| Check information |
| Position in 3PF format |
| Previous move details |
and placeholders. Models produce significantly more accurate moves when board geometry is described with the radial-ring coordinate system. The LLM Bot converter handles the translation automatically. See the full specification for coordinate format details.
Response Formats & Parsing
Simple Text
Model responds with plain text like E2 E4. Parser uses regex to extract coordinates.
JSON
Model responds with {"move_from": "E2", "move_to": "E4"}. Parser extracts keys directly.
JSON with Thinking
Model includes analysis: {"thinking": "Analysis...", "move_from": "E2", "move_to": "E4"}. Thinking is saved in trace logs for analysis.
Reliability & Fallbacks
- Retries with escalation — On illegal move, repeats the request (up to N attempts). On the last attempt, simplifies the prompt with a direct list of legal moves.
- Random fallback — If the LLM cannot produce a valid move, selects a random legal move (configurable, can be disabled).
- JSON sanitization — Handles malformed JSON responses from LLMs.
Cost Tracking
For OpenRouter, pricing is automatically loaded from the API. Per-move cost is calculated from input, output, and reasoning tokens. Cumulative stats are shown when the bot stops.
Move Tracing
Every move is saved as a JSON file in logs/game_<id>/move_NNN.json containing:
- Raw board state from server
- Rendered multi-layer prompt
- All LLM requests and responses with token counts and cost
- All parsing attempts with intermediate results
- Final move choice and server response
Trace Analyzer
A separate web app + CLI tool for analyzing trace logs:
# Launch trace viewer
cd trace_analyzer && python app.py --logs ../logs --port 8091
# Generate metrics and model rankings
python -m trace_analyzer.metrics
# With SmartBot objective evaluation
python -m trace_analyzer.metrics --smartbot
The viewer has 4 tabs: Overview (dashboard), Moves Table, Thinking Gallery, Move Detail.
Multi-Agent Evaluation
A system of three components for analyzing LLM play quality:
| Component | Type | Purpose |
|---|---|---|
| metrics.py | Python CLI | Automated metrics, model rankings |
| Model Evaluator | Claude Code agent | Chess evaluation, model suitability |
| Prompt Optimizer | Claude Code agent | Prompt analysis, recommendations |
Composite Score
Models are ranked by a composite score (0–1):
| Score | Rating |
|---|---|
| 0.70+ | Excellent — suitable for production use |
| 0.50–0.70 | Average — works with limitations |
| 0.30–0.50 | Weak — needs prompt optimization |
| <0.30 | Unsuitable — can't follow format or produce legal moves |
Components (without SmartBot): Reliability 35% + Activity 30% + Tactical 20% + Efficiency 15%
Components (with SmartBot): Reliability 20% + SmartBot Quality 35% + Tactical 15% + Efficiency 10% + Win Rate 20%
Triumvirate Notation in the LLM Bot
The LLM Bot supports two notation modes for prompts. The notation mode is configured in the settings panel or via the config file.
Notation Comparison in Prompts
| Aspect | Server (Classic) | Triumvirate v4.0 (Recommended) |
|---|---|---|
| Cell format | E2, A1, L12 |
W2/R2.3, W3/B3.0, R3/B3.0 |
| Piece names | King, Queen, Rook, Bishop, Knight, Pawn | Leader, Marshal, Train, Drone, Noctis, Private |
| Board geometry | Column-row grid with gaps (E5–H8, I1–L4, A9–D12 invalid) | Seamless radial-ring system; all 96 cells addressable without gaps |
| Sector awareness | Implicit (must memorize which columns belong to which segment) | Explicit (sector letter W/B/R is part of every coordinate) |
| LLM accuracy | Lower — models struggle with irregular bridge geometry | Higher — models understand radial/ring spatial relationships natively |
How the Converter Works
The bot's notation_converter.py module translates between the two systems. When Triumvirate mode is active:
- Server sends
/stateresponse in classic notation (e.g.,E2) - Converter translates board and legal_moves to Triumvirate (e.g.,
W2/R2.3) - Prompt is built with Triumvirate coordinates and piece names
- LLM responds with a Triumvirate move (e.g.,
W2/R2.3 to W1/R1.3) - Converter translates back to classic notation for the
/moveAPI call
Key Positions in Both Notations
| Description | Classic | Triumvirate |
|---|---|---|
| White Leader (King) | E1 | W3/R3.3 |
| White Marshal (Queen) | D1 | W3/B3.3 |
| Black Leader (King) | I8 | B3/R3.3 |
| Red Leader (King) | I12 | R3/W3.3 |
| Rosette (White-Black) | D4 | C/W.B |
| Rosette (White-Red) | E4 | C/W.R |
| Rosette (Black-Red near Black) | D5 | C/B.R |
| Rosette (Black-Red near Red) | I9 | C/R.B |
Multi-Bot Mode
# Run 3 bots with random models from pool
python main.py --headless --bots 3
# Run 5 bots with specific models
python main.py --headless --models openai/gpt-4o anthropic/claude-haiku-4-5-20251001
# Custom model pool with staggered start
python main.py --headless --bots 5 --models-pool custom_pool.json --start-delay 10