From 9522224bf9045c7aa8fee9258226afdc2215d100 Mon Sep 17 00:00:00 2001 From: Halit Aksoy Date: Fri, 16 Oct 2026 14:46:50 +0300 Subject: [PATCH] docs(tutorial): add the spec Requirements, design and tasks for a rules page, interactive lessons and a practice game with hints against the Easy bot. --- docs/specs/tutorial/design.md | 145 ++++++++++++++++++++++++++++ docs/specs/tutorial/requirements.md | 83 ++++++++++++++++ docs/specs/tutorial/tasks.md | 16 +++ 3 files changed, 244 insertions(+) create mode 100644 docs/specs/tutorial/design.md create mode 100644 docs/specs/tutorial/requirements.md create mode 100644 docs/specs/tutorial/tasks.md diff --git a/docs/specs/tutorial/design.md b/docs/specs/tutorial/design.md new file mode 100644 index 0000000..2c35773 --- /dev/null +++ b/docs/specs/tutorial/design.md @@ -0,0 +1,145 @@ +# Tutorial: design + +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, + that player loses. + +## Shared code (`packages/core/src/tutorial/`) + +```ts +type RuleId = "board" | "sowing" | "extraTurn" | "evenCapture" | "emptyPitCapture" | "endOfGame" | "clock"; + +interface RuleExample { + board: number[]; // 14 stone counts, player 1 (bottom) to move + pit: number; // board index played +} + +type LessonTask = "anyMove" | "extraTurn" | "evenCapture" | "emptyPitCapture" | "endGame"; + +interface Lesson { + board: number[]; // player 1 (bottom) to move + task: LessonTask; + taskKey: string; // e.g. "LessonTaskExtraTurn": "End your move in your store" + opponentReply?: number; // board index the opponent plays after a fulfilled move, if the lesson needs one +} + +interface Rule { + id: RuleId; + titleKey: string; + textKeys: string[]; // at most three sentences (R1.2) + example?: RuleExample; + lesson?: Lesson; // every rule except the clock (R2.1) +} + +export const RULES: Rule[]; + +// The immediate effect of playing a pit, by running the engine on a copy of the board. +interface MoveOutcome { + path: number[]; // where the stones went (getSowingPath) + extraTurn: boolean; + capture?: "even" | "emptyPit"; + captured: number; // stones taken from the opponent's side + storeGain: number; + endsGame: boolean; + board: number[]; // after the move +} +export function getMoveOutcome(board: number[], player1ToMove: boolean, pit: number): MoveOutcome; +export function fulfilsTask(task: LessonTask, outcome: MoveOutcome): boolean; + +// "Why did that happen" for a wrong lesson move and for the practice game (R2.3, R3.3). +export function explainMove(outcome: MoveOutcome, mover: "you" | "opponent" | "bot", t): string; +``` + +`getMoveOutcome` builds the position with `createGameFromBoard` (core history helpers), plays the pit and reads the +game steps, as `describeMove` does for the move list. `describeMove` gains `capture` kind and `captured`, so the +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 | + +Texts: `RuleTitle`, `RuleText1..3`, `LessonTask…`, `MoveExplain…`, `HowToPlay`, `Learn`, `Hints`, +`NewToMangala`, `StartTutorial`, `NotNow`, `PracticeGame`, in `en.ts` and `tr.ts`. + +Storage helpers next to the rules: `getCompletedLessons` / `completeLesson`, `isTutorialBannerHidden` / +`hideTutorialBanner`, `getHintsEnabled` / `setHintsEnabled`, `markGamePlayed`. + +## Web app + +Routes: +- `/rules`: `RulesPage` with `RulesView`. +- `/learn`: the lesson list with progress, then the practice game button. +- `/learn/:ruleId`: `LessonPage`. + +Components: +- `RulesView`: the rules in order; each with its texts, a `RuleBoard` and "Try it" linking to the lesson (R1.5). +- `RuleBoard`: two `MiniBoard`s, before and after the example move, the played pit and the landing pit marked. +- `RulesPanel`: `RulesView` in a side panel (full screen below 600 px) over the game page; opened from a "How to + play" button in the game page's header; the game keeps running behind it (R1.4). +- `AppHeader`: a help icon button "How to play" on every page, going to `/rules` (or opening `RulesPanel` on the game + page). +- `LessonPage`: the rule's text and task above a `BoardView` on a local game (`createGameFromBoard`), animated with + `PitAnimator` like a live game. On a move: `getMoveOutcome`, animate, then praise and "Next lesson" or + `explainMove` and "Try again", which resets the board. A scripted `opponentReply` is played after a short pause. +- `TutorialBanner` on the home page for newcomers (R4). +- Hints (R3): in `GamePage`, when the opponent is a bot and hints are on, `BoardView` gets a `hints` map + (pit → "Extra turn" / "Captures 5"), drawn as a chip on the pit like the move preview; below the board a line with + `explainMove` of the last move. A "Hints" switch in the game page's toolbar, only in bot games. +- Practice game: the lesson list's last button sets hints on and goes to `/loby?bot=easy`. + +## Mobile app + +`RulesScreen`: the rules' texts and a simple stone-count drawing of each example (before/after), reachable from +`HelpScreen` and from the game screen's header (R5.1). + +## Testing +- Core: every lesson position fulfils its task with some move and (except `anyMove`) fails it with another; + `getMoveOutcome` against the engine on random positions; every rule example produces the effect its rule is about + (the even capture example really captures, etc.); `explainMove` for each kind of move in both languages; every + text key exists in `en` and `tr`. +- Web: build and the temporary type check. +- End to end in Chrome: a newcomer sees the banner and "Not now" hides it; all six lessons, each first with a wrong + move (explanation, reset) and then a right one; the list shows them done; the rules page and the rules panel during + a game (the clock goes on); a practice game shows hints and explanations, and a game between two people shows + none. +- Mobile: type check and bundle; the rules screen on a device when one is available. diff --git a/docs/specs/tutorial/requirements.md b/docs/specs/tutorial/requirements.md new file mode 100644 index 0000000..eab42ad --- /dev/null +++ b/docs/specs/tutorial/requirements.md @@ -0,0 +1,83 @@ +# Tutorial: requirements + +Status: approved, being implemented on branch `feature/tutorial` (based on `develop`). +Scope: `packages/core`, web app; the mobile app gets the rules only (see R5). + +## Goals + +Someone who has never played Mangala can learn it in a few minutes, and anyone can look a rule up later, also in +the middle of a game. Three parts, from reading to playing: + +1. **Rules**: one page with every rule, a short text and a small board for each. Always reachable. +2. **Lessons**: one short interactive step per rule; the player makes the move the rule is about on a prepared + board and sees what happens. Runs in the browser, without the server. +3. **Practice game**: a real game against the Easy bot with hints: which moves give an extra turn or capture, and + after every move a sentence about what happened. + +## Terms + +- **Store**: the large pit on each side (in the texts "hazine"); stones in it count for the player. +- **Lesson**: one rule taught on a prepared position with a task, e.g. "End your move in your store". +- **Hint**: in the practice game, the effect a move would have right away (extra turn, capture, stones to store). + Hints never look further ahead than the move itself. +- **Newcomer**: someone on a device where no game has been played and the tutorial has not been opened or + dismissed. + +## Requirements + +### R1. Rules +- R1.1 A rules page ("How to play" / "Nasıl oynanır") lists, in order: the board and the goal, sowing, the extra + turn, capturing an even pit, capturing from an empty pit, the end of the game and who wins, and the clock. +- R1.2 Each rule has at most three sentences and a small board that shows it (before and after the move). +- R1.3 The texts describe exactly what the game does (`packages/mancala.js` and the backend's port), including the + details beginners miss: the first stone goes back into the pit it was taken from, a single stone moves to the next + pit, the opponent's store is skipped. +- R1.4 The rules can be reached from the header on every page, and from the game page without leaving the game + (a panel over the game; the game and its clock go on). +- R1.5 Each rule links to its lesson. + +### R2. Lessons +- R2.1 There is a lesson for each rule in R1.1 except the clock: six in all, in the order of R1.1. +- R2.2 A lesson shows the rule's text, a board with a prepared position and a task. The player plays on the real + board (same look, animation and move preview as a game). +- R2.3 A move that fulfils the task is praised and the next lesson is offered. Any other move is animated, then + explained ("This move ended in the opponent's pit, so the turn passed") and the position is set back so they can + try again. +- R2.4 Where a lesson needs the opponent to move, the opponent's move is scripted and shown like the bot's. +- R2.5 Lessons work without a connection to the server and need no login. +- R2.6 Completed lessons are remembered on the device and marked in the list. Lessons can be taken in any order + and repeated. +- R2.7 After the last lesson, the player is offered the practice game. + +### R3. Practice game +- R3.1 The practice game is a game against the Easy bot with hints turned on, started from the tutorial. It is + otherwise a normal bot game (clock, saving, history). +- R3.2 While it is the player's turn, every pit whose move gives an extra turn or captures is marked, with what it + does ("Extra turn", "Captures 5"). +- R3.3 After every move, of either side, one sentence says what happened, built from the move: e.g. "The bot's + last stone landed in its store: it plays again" or "You captured 6 stones from an even pit". +- R3.4 Hints can be turned on and off in every game against the bot; the choice is remembered on the device. +- R3.5 Hints are only available in games against the bot. Games between people never show them, also not to + spectators. + +### R4. Newcomers +- R4.1 A newcomer sees a banner on the home page: "New to Mangala? Learn it in 3 minutes" with "Start" and + "Not now". "Not now" hides it for good on that device. +- R4.2 The banner does not show once the tutorial has been opened or a game has been played on the device. +- R4.3 Nothing is forced: a newcomer can start a game without the tutorial. + +### R5. Mobile app +- R5.1 The mobile app shows the same rules (same texts, from `packages/core`) on a rules screen reachable from the + help screen and from the game screen's header. +- R5.2 Lessons and the practice game's hints on mobile are out of scope until the mobile app gets the new design + (`TODO.md`, Web redesign). + +### R6. Languages +- R6.1 All texts exist in English and Turkish. Turkish uses the usual Mangala words (kuyu, hazine, taş). + +## Out of scope (TODO) +- Lessons and hints in the mobile app (R5.2). +- Hints that look ahead (warning about a move that gives the opponent a capture); the bot's search could provide + them later. +- Strategy lessons beyond the rules. +- Keeping tutorial progress per account instead of per device. diff --git a/docs/specs/tutorial/tasks.md b/docs/specs/tutorial/tasks.md new file mode 100644 index 0000000..0ca7ebe --- /dev/null +++ b/docs/specs/tutorial/tasks.md @@ -0,0 +1,16 @@ +# Tutorial: tasks + +Work through the tasks in order; each ends with its tests passing and a commit. Tick a task when it is committed. + +- [x] 1. **Spec**: requirements, design and tasks. +- [ ] 2. **Core**: `RULES` with examples and lessons, `getMoveOutcome`, `fulfilsTask`, `explainMove`, `captured` and + the capture kind in `describeMove`, the storage helpers, texts in English and Turkish; unit tests. +- [ ] 3. **Rules on the web**: `RuleBoard`, `RulesView`, `/rules`, the help button in `AppHeader`, `RulesPanel` on the + game page. +- [ ] 4. **Lessons**: `/learn` with progress, `LessonPage` with the local game, animation, praise, explanation and + reset, scripted opponent replies. +- [ ] 5. **Newcomers and practice game**: `TutorialBanner`, `markGamePlayed`, the practice game button. +- [ ] 6. **Hints**: hint chips on `BoardView`, the explanation line and the "Hints" switch in bot games on `GamePage`. +- [ ] 7. **Mobile rules**: `RulesScreen`, links from the help and game screens; type check and bundle. +- [ ] 8. **End to end and docs**: the Chrome tests from `design.md`; `AGENTS.md` (specs list), `TODO.md` (out of scope + items).