See `requirements.md`; requirement ids (R1.1, …) are referenced below. Builds on `web-redesign` (board, pages,
themes) and `game-bot` (the Easy bot for the practice game).
## Decisions
| Decision | Reason |
|---|---|
| Rules, lessons and hints are defined once in `packages/core` | The web and mobile apps show the same rules (R5.1) and the texts stay in one place. |
| The "after" board of every example and the outcome of every lesson move come from running the engine (`mancala.js`), never written by hand | The texts and pictures cannot drift from what the game does (R1.3). A rule change shows up in the tutorial at once, and a test fails if a lesson no longer works. |
| Lessons run entirely in the client on the TypeScript engine | No server, no login, no waiting (R2.5). The engine is already in every client. |
| A lesson's task is an outcome ("end in your store"), not a pit | Players who find another move with the same effect are right too; the check is the engine's game steps, the same ones the move list uses. |
| The practice game is an ordinary Easy bot game with hints on; hints are a client feature of any bot game | No new server state (R3.1). Hints only show the immediate effect of a move, which the board's preview mostly shows already, so they give little away; still, they are limited to bot games (R3.5). |
| Hints look one move ahead only | They explain the rules, not strategy. Looking further would need the bot's search on the client or a new server message (out of scope). |
| Progress, "not now" and the hints setting are kept in the device's storage | Enough for learning the rules (R2.6, R3.4, R4.1); accounts are not needed for any of it. |
| Mobile gets the rules screen only for now | The mobile app still has the old board and game screen; lessons and hints there would be built twice (R5.2). |
| The end-of-game lesson teaches the current rule | `TODO.md` (Game rules) asks whether the remaining stones should always go to the player whose side is empty. The tutorial follows the engine; because its boards come from the engine, a rule change only needs the texts updated. |
## The rules as taught (R1.3)
Checked against `packages/mancala.js` (`Board.move`, the four `GR…` rules):
1.**Board and goal**: two rows of 6 pits with 4 stones each, a store on each side. Stones in your store are yours;
at the end, more stones in your store wins (48 stones: 25 always wins). A draw is possible.
2.**Sowing**: take all stones from one of your pits; put the first back into the same pit and one into each pit
after it. A single stone moves to the next pit. Your own store gets a stone on the way, the opponent's is
skipped.
3.**Extra turn**: if the last stone lands in your store, you play again.
4.**Even capture**: if the last stone lands in an opponent's pit and makes its count even, you take all stones in
it.
5.**Empty pit capture**: if the last stone lands in an empty pit of yours and the opponent's pit opposite has
stones, you take both.
6.**End of the game**: the game ends when one side's pits are empty; the player whose side is empty gets the
stones left on the other side. Then the stores are counted.
7.**Clock**: 5 minutes per player for the whole game; the clock of the player to move runs; when it reaches zero,
move list, hints and explanations share one reading of the steps.
Lesson positions (player 1, bottom, to move; pits 0–5, store 6), checked with the rules above. Task 2 tests that
each has a move that fulfils its task and, where there is more than one legal move, one that does not:
| Lesson | Board | Task | Moves that fulfil it |
|---|---|---|---|
| Board and goal | start position | `anyMove` | all |
| Sowing | `[4,4,1,4,4,4, 1, 4,4,4,4,4,4, 2]` | `anyMove`; the text points at pit 3's single stone, which moves to pit 4 | all |
| Extra turn | start position | `extraTurn` | pit 4 |
| Even capture | `[4,4,4,4,4,4, 0, 4,3,4,4,4,4, 1]` | `evenCapture` | pit 6: its last stone makes the opponent's 3 a 4 |
| Empty pit capture | `[2,0,4,4,4,4, 3, 4,4,4,4,5,4, 2]` | `emptyPitCapture` | pit 1: its last stone lands in the empty pit 2, opposite 5 stones |
| End of the game | `[0,0,0,0,0,1, 20, 2,3,1,0,2,1, 18]` | `endGame` | pit 6 (the only move): your side is empty and the 9 stones opposite are yours, 30–18 |