Release: Quarkus backend, Keycloak login, game history, clock, bot, tutorial and rematch #3

Merged
jhalitaksoy merged 88 commits from develop into main 2026-10-17 03:23:38 +03:00
6 changed files with 49 additions and 6 deletions
Showing only changes of commit f7a76c861e - Show all commits

View File

@ -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
View File

@ -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`.

View File

@ -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 }` |

View File

@ -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.

View File

@ -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

View File

@ -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).