Release: Quarkus backend, Keycloak login, game history, clock, bot, tutorial and rematch #3
@ -23,8 +23,8 @@ Online Mancala (the Turkish *Mangala* rules) with a web app, an Android/iOS app
|
|||||||
|
|
||||||
Specs that exist: `keycloak-auth` (login, guests, tokens), `game-history` (saved games, live games, replay),
|
Specs that exist: `keycloak-auth` (login, guests, tokens), `game-history` (saved games, live games, replay),
|
||||||
`game-clock` (5 minutes per player, ending games on time), `web-redesign` (the web app's look: themes, board,
|
`game-clock` (5 minutes per player, ending games on time), `web-redesign` (the web app's look: themes, board,
|
||||||
pages), `quarkus-backend` (the backend's move from Node to Java and Quarkus) and `game-bot` (playing against a bot
|
pages), `quarkus-backend` (the backend's move from Node to Java and Quarkus), `game-bot` (playing against a bot
|
||||||
at four levels).
|
at four levels) and `tutorial` (rules page, lessons, hints in bot games).
|
||||||
|
|
||||||
## Running locally
|
## Running locally
|
||||||
|
|
||||||
@ -114,6 +114,9 @@ which helps to check what the server holds.
|
|||||||
(`theme/themes.ts`) are passed to core's `ThemeManager`, which the mobile app uses with its own default themes.
|
(`theme/themes.ts`) are passed to core's `ThemeManager`, which the mobile app uses with its own default themes.
|
||||||
- Styles that need container queries (`@container`, `cqw`), such as the board's, are plain `.css` files imported by the
|
- Styles that need container queries (`@container`, `cqw`), such as the board's, are plain `.css` files imported by the
|
||||||
component; styled-jsx 5.0 does not handle them. `BoardView` fills its container and stands upright below 600 px.
|
component; styled-jsx 5.0 does not handle them. `BoardView` fills its container and stands upright below 600 px.
|
||||||
|
- The rules, lessons and move explanations live in `packages/core/src/tutorial`; their boards after a move come from
|
||||||
|
the engine (`getMoveOutcome`). After a rule change, run core's tests: they fail if a lesson no longer works, and
|
||||||
|
the rule texts in `en.ts`/`tr.ts` may need updating.
|
||||||
- When a script drives the app in Chrome, wait for the header (`.hdr`) and a few seconds after the first load before
|
- When a script drives the app in Chrome, wait for the header (`.hdr`) and a few seconds after the first load before
|
||||||
navigating; navigating during the guest login leaves an invalid token. Open the lobby with the "New Game" button,
|
navigating; navigating during the guest login leaves an invalid token. Open the lobby with the "New Game" button,
|
||||||
not by URL, or the matchmaking message goes out before the WebSocket is open.
|
not by URL, or the matchmaking message goes out before the WebSocket is open.
|
||||||
|
|||||||
10
TODO.md
10
TODO.md
@ -65,6 +65,16 @@ See `docs/specs/game-bot`.
|
|||||||
- [ ] Tune the levels from real games (`games.bot` holds each game's level, strength and seed)
|
- [ ] Tune the levels from real games (`games.bot` holds each game's level, strength and seed)
|
||||||
- [ ] Try the bot on a mobile device; only the type check and the bundle were run
|
- [ ] Try the bot on a mobile device; only the type check and the bundle were run
|
||||||
|
|
||||||
|
## Tutorial
|
||||||
|
|
||||||
|
See `docs/specs/tutorial`.
|
||||||
|
|
||||||
|
- [ ] Lessons and hints in the mobile app, once it has the new design (it has the rules screen)
|
||||||
|
- [ ] Hints that look ahead, e.g. a warning when a move lets the opponent capture (the bot's search could do it)
|
||||||
|
- [ ] Strategy lessons beyond the rules
|
||||||
|
- [ ] Tutorial progress per account instead of per device
|
||||||
|
- [ ] If the end-of-game rule changes (Game rules below), update `RuleEndOfGameText2` in `en.ts` and `tr.ts`
|
||||||
|
|
||||||
## Web redesign
|
## Web redesign
|
||||||
|
|
||||||
See `docs/specs/web-redesign`.
|
See `docs/specs/web-redesign`.
|
||||||
|
|||||||
@ -144,3 +144,10 @@ Components:
|
|||||||
a game (the clock goes on); a practice game shows hints and explanations, and a game between two people shows
|
a game (the clock goes on); a practice game shows hints and explanations, and a game between two people shows
|
||||||
none.
|
none.
|
||||||
- Mobile: type check and bundle; the rules screen on a device when one is available.
|
- Mobile: type check and bundle; the rules screen on a device when one is available.
|
||||||
|
|
||||||
|
Checked end to end in Chrome on 2026-10-16: a newcomer's banner, "Not now" hiding it, and the banner gone after the
|
||||||
|
lessons; all six lessons from the banner, the four with a wrong move first (explained, then reset) and each done;
|
||||||
|
the list showing all six done and the last lesson offering the practice game; the rules page and, during a game,
|
||||||
|
the rules panel with the clock running behind it (5:00 → 4:57); a practice game with hint chips that match the board
|
||||||
|
and an explanation of each move, hints turned off with the switch; a game between two people with hints turned on
|
||||||
|
on both devices showing no switch and no hints; the rules page and the lesson list at phone width.
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Tutorial: requirements
|
# Tutorial: requirements
|
||||||
|
|
||||||
Status: approved, being implemented on branch `feature/tutorial` (based on `develop`).
|
Status: implemented on branch `feature/tutorial` (based on `develop`).
|
||||||
Scope: `packages/core`, web app; the mobile app gets the rules only (see R5).
|
Scope: `packages/core`, web app; the mobile app gets the rules only (see R5).
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|||||||
@ -12,5 +12,5 @@ Work through the tasks in order; each ends with its tests passing and a commit.
|
|||||||
- [x] 5. **Newcomers and practice game**: `TutorialBanner`, `markGamePlayed`, the practice game button.
|
- [x] 5. **Newcomers and practice game**: `TutorialBanner`, `markGamePlayed`, the practice game button.
|
||||||
- [x] 6. **Hints**: hint chips on `BoardView`, the explanation line and the "Hints" switch in bot games on `GamePage`.
|
- [x] 6. **Hints**: hint chips on `BoardView`, the explanation line and the "Hints" switch in bot games on `GamePage`.
|
||||||
- [x] 7. **Mobile rules**: `RulesScreen`, links from the help and game screens; type check and bundle.
|
- [x] 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
|
- [x] 8. **End to end and docs**: the Chrome tests from `design.md`; `AGENTS.md` (specs list), `TODO.md` (out of scope
|
||||||
items).
|
items).
|
||||||
|
|||||||
@ -94,7 +94,8 @@ export function explainMove(outcome: MoveOutcome, mover: Mover, t: Translate): s
|
|||||||
} else if (outcome.capture === "emptyPit") {
|
} else if (outcome.capture === "emptyPit") {
|
||||||
sentences.push(t(`MoveExplainEmptyPit${who}`, { count: outcome.captured }));
|
sentences.push(t(`MoveExplainEmptyPit${who}`, { count: outcome.captured }));
|
||||||
} else if (outcome.landing === "ownStore") {
|
} else if (outcome.landing === "ownStore") {
|
||||||
sentences.push(t(`MoveExplainExtraTurn${who}`));
|
// A move that ends the game gives no turn to play.
|
||||||
|
if (!outcome.endsGame) sentences.push(t(`MoveExplainExtraTurn${who}`));
|
||||||
} else if (outcome.landing === "opponentPit") {
|
} else if (outcome.landing === "opponentPit") {
|
||||||
sentences.push(t(`MoveExplainOdd${who}`, { count: outcome.stonesBeforeLanding + 1 }));
|
sentences.push(t(`MoveExplainOdd${who}`, { count: outcome.stonesBeforeLanding + 1 }));
|
||||||
} else if (outcome.stonesBeforeLanding === 0) {
|
} else if (outcome.stonesBeforeLanding === 0) {
|
||||||
|
|||||||
@ -145,7 +145,7 @@ describe("explainMove", () => {
|
|||||||
expect(explainMove(outcome([4, 4, 4, 4, 4, 4, 0, 4, 3, 4, 4, 4, 4, 1], 5), "you", translator(en)))
|
expect(explainMove(outcome([4, 4, 4, 4, 4, 4, 0, 4, 3, 4, 4, 4, 4, 1], 5), "you", translator(en)))
|
||||||
.toBe("Your last stone made an opponent's pit even: you took 4 stones.");
|
.toBe("Your last stone made an opponent's pit even: you took 4 stones.");
|
||||||
expect(explainMove(outcome([0, 0, 0, 0, 0, 1, 20, 2, 3, 1, 0, 2, 1, 18], 5), "you", translator(en)))
|
expect(explainMove(outcome([0, 0, 0, 0, 0, 1, 20, 2, 3, 1, 0, 2, 1, 18], 5), "you", translator(en)))
|
||||||
.toBe("Your last stone landed in your store: you play again. One side is empty, so the game is over.");
|
.toBe("One side is empty, so the game is over.");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("explains the bot's moves from the player's side", () => {
|
it("explains the bot's moves from the player's side", () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user