feat: rated Puzzles with Themes, Hints, Replays and a History #554

Merged
root merged 50 commits from feat/puzzles-440 into main 2026-10-19 09:53:32 +00:00
root commented 2026-10-18 13:02:16 +00:00 (Migrated from gitlab.core-project.space)

Summary

A signed-in Player trains on rated Puzzles (a curated Lichess sample), with a Glicko-2 Puzzle rating, Themes, Hints, unrated Replays and a History.

sequenceDiagram
    participant Page as /puzzles
    participant API as puzzle router
    participant DB as Postgres
    Page->>API: next({ theme? })
    API->>DB: pending Attempt, else unseen Puzzle within ±100, widening
    API-->>Page: FEN, opponent's first Move, Color, play: attempt|replay
    Page->>API: move({ puzzleId, uci, line? })
    API->>DB: lock Attempt, check Move against the line (or any Checkmate)
    API-->>Page: correct + reply | solved | failed + right Move, rating change
apps/api/src/puzzles/
├── puzzle-rating.ts     # Glicko-2, provisional above 110 (ADR 0046)
├── puzzle-set.ts        # db:puzzles loader, engine-checks each line
└── puzzles.{module,controller,service}.ts   # next, move, hint, solution, replay, history, rating
apps/api/puzzles/lichess-sample.csv.gz       # 53,187 Puzzles, 2.6 MB, CC0
apps/front/src/puzzles/                      # Puzzles, History and Replay pages
apps/front/src/design-system/                # InfoTip, ui/select.tsx (new)
apps/e2e/tests/puzzles.spec.ts
  • Loader runs on every stack after the migrations (deploy/compose.yaml), outside the devtools gate.
  • On a stack with devtools on, next serves the first unseen Puzzle by id instead of a random one, so e2e knows what it gets (ADR 0039, one new line).
  • Rule changes the owner approved during the work: a puzzles layer in the API order (ADR 0044, API standards); puzzles/ in both apps' lint configs and standards rule 1; the whole-sample load test moved to test:slow (CONTRIBUTING.md, turbo.json).

Owner decisions on review points: history filters with result? like game.list; an unknown Theme is a plain BAD_REQUEST (closed enum); History pages with "Show more"; no credit line.

Evidence

  • Before: no Puzzle, Puzzle rating, Attempt or History in the app or API.
    After: gate green on e8d677c2 (lint, typecheck, build, format:check, test: engine 26 files, API 38 files / 955 tests, front 75 files). puzzles.spec.ts passes on the dev and built targets:
sign up → /puzzles → solve the mate in 1 → rating rises
next → wrong Move → failed, right Move shown → rating falls
/puzzles/history → both rows → replay the solved one → "Unrated", rating unchanged, row shows "Replayed"
  • Glicko-2 reproduces Glickman's worked example (rating within 0.01 of 1464.06; deviation and volatility to the published digits).
  • pnpm turbo run test:slow --filter=@chess/api: the whole committed sample loads on a fresh database, none rejected (~55 s).
  • Known flake, not from this branch: rpc-games.integration.test.ts "a Game whose write fails" fails about 1 run in 6 with Received unexpected parseComplete message from backend, also with main's games code.

Merge Danger

Door: two-way

Three new tables (puzzles, puzzle_ratings, puzzle_attempts), all additive; reverting is git revert plus the usual migration rollback (ADR 0031). Ratings and Attempts are new data, nothing existing is rewritten.

