Files
dojo_cate/agents/plans/2026-09-25-dojo-board-and-sim.md
T
eisterman 7b76b10175 Add Monte Carlo simulator, game metrics and sim CLI (Phase 5)
`dojo sim` plays seeded games over a scenario matrix (variant, players,
Kiai bonus, start position, bot lineup) in parallel, prints a summary per
scenario and optionally writes one CSV row per game. Metrics are replayed
from the event log. Plan updated with the Phase 5 baseline.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 16:29:22 +02:00

179 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Il Labirinto del Dojo — board format, rules engine, simulator
## Goal and scope
A small tabletop game (1–2 h, target 4 players, playable 3–8), gifted to a friend.
Logic and balance first; graphics and cosmetics are out of scope for now.
Deliverables:
1. `RULES.md` — the rulebook (English while iterating; Italian final edition produced separately).
2. A text-based board format, with parser, full-board generator and validator.
3. A first playable board.
4. A rules engine + bots + Monte Carlo simulator to validate balance and pacing,
and in particular to choose between rule variants A and B.
## Decisions log
| # | Decision | Why |
|---|---|---|
| D1 | Kumite loser loses 1 belt (−3), never below 0 | Developer requirement. |
| D2 | "Punching up": belts change only on equal-belt fights or upsets | Prevents leader farming weak players; built-in catch-up. |
| D3 | Kumite: 1d6 each, higher belt +1 per belt of gap, capped at +2 | Uncapped d6 bonus made fights predetermined (gap 5 = 100%). |
| D4 | **Senshu rule**: ties go to the attacker | WKF-inspired, one rule without exceptions, makes attacking slightly favourable so fights happen. |
| D5 | Kumite triggers on **crossing** (entering an occupied square), always forced; winning attacker continues moving; chains allowed | Developer requirement; route choice gives the agency. |
| D6 | Kumite loser is sent to own Start (corner Safe Room) | Developer requirement. |
| D7 | Corner Safe Rooms: 3×3, 2 exits, no Kumite/effects/targeting, stacking allowed | Prevents spawn camping / spawn lock. |
| D8 | Movement 2d6, exact count, orthogonal, no re-entering a square in the same move | Developer requirement + clarity. |
| D9 | Most squares carry an effect from a small vocabulary (7 codes); ~30–40% Tatami | Developer requirement; limits reading load and analysis paralysis. |
| D10 | Effects trigger only when stopping on a square | Avoids multiple triggers per move. |
| D11 | Nebbia works like Monopoly Chance: the square stays active | Developer requirement. |
| D12 | Progress track 0–17, 3 stripes per belt; squares ±1, Kumite ±3 | Fine-grained board income without breaking pacing. Physical tracking method decided later. |
| D13 | Nebbia deck is mostly movement/interaction/Tecniche, very few flat stripes | Prevents "stripe farming" that would make players avoid fights. |
| D14 | Tecnica cards: hold max 1, played after dice, attacker first | Converts Nebbia luck into a reason to fight. |
| D15 | Passo Falso returns to the start of the current move; skip-turn allowed only if rare (1 card) | Developer requirement. |
| D16 | Tunnel: when stopping on it you may teleport; movement is over; occupied exit starts a Kumite | Developer confirmed. |
| D17 | Meditazione: pass-through; ending on it forces one extra step | Developer rule. |
| D18 | Central Dojo is one logical space, several entrances, Brown only, no exact count to enter | Avoids end-game "need exactly a 3" frustration. |
| D19 | Final Exam: highest opponent defends; challenger wins → game; challenger loses → −3 and Start; defender gains nothing | Avoids the top-two flip-flop. Brown's extra stripes protect against the board, not the exam. |
| D20 | Two rule variants to be decided by simulation: **A** (no Brown cap, higher-beats-lower 0) vs **B** (squares/cards cannot move 14→15, higher-beats-lower +1) | Neither is clearly better a priori. |
| D21 | Board 16×16, 4×4 Dojo in the centre, authored as one 8×8 quadrant rotated 3× (C4 symmetry) | Fairness by construction. Small asymmetries may be wanted later (see Q2). |
| D22 | Tooling in Python, environment managed with **uv** | Developer choice. |
| D23 | Rules drafted in English; Italian final edition generated separately | Developer choice. |
| D24 | Board format: see `board/FORMAT.md`. Tunnel pairing across quadrants via `quadrant_offset` in `board/legend.toml` (cell `a` in quadrant k ↔ cell `b` in quadrant k+o) | Every tunnel cell keeps exactly one partner under rotation. |
| D25 | Only the Tunnel square is optional. Sfida (square and card), Irimi and Tunnel Segreto are mandatory; every Nebbia card must be resolved | Sfida prevents stalling; Nebbia cards are chances that can also turn out badly. |
| D26 | Tecnica cards are secret: drawn face down, others only know *whether* you hold one, a replaced Tecnica is discarded face down, revealed only when played | Hidden information and bluffing. Bots must not see opponents' Tecnica identity (Phase 4). |
| D27 | Edge rules (RULES.md v0.5, inline + FAQ): boxed-in → move as far as possible (safeguard; boards have no dead ends); Uke-saved attacker stops on its previous square, turn ends, no square effect; extra-step rule for ending on any unfightable pawn; tunnel exits with a meditating pawn unusable; meditating players cannot defend the Exam; Tecnica window after both dice, attacker first; relocations never fight or trigger squares; Tsugi-ashi has its own origin; becoming Brown mid-move opens the Dojo | Developer confirmed all ten. |
## Phases
### Phase 0 — Rules consolidation
- [x] 0.1 Write `RULES.md` v0.4 with variants A/B marked.
- [x] 0.2 `RULES.md` v0.5: mandatory effects, Tecnica secrecy, edge rules and FAQ (D25–D27).
### Phase 1 — Project scaffold and board format
- [x] 1.1 `uv init` project (package `dojo`), Python version pinned via uv; dev deps: pytest, ruff. No runtime deps (legend in TOML, parsed with stdlib `tomllib`).
- [x] 1.2 `board/FORMAT.md` — the format specification:
- [x] ASCII grid: `+` corners, `|` vertical walls, `---` horizontal walls, 3-char cells with a centred code.
- [x] Cell codes: `S` safe room, `D` dojo, `.` `A` `F` `?` `P` `K` (Allenamento/Fatica were `+`/`-`, changed so no code looks like a wall), digits = tunnel pair ids.
- [x] Outer border must be fully walled.
- [x] Dojo entrance = open edge between a `D` cell and a non-`D` cell.
- [x] Cross-quadrant tunnel links declared in `board/legend.toml`.
- [x] 1.3 Parser: `quadrant.txt` → cell grid + wall set.
- [x] 1.4 Generator: rotate the quadrant 90°/180°/270° around the board centre → full 16×16 model; write `board/board.txt` (generated, never hand-edited).
- [x] 1.5 Graph model: cells as nodes, open edges as links. The Dojo is handled as one cell set (impassable, entrances = open D/non-D edges); treating it as a single space for movement lands in the rules engine (3.3).
- [x] 1.6 Validator:
- [x] every non-Dojo square reachable from every Safe Room;
- [x] each Safe Room has exactly 2 exits, on different sides;
- [x] no Safe Room lies on a shortest path between two other points (not a shortcut);
- [x] every tunnel id has exactly one partner;
- [x] Dojo has ≥1 entrance per quadrant;
- [x] report: square-type mix and Tatami %, shortest distance from each start to the Dojo.
- [x] 1.7 CLI: `uv run dojo board validate`, `uv run dojo board render`.
- [x] 1.8 Unit tests for parser, rotation, validator.
### Phase 2 — First board
- [x] 2.1 Draft the first quadrant (`board/quadrant.txt`), following D7, D9, D18.
- [x] 2.2 Validate and iterate until clean.
- [x] 2.3 Developer review of the layout (approved as draft 1).
### Phase 3 — Rules engine
- [x] 3.1 Game state: pawns, track positions, hands, deck/discard, meditation status, turn order.
- [x] 3.2 Rule configuration object: variant A/B, Kiai value, stripes per belt, starting position, player count.
- [x] 3.3 Movement enumeration: all legal exact-length self-avoiding walks, with Kumite on crossing, chain continuation, Dojo entry with ≥1 step left, Meditazione extra step.
- [x] 3.4 Kumite resolution: gap bonus cap, Senshu, Tecnica timing, consequences table per variant, Uke placement.
- [x] 3.5 Square effects and Nebbia cards (all 13 card types).
- [x] 3.6 Final Exam.
- [x] 3.7 Unit tests covering every edge rule in `RULES.md` (`tests/test_engine.py`, `tests/test_movement.py`; mutation-checked: breaking Senshu, bonus cap, chain continuation, Meditazione step, variant B cap, Safe Room or meditator immunity each fails tests).
- Code: `src/dojo/rules.py` (config, variants, deck), `movement.py` (walks), `engine.py` (Game, Agent protocol, Randomness), `events.py` (event log for metrics).
### Phase 4 — Bots
- [x] 4.0 Hidden information: agents receive a `GameView` snapshot (`Game.view(player)`), never the `Game`: opponents' Tecnica shows only as `holds_tecnica`; the event log (which records `CardDrawn`) is not visible to agents (D26).
- [x] 4.1 Random bot (baseline): `RandomAgent(rng)` in `src/dojo/bots.py`.
- [x] 4.2 Heuristic bot: `HeuristicAgent(profile)`. Value = track position; once Brown, plus `DOJO_PULL` (0.3) per step closer to the Dojo. Scores each offered path as the expected value change over the chain of Kumite (exact 1d6-vs-1d6 odds with belt bonus and Senshu, ±0.1 per Tecnica held), then the stop square's effect (Nebbia worth 0.2, Sfida = best target, Tunnel = better of staying/exit). Exam = 100 × pass chance. Tecnica played only when losing (Kiai only if it turns the fight). Known simplifications: Meditazione extra step, Passo Falso origin after a chain, opponents' replies.
- [x] 4.3 Profiles (`PROFILES`): losses weighted by `risk`, opponents' value changes by `rivalry`, Tecnica keep order. balanced (1.0, 0.5, Kiai>Kaeshi>Uke), farmer (2.5, 0.2, Uke first), aggressive (0.5, 1.0).
- Smoke run (60 seeds, real board, 1 heuristic vs 3 random): heuristic wins 58–60/60 for every profile and both variants; ~20–22 rounds; ~1,300–1,450 turns/s. Four heuristic bots: ~18.7 rounds. Finding to check in Phase 5/6: attacking a higher belt risks nothing for a non-Brown player (losing costs 0 stripes; Start is no worse than elsewhere), so every profile attacks upward.
### Phase 5 — Simulator and metrics
- [x] 5.1 Monte Carlo runner over a config matrix; seeded and reproducible. `src/dojo/sim.py`: `Scenario` (variant × players × Kiai bonus × start position × lineup), `matrix`, `run(files, scenarios, games, seed, jobs)`. Game `i` uses seed `base + i` in every scenario (same dice across scenarios); the lineup rotates one seat per game; process pool, results independent of `jobs`.
- [x] 5.2 Metrics (`src/dojo/metrics.py`, computed from the event log; Kumite counts exclude the Exam):
- [x] game length in rounds and estimated minutes (45 s per turn + 30 s per Kumite);
- [x] Kumite per round, upset rate, higher-beats-lower rate, longest chain per game (Kumite attacked by the active player in one turn);
- [x] win rate by seat and by bot profile (per seat occupied);
- [x] exam attempts per game, exam success rate;
- [x] turns started on 14, longest streak (variant B deadlock check);
- [x] net stripe income per turn: squares vs cards vs Kumite vs Exam; square income in the player's own quadrant vs elsewhere (home farming);
- [x] lead changes per game (strict leader), final spread.
- [x] 5.3 Report output: `uv run dojo sim` prints a text summary per scenario; `--csv` writes one row per game (`src/dojo/report.py`). Commands in `README.md`.
- Baseline (1,000 games per variant, 4 players, lineup balanced/farmer/aggressive, seed 0), for Phase 6:
| Metric | A | B |
|---|---|---|
| Rounds mean (p10–p90) | 18.4 (14–23) | 13.1 (9–17) |
| Minutes mean (p10–p90) | 66 (50–85) | 46 (33–62) |
| Kumite per round | 1.25 | 1.16 |
| Upsets / higher beats lower | 29% / 55% | 27% / 52% |
| Exam attempts per game, pass rate | 1.26, 79% | 1.22, 82% |
| Turns on 14 per game, longest streak | 1.26, 4 | 1.73, 7 |
| Income per turn: square (home / away), card, Kumite | +0.52 (0.38 / 0.14), 0.00, +0.04 | +0.54 (0.39 / 0.14), 0.00, +0.22 |
| Lead changes, final spread | 5.6, 9.3 | 3.5, 11.5 |
| Wins by seat | 23–27% | 23–27% |
### Phase 6 — Balance experiments
- [ ] 6.1 Variant A vs B.
- [ ] 6.2 Kiai +2 vs +1.
- [ ] 6.3 Player counts 3/4/6/8; start on 3 for 6+.
- [ ] 6.4 Square mix tuning; board income per turn (target ≈ 0.8 stripes/turn net, to be confirmed).
- [ ] 6.5 Update `RULES.md` with the decisions.
### Phase 7 — Finalisation (later)
- [ ] 7.1 Freeze rules.
- [ ] 7.2 Italian edition.
- [ ] 7.3 Physical tracking method and components; printable board.
- [ ] Tunnel pairs need unique labels on the printed board (the text format repeats each digit in all 4 quadrants).
## Questions
### Resolved
- Tie rule → Senshu (attacker wins).
- Several opponents on one square → impossible (loser is teleported); only exception handled by the Meditazione extra-step rule.
- Exam loss → −3 and Start; defender gains nothing.
- Tunnel → teleport optional on stop, movement over.
- Optional vs mandatory effects → D25.
- Engine edge rules → D27.
- Sfida squares → keep, balance via low count.
- Il Peso della Cintura → keep (1 card).
- Player count → focus 4, support 3–8.
- Tests → the agent runs the suite (`uv run pytest`), plus `ruff check`, `ruff format --check`, `pyright`.
- Minutes estimate → 45 s per turn, +30 s per Kumite.
- Safe Room placement → the owner places the pawn on any square of their room (setup and when sent to Start).
### Open
- Q2 Board asymmetry: keep the quadrant as source of truth, or later switch to the full board as source of truth with a symmetry *report* (warnings) instead of generation? *(Deferred by the developer.)*
- Q4 Target net income per turn — derived from 1–2 h and ~15–20 turns per player; confirm with first simulations. Baseline measured net income ≈ 0.56 (A) / 0.75 (B) stripes per turn.
### Risks
- Movement enumeration: self-avoiding walks of length up to 12 can explode in open rooms. Keep rooms small; prune by outcome (destination + ordered opponents crossed + Dojo entry).
- Engine speed ~1,400 turns/s (random and heuristic bots, real board). If Phase 5 needs more: precompute neighbour lists in `Board`.
- Simulation conclusions depend on bot quality; compare several profiles before trusting a result.
- Board draft 1 puts 4 Allenamento in each 3x3 room next to the Safe Room: possible "home farming" (see the Phase 2 review). Measured (Phase 5 baseline): square income is +0.38/turn in the own quadrant vs +0.14/turn elsewhere; 6.4 decides whether that is a problem.
## External documentation
- uv — https://docs.astral.sh/uv/ — project init, dependency and Python version management.
- Python `tomllib` — https://docs.python.org/3/library/tomllib.html — stdlib TOML parser for `legend.toml`.
- pytest — https://docs.pytest.org/ — unit tests.
- Ruff — https://docs.astral.sh/ruff/ — lint and format.
- WKF Kumite competition rules (wkf.net, "Rules") — source of the Senshu concept.