Initial commit, with rules 0.5 and basic bots

This commit is contained in:
2026-09-26 16:01:46 +02:00
commit 0aaf113fc3
32 changed files with 4206 additions and 0 deletions
@@ -0,0 +1,165 @@
# 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
- [ ] 5.1 Monte Carlo runner over a config matrix; seeded and reproducible.
- [ ] 5.2 Metrics:
- [ ] game length in rounds and estimated minutes (turn and fight duration as parameters);
- [ ] Kumite per round, upset rate, chain length;
- [ ] win rate by seat and by bot profile;
- [ ] exam attempts per game, exam success rate;
- [ ] rounds spent stuck on 14 (variant B deadlock check);
- [ ] stripe income split: squares vs cards vs Kumite;
- [ ] lead changes per game, final spread.
- [ ] 5.3 Report output (plain text / CSV).
### 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.
### 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). Measure stripe income by region in the simulator.
## 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.