Blast Radius: deploys

  • Every stack's deploy gains a puzzles step (about a minute of CPU to engine-check 53k lines; #448 makes it cheaper). A failing load fails the deploy.
  • Preview stacks (devtools on) always serve the first unseen Puzzle by id, not a random one.
  • The sidebar and phone bar gain a Puzzles entry for every visitor.
  • Left for later: Puzzle data in the account export (#449); ~15 lines duplicated between the Puzzles and Replay pages, where removing them needs a lint-rule change (props spreading, or widgets importable by pages).

Closes #440
Closes #441
Closes #442
Closes #443
Closes #444
Closes #445
Closes #446
Closes #447

## Summary A signed-in Player trains on rated Puzzles (a curated Lichess sample), with a Glicko-2 Puzzle rating, Themes, Hints, unrated Replays and a History. ```mermaid sequenceDiagram participant Page as /puzzles participant API as puzzle router participant DB as Postgres Page->>API: next({ theme? }) API->>DB: pending Attempt, else unseen Puzzle within ±100, widening API-->>Page: FEN, opponent's first Move, Color, play: attempt|replay Page->>API: move({ puzzleId, uci, line? }) API->>DB: lock Attempt, check Move against the line (or any Checkmate) API-->>Page: correct + reply | solved | failed + right Move, rating change ``` ```text apps/api/src/puzzles/ ├── puzzle-rating.ts # Glicko-2, provisional above 110 (ADR 0046) ├── puzzle-set.ts # db:puzzles loader, engine-checks each line └── puzzles.{module,controller,service}.ts # next, move, hint, solution, replay, history, rating apps/api/puzzles/lichess-sample.csv.gz # 53,187 Puzzles, 2.6 MB, CC0 apps/front/src/puzzles/ # Puzzles, History and Replay pages apps/front/src/design-system/ # InfoTip, ui/select.tsx (new) apps/e2e/tests/puzzles.spec.ts ``` - Loader runs on every stack after the migrations (`deploy/compose.yaml`), outside the devtools gate. - On a stack with devtools on, `next` serves the first unseen Puzzle by id instead of a random one, so e2e knows what it gets (ADR 0039, one new line). - Rule changes the owner approved during the work: a `puzzles` layer in the API order (ADR 0044, API standards); `puzzles/` in both apps' lint configs and standards rule 1; the whole-sample load test moved to `test:slow` (`CONTRIBUTING.md`, `turbo.json`). Owner decisions on review points: `history` filters with `result?` like `game.list`; an unknown Theme is a plain `BAD_REQUEST` (closed enum); History pages with "Show more"; no credit line. ## Evidence - **Before:** no Puzzle, Puzzle rating, Attempt or History in the app or API. **After:** gate green on `e8d677c2` (lint, typecheck, build, format:check, test: engine 26 files, API 38 files / 955 tests, front 75 files). `puzzles.spec.ts` passes on the `dev` and `built` targets: ```text sign up → /puzzles → solve the mate in 1 → rating rises next → wrong Move → failed, right Move shown → rating falls /puzzles/history → both rows → replay the solved one → "Unrated", rating unchanged, row shows "Replayed" ``` - Glicko-2 reproduces Glickman's worked example (rating within 0.01 of 1464.06; deviation and volatility to the published digits). - `pnpm turbo run test:slow --filter=@chess/api`: the whole committed sample loads on a fresh database, none rejected (~55 s). - Known flake, not from this branch: `rpc-games.integration.test.ts` "a Game whose write fails" fails about 1 run in 6 with `Received unexpected parseComplete message from backend`, also with main's games code. ## Merge Danger **Door:** two-way Three new tables (`puzzles`, `puzzle_ratings`, `puzzle_attempts`), all additive; reverting is `git revert` plus the usual migration rollback (ADR 0031). Ratings and Attempts are new data, nothing existing is rewritten. **Blast Radius:** deploys - Every stack's deploy gains a `puzzles` step (about a minute of CPU to engine-check 53k lines; #448 makes it cheaper). A failing load fails the deploy. - Preview stacks (devtools on) always serve the first unseen Puzzle by id, not a random one. - The sidebar and phone bar gain a Puzzles entry for every visitor. - Left for later: Puzzle data in the account export (#449); ~15 lines duplicated between the Puzzles and Replay pages, where removing them needs a lint-rule change (props spreading, or widgets importable by pages). Closes #440 Closes #441 Closes #442 Closes #443 Closes #444 Closes #445 Closes #446 Closes #447
root commented 2026-10-18 13:11:05 +00:00 (Migrated from gitlab.core-project.space)

added 1 commit

  • f3dc8a8a - fix(repo): let the Puzzle sample into the image's build context

Compare with previous version

added 1 commit <ul><li>f3dc8a8a - fix(repo): let the Puzzle sample into the image&#39;s build context</li></ul> [Compare with previous version](/amaurycoudr/chess-project/-/merge_requests/54/diffs?diff_id=337&start_sha=e8d677c2da96274a45d0eedb8f516bc3c5ee8e57)
project_1_bot_a8dd9262857d850eef8dca5206e61de5 commented 2026-10-18 13:15:40 +00:00 (Migrated from gitlab.core-project.space)

Screenshots

Before is production, after is this merge request's review app at f3dc8a8.

Desktop

Page Before After
Home
Local game
Room

Mobile

Page Before After
Home
Local game
Room
<!-- chess-screenshots --> ### Screenshots Before is production, after is this merge request's review app at f3dc8a8. #### Desktop | Page | Before | After | | --- | --- | --- | | Home | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/before/home-desktop.png" width="400"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/after/home-desktop.png" width="400"> | | Local game | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/before/local-desktop.png" width="400"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/after/local-desktop.png" width="400"> | | Room | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/before/room-desktop.png" width="400"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/after/room-desktop.png" width="400"> | #### Mobile | Page | Before | After | | --- | --- | --- | | Home | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/before/home-mobile.png" width="200"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/after/home-mobile.png" width="200"> | | Local game | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/before/local-mobile.png" width="200"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/after/local-mobile.png" width="200"> | | Room | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/before/room-mobile.png" width="200"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/1979/artifacts/raw/review-screenshots/after/room-mobile.png" width="200"> |
root (Migrated from gitlab.core-project.space) merged commit into main 2026-10-19 09:53:33 +00:00
Sign in to join this conversation.
No description provided.