# Minimal Auto Chess · DSH Web Plugin English · [中文](README.md) ![dsh-auto-chess](assets/prep.png) Tucked into the DSH tab bar is a tiny auto-chess table: take the blue side yourself and play a match against the AI, or lean back in the audience seat and watch two AIs, each on a model of its own choosing, slug it out on the board. No move here is free: every round's decision is a real model request. Crank the thinking level up to High or Max, open the reasoning log, and watch the AI work through who to buy, who to sell, which traits to chase, and whether that gold belongs in a level-up or a reroll. The result lands on the board; the reasoning stays in the log. Game state lives in a module-level store in the browser: switching tabs or sessions never resets the match or interrupts an in-flight AI decision. ## Key features - **Human vs AI, or AI vs AI**: play against the AI yourself (you take blue), or spectate two AIs dueling each other. - **Independent model selection**: blue and red each pick their AI from the model catalog (built-in adapters plus your configured providers/models); each side is configured separately, with blue's AI settings and reasoning log on the left of the UI and red's on the right. - **Adjustable thinking levels**: Off / High / Max, set per side, controlling how much each AI thinks before a decision; a level the model does not support falls back to its default automatically. - **Fixed timeout and token cap**: the per-decision deadline (default 300000 ms) and the output-token cap (default 32000) are deployment policy; the UI no longer exposes them, and the client sends the fixed values with every request. - **Per-side editable prompts**: the default system prompt ships with the full rules, a strict return format and one correct example; blue and red can edit their own prompt in their settings area and restore the default in one click. - **Reasoning display**: after every AI decision, the full reasoning text is available in the side's reasoning log (blue left, red right); entries start collapsed and expand on click. - **Retry on failure**: an illegal action batch is rejected with the reason and a retry button; transient streaming failures retry automatically within the attempt budget, so one hiccup never sinks the whole game. - **Tab switches don't pause the match**: the game and its battle animation are driven by the in-page module-level store; switching sessions or closing the tab won't interrupt AI thinking or the battle. The state lives only in page memory though: refreshing or closing the browser page resets the match. ## Game rules (Minimal Auto Chess) - **Board**: each side has a 4×7 deployment grid, coordinates `[row, col]` (row 0-3, col 0-6), mirrored into a 4×14 battlefield for combat. - **Unit pool**: a shared pool of 24 units across 1/2/3 cost, each with HP, attack, attack speed, attack range, race, class and a skill. - **Cost & star levels**: three copies of the same unit at the same star level merge into the next star (1★→2★→3★; 2★≈1.8× stats, 3★≈3.24×). The merged unit keeps the id of the last unit consumed, so the AI can chain actions on it. - **Traits**: 11 races/classes, each triggering a bonus when a fielded-count threshold is met (e.g., 2/4 冰裔, 2/3 星灵, 2/4 法师); only deployed units count. - **Round structure**: prep phase (buy, merge, deploy, level up, reroll) → auto battle (a low-fidelity simulation at 0.5 s per tick) → settlement (damage, income, win/loss streaks). - **Economy**: base income 5 + interest min(gold÷10, 5) + streak bonus (2-win streak +1, 4-win +2, 6-win +3) + win +1; leveling costs 4 gold for 4 exp (exp needed = level + 2, level cap 8). - **Winning & losing**: both sides start at 100 HP; the loser takes damage equal to the enemy's surviving units ×2, and hitting zero loses the game. - **Simplifications**: no item system; combat is a low-fidelity simulation so LLMs can follow it and computation stays bounded. ## Quick install DSH's standard plugin mechanism is bundle → profile: the package declares `dsh.bundle` in `package.json` and ships a patch file (`cordis.patch.yml`); use `dsh plugin` to install it into any profile: ```sh # Install from a local checkout (run inside the plugin directory; build lib/ first) pnpm run build dsh plugin --profile web add /Users/yejiming/Desktop/OpenSource/dsh-auto-chess ``` After installing, verify the config layer, then start (or restart) the Web UI: ```sh dsh --profile web --dump-config # the output should contain the auto-chess layer dsh web # the 自走棋 tab appears in the session tab bar after the restart ``` > The plugin is installed into the local `web` profile (`~/.dsh/profiles/web`, with `@deepseek-ai/dsh-auto-chess` in the bundle list as a link dependency). The Web process must restart to scan new client bundles and inject the client tab. Remove: `dsh plugin --profile web remove @deepseek-ai/dsh-auto-chess` removes both the dependency and its layer. ## Architecture The game itself (state, economy, traits, battle simulation) lives in the browser UI; the server only arbitrates the AI's prep-phase decisions, so illegal replies are rejected server-side and can never corrupt the browser's game. The game engine (`src/game/engine.ts`) is pure TypeScript, dependency-free and deterministically shared: the server clones the full board state from each AI request, validates and applies the model's action batch with the exact same engine the browser uses, and returns the all-valid action list plus the reasoning text; the browser then applies the accepted actions to its own authoritative state with the same engine. The two sides can never drift. ## Model calls ### Auto-chess decision request (auxiliary) #### What the model sees Every AI decision is a standalone auxiliary request to the selected provider/model route. The system prompt is either the user-edited text or the package default (full rules, a strict JSON-array return format, a correct example, common mistakes, and basic strategy); the single user message carries the side's complete board state (HP, gold, exp/level, board cap, bench JSON, board layout JSON, active traits, shop JSON, last round's battle result, and the known enemy composition: fielded count, star distribution, race/class counts and rough power), plus, on retry, the previous illegal reply and its rejection reason. A decision request can also carry a thinking level (`off`/`high`/`max`): the server forwards it as the request's reasoning effort only when the selected model advertises support; unsupported levels fall back to the model's own default. Optional per-request `actionTimeoutMs` and `maxActionOutputTokens` overrides replace the config defaults for that request only. The reply's reasoning block is returned to the browser as the `reasoning` field alongside the action results. The model returns a JSON array (at most 4 actions): `{"action":"buy","name":"..."}`, `{"action":"sell","chess_id":N}`, `{"action":"move","chess_id":N,"pos":[r,c]}`, `{"action":"upgrade"}`, `{"action":"refresh"}`, `{"action":"end_prep"}`. The server validates the whole batch (gold, bench cap, board cap, coordinates in range, occupied cells, id existence, etc.); any illegal action rejects the entire batch and retries with corrective feedback (default max 3 attempts); the final failure returns `error` with `lastText`/`lastReason`, and the browser shows the error plus a retry button. #### Token effect The auxiliary request spends tokens on constructing the state (roughly 1-2 KB of board text + system prompt + `maxActionOutputTokens`, default 32000). Thinking levels above `off` add reasoning tokens that count against the same output budget: reasoning models may spend most of the budget thinking, so a truncated reply still parses as long as the JSON array is complete; only lost JSON triggers a retry. The request is fully independent of any agent conversation and is never written to the session log. #### KV cache effect Auxiliary requests are standalone model requests; their prompts change with every board position, so there is no stable shared prefix with agent traffic. Editing the system prompt replaces the text wholesale, defeating provider-side cross-game prefix reuse; the default prompt stays stable only while unedited. ## Configuration All fields have loader defaults; there are no library-level defaults. | Key | Description | |---|---| | `actionTimeoutMs` | End-to-end deadline for one AI decision attempt (default 300000 ms, i.e. 5 minutes); the UI no longer offers an edit entry. | | `maxActionOutputTokens` | Output-token cap for one AI decision reply. Default 32000 (generous, because reasoning models count thinking against the output budget); truncated replies still parse while the JSON is complete, and only lost JSON triggers a retry. No longer editable in the UI. | | `maxActionAttempts` | Total AI attempts per decision request; the last attempt carries corrective feedback (default 3). | ## Development ```sh pnpm run build # tsdown builds the node half + the browser bundle and emits type declarations pnpm run typecheck # tsc --noEmit pnpm test # vitest: engine (economy/merge/battle/settlement), reply parsing, request validation, prompt assembly node tests/e2e/gui-round.spec.mjs # end-to-end: run one full round against a separate dsh web test instance (--port 3095; the plugin must be installed with a working model configured) ``` Local dependencies point at the Harness SDK checkout through symlinks (`packages/*` and `vendor/*` under `~/.dsh/source/current`), the same approach as `dsh-gomoku`.