docs: describe the game bot

The bot in the backend README with the calibration command, AGENTS.md notes on
bot ids and test tags, and the open work in TODO.md.
This commit is contained in:
Halit Aksoy 2026-10-16 14:27:49 +03:00
parent d1918fed10
commit f7a76c861e
6 changed files with 49 additions and 6 deletions

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),
`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
@ -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`) |
| 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 |
| 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 |
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
@ -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
manual tests.
- `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
direct executors); only storage, HTTP and WebSocket tests are `@QuarkusTest`s. `FakeTokenVerifier` replaces
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
- [ ] 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
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 |
|---|---|
| `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` |
| `loop` | `GameLoop`, the single thread that owns all live game state |
| `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 |
## 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 |
|---|---|---|
| `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
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.
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
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.
## 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
game, bot names everywhere players are shown.
- [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).