Release: Quarkus backend, Keycloak login, game history, clock, bot, tutorial and rematch #3
13
AGENTS.md
13
AGENTS.md
@ -23,7 +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) and `quarkus-backend` (the backend's move from Node to Java and Quarkus).
|
pages), `quarkus-backend` (the backend's move from Node to Java and Quarkus) and `game-bot` (playing against a bot
|
||||||
|
at four levels).
|
||||||
|
|
||||||
## Running locally
|
## Running locally
|
||||||
|
|
||||||
@ -44,13 +45,15 @@ unless `useLocalServer` is set in `apps/mobile/src/const/config.ts`.
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Engine | `cd packages/mancala.js && npx jest` | After changing the rules, change the Java port too and record the parity games again (`apps/backend/README.md`) |
|
| Engine | `cd packages/mancala.js && npx jest` | After changing the rules, change the Java port too and record the parity games again (`apps/backend/README.md`) |
|
||||||
| Core | `cd packages/core && npx jest` | |
|
| Core | `cd packages/core && npx jest` | |
|
||||||
| Backend | `cd apps/backend && ./mvnw test` | Needs a JDK 25 and Docker: Postgres and Valkey run with Quarkus Dev Services |
|
| Backend | `cd apps/backend && ./mvnw test` | Needs a JDK 25 and Docker: Postgres and Valkey run with Quarkus Dev Services. The bot's calibration tournament is tagged `calibration` and skipped; see `apps/backend/README.md` |
|
||||||
| Keycloak extension | `cd apps/keycloak/extension && mvn test` | Needs Docker; starts Keycloak with the real realm file |
|
| Keycloak extension | `cd apps/keycloak/extension && mvn test` | Needs Docker; starts Keycloak with the real realm file |
|
||||||
| Web app | `cd apps/frontend && yarn build` | There is no `tsconfig.json`, so nothing type-checks it; use a temporary one when needed (with `"types": ["styled-jsx", "react", "react-dom"]`). 1 type error (`react-dom/client`) is known |
|
| Web app | `cd apps/frontend && yarn build` | There is no `tsconfig.json`, so nothing type-checks it; use a temporary one when needed (with `"types": ["styled-jsx", "react", "react-dom"]`). 1 type error (`react-dom/client`) is known |
|
||||||
| Mobile | `cd apps/mobile && npx tsc --noEmit -p .` and `npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output /tmp/x.bundle` | 11 type errors are known and older than recent work |
|
| Mobile | `cd apps/mobile && npx tsc --noEmit -p .` and `npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output /tmp/x.bundle` | 11 type errors are known and older than recent work |
|
||||||
|
|
||||||
There is no automated end-to-end suite. Changes to the game flow were checked by driving Chrome with
|
There is no automated end-to-end suite. Changes to the game flow were checked by driving Chrome with
|
||||||
`playwright-core` (installed outside the repo, `executablePath: /usr/bin/google-chrome`) against the local stack.
|
`playwright-core` (installed outside the repo, e.g. `npm install playwright-core@1.48.2` in a scratch folder;
|
||||||
|
`executablePath: /usr/bin/google-chrome`) against the local stack. A live game is `GET /game/:id` on the backend,
|
||||||
|
which helps to check what the server holds.
|
||||||
|
|
||||||
## How we work
|
## How we work
|
||||||
|
|
||||||
@ -92,6 +95,10 @@ There is no automated end-to-end suite. Changes to the game flow were checked by
|
|||||||
left with `GameClocks.getClockTimes`, never store a ticking value. `GAME_CLOCK_MS=40000` makes games short for
|
left with `GameClocks.getClockTimes`, never store a ticking value. `GAME_CLOCK_MS=40000` makes games short for
|
||||||
manual tests.
|
manual tests.
|
||||||
- `Rtmt.listenOnClientConnectionChange` adds a listener (several are allowed).
|
- `Rtmt.listenOnClientConnectionChange` adds a listener (several are allowed).
|
||||||
|
- The bot plays as `bot:<level>` (`BotLevel.isBotId`) in many games at once: `GameStore` does not map it to a game,
|
||||||
|
nothing is sent to it, it is always online and `PublicIds` keeps its id. The bot's search must never run on the
|
||||||
|
game loop; `BotPlayer` runs it on its own pool and plays the move back on the loop.
|
||||||
|
- `-Dgroups=...` runs only the tests with that tag; run the untagged tests separately too.
|
||||||
- Unit tests build the logic with fakes (`FakeRtmt`, in-memory stores, `ManualScheduler` instead of timers and
|
- Unit tests build the logic with fakes (`FakeRtmt`, in-memory stores, `ManualScheduler` instead of timers and
|
||||||
direct executors); only storage, HTTP and WebSocket tests are `@QuarkusTest`s. `FakeTokenVerifier` replaces
|
direct executors); only storage, HTTP and WebSocket tests are `@QuarkusTest`s. `FakeTokenVerifier` replaces
|
||||||
Keycloak in all `@QuarkusTest`s.
|
Keycloak in all `@QuarkusTest`s.
|
||||||
|
|||||||
11
TODO.md
11
TODO.md
@ -54,6 +54,17 @@ See `docs/specs/game-clock`.
|
|||||||
- [ ] Clocks in the home page's live game previews and in the ongoing-game banner
|
- [ ] Clocks in the home page's live game previews and in the ongoing-game banner
|
||||||
- [ ] Try the mobile clocks on a device; only the type check and the bundle were run
|
- [ ] Try the mobile clocks on a device; only the type check and the bundle were run
|
||||||
|
|
||||||
|
## Game bot
|
||||||
|
|
||||||
|
See `docs/specs/game-bot`.
|
||||||
|
|
||||||
|
- [ ] Play against the bot without a connection (a bot in the TypeScript engine on the device)
|
||||||
|
- [ ] Keep bot games apart once there are statistics or ratings
|
||||||
|
- [ ] Hints or move analysis for players, with the same search
|
||||||
|
- [ ] Rebuild `MancalaGame` on `Position`, or use `Position` to check moves before running them (see Security)
|
||||||
|
- [ ] 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
|
||||||
|
|
||||||
## Web redesign
|
## Web redesign
|
||||||
|
|
||||||
See `docs/specs/web-redesign`.
|
See `docs/specs/web-redesign`.
|
||||||
|
|||||||
@ -21,7 +21,8 @@ The Docker image is built from the repo root: `docker build -f apps/backend/Dock
|
|||||||
|
|
||||||
| Package | What |
|
| Package | What |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `engine` | The game rules, a port of `packages/mancala.js` |
|
| `engine` | The game rules, a port of `packages/mancala.js`; `Position`, a compact board for searching moves |
|
||||||
|
| `bot` | The bot: levels, strength, the search and `BotPlayer`, which plays its turns in live games |
|
||||||
| `model` | The JSON sent to clients and stored, and the shared `ObjectMapper` |
|
| `model` | The JSON sent to clients and stored, and the shared `ObjectMapper` |
|
||||||
| `loop` | `GameLoop`, the single thread that owns all live game state |
|
| `loop` | `GameLoop`, the single thread that owns all live game state |
|
||||||
| `auth` | Keycloak token verification |
|
| `auth` | Keycloak token verification |
|
||||||
@ -93,6 +94,24 @@ backend is down is not charged. The design is in `docs/specs/game-clock`.
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `GAME_CLOCK_MS` | `300000` | Time each player has for a game; a short value such as `40000` helps when testing by hand |
|
| `GAME_CLOCK_MS` | `300000` | Time each player has for a game; a short value such as `40000` helps when testing by hand |
|
||||||
|
|
||||||
|
## Bot
|
||||||
|
|
||||||
|
Players can play against a bot at four levels (`new_bot_game` with `{ level }`); the lobby offers it after 10 seconds
|
||||||
|
without an opponent. Each level is a range of strengths, and the bot draws one per game, so games at the same level
|
||||||
|
differ. It plays as `bot:<level>` through the same game logic as players, with the clock, saving and spectators, and
|
||||||
|
waits for the previous move's animation and a thinking time before moving. Its moves are worked out with alpha-beta
|
||||||
|
search on `Position` on a thread pool of their own, never on the game loop. The design is in `docs/specs/game-bot`.
|
||||||
|
|
||||||
|
| Variable | Default | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `BOT_SEARCH_THREADS` | half the processors | Threads working out the bot's moves |
|
||||||
|
|
||||||
|
After changing the search or the strength table, check that every level still beats the one below:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./mvnw test -Dtest=BotTournamentTest -Dgroups=calibration -DexcludedGroups=none
|
||||||
|
```
|
||||||
|
|
||||||
| Route | Auth | Response |
|
| Route | Auth | Response |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `GET /game/:id` | – | A live game, or `{ status: "finished", record }` |
|
| `GET /game/:id` | – | A live game, or `{ status: "finished", record }` |
|
||||||
|
|||||||
@ -231,3 +231,9 @@ Games being worked on are kept in a set by identity, so a game restored with the
|
|||||||
"Play with bot" on the home page; the bot moves after the animation; the game ends and shows in recent games and
|
"Play with bot" on the home page; the bot moves after the animation; the game ends and shows in recent games and
|
||||||
the replay as "Bot · <level>"; "Play again" starts a new bot game; a backend restart during the bot's turn; a
|
the replay as "Bot · <level>"; "Play again" starts a new bot game; a backend restart during the bot's turn; a
|
||||||
second browser watches a bot game from the live previews.
|
second browser watches a bot game from the live previews.
|
||||||
|
|
||||||
|
Checked end to end in Chrome on 2026-10-16 with the default clock: the home menu and the lobby offer (shown after
|
||||||
|
10.3 s) start games at the chosen level; "Bot · Easy" / "Bot · Hard" on the game page, in recent games and in a
|
||||||
|
second browser's live previews; an Easy game played to the end (the bot waited 1.45–4 s per move, longer after long
|
||||||
|
animations); "Play again" starts a new game at the same level; the backend stopped right after a Master game started
|
||||||
|
and the bot made its first move after the restart.
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Game bot: requirements
|
# Game bot: requirements
|
||||||
|
|
||||||
Status: spec, not implemented yet. Branch `feature/game-bot` (based on `develop`).
|
Status: implemented on branch `feature/game-bot` (based on `develop`).
|
||||||
Scope: backend, `packages/core`, web app and mobile app.
|
Scope: backend, `packages/core`, web app and mobile app.
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|||||||
@ -17,5 +17,5 @@ Work through the tasks in order; each ends with its tests passing and a commit.
|
|||||||
- [x] 7. **Web app**: `BotLevelPicker`, the home page button, the lobby offer and `?bot=`, "Play again" after a bot
|
- [x] 7. **Web app**: `BotLevelPicker`, the home page button, the lobby offer and `?bot=`, "Play again" after a bot
|
||||||
game, bot names everywhere players are shown.
|
game, bot names everywhere players are shown.
|
||||||
- [x] 8. **Mobile app**: lobby offer and `botLevel`, home screen button, bot names; type check and bundle.
|
- [x] 8. **Mobile app**: lobby offer and `botLevel`, home screen button, bot names; type check and bundle.
|
||||||
- [ ] 9. **End to end and docs**: the Chrome tests from `design.md`; `README.md`, `apps/backend/README.md`,
|
- [x] 9. **End to end and docs**: the Chrome tests from `design.md`; `README.md`, `apps/backend/README.md`,
|
||||||
`AGENTS.md` (the specs list, bot ids, the calibration test) and `TODO.md` (the out of scope items).
|
`AGENTS.md` (the specs list, bot ids, the calibration test) and `TODO.md` (the out of scope items).
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user