Kriegspiel bot that asks an Anthropic Haiku model to choose the next action from the bot's private game state.
- registers as a listed Kriegspiel bot
- runs one bot process per bot identity/model instance
- polls assigned games from the main process and runs one lightweight runner thread per active game
- does not create waiting lobby games by default
- can join another bot's waiting lobby game using its configured tier probability while still under its active-game cap
- builds a compact stateless prompt from a file-backed ruleset summary, private FEN, ruleset-specific public state, recent scorecard turns, legal actions, and retry feedback
- adds a stable system-prompt strategy reference so Anthropic prompt caching is above Haiku's cacheable token threshold
- asks an Anthropic Haiku model for the top ranked next actions in compact strict JSON
- validates the model output against the server-provided legal actions
- checks Anthropic availability through the non-generating model metadata endpoint before joining a new bot-vs-bot game
- skips the join if Anthropic is unavailable or out of quota
- still falls back safely if the model response itself is malformed
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
python bot.py --register
python bot.pyThe bot uses dedicated prompt summaries in ruleset_summaries/*.md, derived from the canonical ks-content/rules docs.
Keep those summaries short and update them when a ruleset behavior that matters to model play changes.
By default the registration email is bot-haiku@kriegspiel.org.
Use separate env and state files when running one independent Anthropic bot per model:
python bot.py \
--env-file instances/sonnet5.env \
--state-file instances/sonnet5-state.json \
--register
python bot.py \
--env-file instances/sonnet5.env \
--state-file instances/sonnet5-state.jsonEach instance env must have its own Kriegspiel bot identity and
ANTHROPIC_MODEL. ks-deploy bot-instance-bootstrap bot-haiku ... renders this
shape for production instances.
Checked-in T3/T4 templates live under instances/ with .env.example suffixes.
Copy one to .env, fill in secrets, and use the matching state file when
running or registering that instance.
By default the bot does not create open lobby games on its own. That behavior is controlled with:
KRIEGSPIEL_AUTO_CREATE_LOBBY_GAME=true|falseKRIEGSPIEL_AUTO_CREATE_RULE_VARIANT=berkeley|berkeley_any|cincinnati|wild16|rand|english|crazykriegKRIEGSPIEL_AUTO_CREATE_PLAY_AS=white|black|randomKRIEGSPIEL_SUPPORTED_RULE_VARIANTS=berkeley,berkeley_any,cincinnati,wild16,rand,english,crazykriegKRIEGSPIEL_MAX_ACTIVE_GAMES_BEFORE_CREATE=1KRIEGSPIEL_ACTIVE_GAME_DISCOVERY_LIMIT=100LLM_BOT_MAX_CONCURRENT_MODEL_CALLS=5KRIEGSPIEL_LLM_BOT_TIER=T2|T3|T4|T5KRIEGSPIEL_BOT_GAME_PICK_PROBABILITY=0.001optional explicit join probability override; if unset, tier defaults are T20.0010, T30.0005, T40.0002, and T50.0001KRIEGSPIEL_AUTO_CREATE_COOLDOWN_SECONDS=3600|10800|21600
Bot-vs-bot play is also enabled by default:
- the bot samples open waiting games at most once every 10 minutes
- it will only consider games created by another bot
- it samples that decision at most once every 10 minutes
- it will try to join one using the configured/tiered budget probability on that scan
- it uses the same 1-active-game cap for intentional bot-vs-bot joins
- it keeps the local cooldown even when no join candidate is found, matching backend bot-join limits and avoiding tight lobby scans
Assigned active games are handled by per-game runner threads inside the same
process. The main loop discovers active games with
KRIEGSPIEL_ACTIVE_GAME_DISCOVERY_LIMIT, handles lobby create/join policy, and
starts missing runners. Existing runners are not stopped only because a later
capped discovery response omits them; each runner exits when its own game-state
poll reports completion or unavailability. Anthropic calls across all game
runners are bounded by LLM_BOT_MAX_CONCURRENT_MODEL_CALLS, which defaults to
5. Backend polling, lobby scans, sleeps, and fallback move selection do not
hold that provider-call gate.
Optional human-lobby creation is still disabled by default for individual model
instances. If an operator enables one selected model instance as the random
tier representative, the built-in create cooldown defaults to T2 hourly, T3
every 3 hours, and T4 every 6 hours; KRIEGSPIEL_AUTO_CREATE_COOLDOWN_SECONDS
overrides that cadence.
Anthropic prompting defaults:
- system prompt carries a ruleset-specific summary from
ruleset_summaries/*.mdand the overall Kriegspiel scene - the stable system prompt also carries a cacheable strategy reference; the current turn JSON remains in the uncached user prompt
- user prompt is stateless, uses compact keys, and carries private FEN, ruleset-specific public material/reserves, at least the last 10 scorecard turns when available, legal actions, and retry feedback
- Anthropic prompt caching is enabled with a 5-minute TTL by default, with an explicit cache marker on the stable system prompt; set
ANTHROPIC_CACHE_TTL=1honly when requests may be spaced more than 5 minutes apart - verify prompt caching through
cache_creation_input_tokenson the first matching request andcache_read_input_tokenson later matching requests - Anthropic tool use is disabled by default to keep each request smaller; set
ANTHROPIC_USE_TOOLS=trueto force tool-calling output - the bot asks for the top 10 ranked candidate actions by default
- if a batch fails, it asks the model for the next batch of compact move candidates
- defaults can be tuned with:
ANTHROPIC_MODEL=claude-haiku-4-5-20251001ANTHROPIC_MODEL_BATCH_SIZE=10ANTHROPIC_MAX_BATCHES_PER_TURN=5ANTHROPIC_MAX_PROMPT_TURNS=10(values below 10 are clamped to 10)ANTHROPIC_PREFLIGHT_SUCCESS_TTL_SECONDS=60ANTHROPIC_PREFLIGHT_FAILURE_TTL_SECONDS=15ANTHROPIC_INPUT_USD_PER_MILLION_TOKENS=1.00ANTHROPIC_OUTPUT_USD_PER_MILLION_TOKENS=5.00ANTHROPIC_CACHE_READ_INPUT_USD_PER_MILLION_TOKENS=0.10ANTHROPIC_CACHE_WRITE_5M_USD_PER_MILLION_TOKENS=1.25ANTHROPIC_CACHE_WRITE_1H_USD_PER_MILLION_TOKENS=2.00ANTHROPIC_MONTHLY_BUDGET_USD=18ANTHROPIC_MONTHLY_BUDGET_STATE_PATH=~/.local/state/kriegspiel/provider-budgets/anthropic.jsonPROVIDER_BUDGET_RESERVATION_TTL_SECONDS=1800
The monthly ledger is shared by Haiku, Sonnet, Opus, and every other Anthropic bot process on the host. It reserves a conservative request cost before the provider call and settles from returned token and cache usage afterward, preventing concurrent processes from multiplying the cap. Tracking begins prospectively when the ledger is first deployed and resets at each UTC month boundary.
python -m unittest discover -s testsA production host can run the bot as a service with deploy/kriegspiel-haiku-bot.service.