后续改内容或结算顺序时有冻结依据,不必再翻原始稿才能对齐。 Co-authored-by: Cursor <cursoragent@cursor.com>
4.6 KiB
Surfaces
How the project ships two usable fronts on one rules engine.
This is product/engineering framing, not a second ruleset.
Verdict
Yes. We maintain two surfaces:
| # | Surface | Who uses it | Job |
|---|---|---|---|
| 1 | Headless | Dev, AI, batch sims, CI, balance | Fast, no UI (or terminal-only). Same rules as the player game. |
| 2 | Interactive | Human players (and playtesters) | Click/type legal actions; see GameState and play GameEvents. |
They are not two games. They are two adapters over Game.Core (+ in-run progression such as RunProgress).
┌─────────────────────┐
│ Game.Core │
│ rules · RNG · AI │
│ RunProgress · packs │
└──────────┬──────────┘
┌───────────────┼───────────────┐
▼ ▼
Headless surface Interactive surface
tools / tests / batch playtest client → later Godot
1. Headless surface
Purpose: AI search, auto fights, full-run loops, regression tests, future balance batches. Anything that must be cheap to run and easy to pin with a seed.
Today:
tools/HeadlessBattle— single fight and--runfull run (terminal I/O is convenience, not a second rules layer)tests/Game.Core.Tests— pinned scenarios- In-process AI (
TurnBeamSearch) callingGetLegalActions/Execute/Clone
Later (same surface family):
- Batch sims / PT reports / seed sweeps
- Headless full-run without printing every step
- Replay from
seed + action log
Must:
- Depend only on Core (and content JSON), never on Godot or browser UI
- Stay deterministic: same seed + choices → same result
- Be the path AI and automated tests use in production of balance data
Must not:
- Reimplement card text, pack unlocks, or combat resolution “for speed”
- Become a simplified AI-only ruleset
2. Interactive surface
Purpose: What a human actually plays. Input becomes GameAction; screen shows state and events. Presentation only.
Today:
Game.Client— browser playtest (emoji/SVG placeholders)- Optional: interactive mode of
HeadlessBattle(numbered legal actions) as a thin terminal client — still surface 2 in spirit, but not the player-facing target
Later:
- Godot .NET desktop / Steam presentation
- Art pass replaces placeholders; rules stay in Core
Must:
- Call the same Core APIs as headless (
GetLegalActions,Execute, …) - Own layout, input, animation, audio, UX copy
- Refuse to “fix” rules bugs in the UI
Must not:
- Encode damage formulas, pack conditions, legal-move filters, or win checks locally
- Drift into a parallel progression model for “the real game”
Shared core (the only rules owner)
Both surfaces consume:
- Combat:
GameState,GameAction,GameEvent,GameRng - Progression:
RunProgress, rewards, Holder, simple upgrades, in-run packs - AI: evaluation + current-turn search that reads Core, does not own it
If a feature matters for “how the game works,” it lands in Core (and tests) first. Surfaces only expose or automate it.
How work usually splits
| Change type | Land in | Headless | Interactive |
|---|---|---|---|
| New rule / card effect / pack condition | Core + tests | exercise via CLI/tests | render/input only if needed |
| Faster AI / batch balance | Core + tools | primary | optional “show AI candidates” |
| UX, targeting UX, animation, art | Client (later Godot) | no | primary |
| Full-run loop (reward → upgrade → next fight) | Core + headless first | prove it | then wire the same loop in the client |
Default order stays simulation first: headless (or tests) prove the loop; interactive follows.
Roadmap mapping
- CLI combat / CLI full run → headless surface (also a crude interactive terminal)
- Playtest client → interactive surface v1
- Art pass / Godot → interactive surface v2 (same Core)
- Expansion packs → content + Core; both surfaces pick them up automatically
Non-goals
- Two rulebooks (player vs AI)
- Meta / cross-run unlocks on either surface
- Making the browser client a permanent architecture requirement (it is a playtest skin; Godot may replace it)
Related docs
- Principles:
docs/FOUNDATION.md(simulation vs presentation) - Layout & phases:
docs/ARCHITECTURE.md - AI constraints:
docs/AI.md - Run / Holder grammar:
docs/RULES.md