Files
NowChessSystems/docs/Tournament-API.md
T
LQ63 3bda9466b5
Build & Test (NowChessSystems) TeamCity build failed
docs(tournament): spec
Added tournament spec to docs
2026-05-13 13:32:52 +02:00

314 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# NowChess Tournament API
Swiss-system bot tournaments. Bots are paired by score each round; all bots play every round (no eliminations). Game moves flow through the existing board and bot endpoints — the tournament module only orchestrates pairings, standings, and lifecycle.
---
## Base path
```
/api/tournament
```
Routing: `/api/tournament``nowchess-tournament-active:8086`
---
## Authentication
All endpoints require a valid JWT (`Authorization: Bearer <token>`).
Bot-facing streaming endpoints additionally require the token's subject to match the registered `botId`.
---
## Data models
### Tournament
```json
{
"id": "t7kXq2",
"name": "Friday Night Bots",
"status": "created | started | finished",
"rounds": 5,
"currentRound": 2,
"timeControl": {
"limitSeconds": 300,
"incrementSeconds": 3
},
"createdBy": "userId",
"createdAt": "2026-05-13T18:00:00Z",
"startedAt": "2026-05-13T18:05:00Z",
"finishedAt": null
}
```
### Standing
```json
{
"rank": 1,
"botId": "bot_abc",
"botName": "StockfishClone",
"points": 3.5,
"wins": 3,
"draws": 1,
"losses": 0,
"buchholz": 9.0
}
```
Tiebreaker: Buchholz score (sum of opponents' points).
### Pairing
```json
{
"round": 2,
"whiteBot": "bot_abc",
"blackBot": "bot_xyz",
"gameId": "j0nPtcjl",
"result": "white | black | draw | ongoing"
}
```
### TournamentEvent (SSE)
```json
{ "type": "tournamentStarted", "tournamentId": "t7kXq2" }
{ "type": "roundStarted", "tournamentId": "t7kXq2", "round": 2 }
{ "type": "pairingReady", "tournamentId": "t7kXq2", "round": 2, "gameId": "j0nPtcjl", "color": "white" }
{ "type": "roundFinished", "tournamentId": "t7kXq2", "round": 2 }
{ "type": "tournamentFinished","tournamentId": "t7kXq2" }
```
---
## Endpoints
### Tournament lifecycle
#### Create tournament
```
POST /api/tournament
```
Body:
```json
{
"name": "Friday Night Bots",
"rounds": 5,
"timeControl": {
"limitSeconds": 300,
"incrementSeconds": 3
}
}
```
Response `201 Created`:
```json
{ "id": "t7kXq2" }
```
The creator becomes the tournament director. Only the director can start and delete the tournament.
---
#### Get tournament
```
GET /api/tournament/{tournamentId}
```
Response `200 OK`: `Tournament` object.
---
#### List tournaments
```
GET /api/tournament
```
Query params:
| Param | Type | Default |
|----------|---------------------------------|-----------|
| `status` | `created\|started\|finished` | (all) |
| `limit` | integer (max 50) | 20 |
| `offset` | integer | 0 |
Response `200 OK`:
```json
{
"tournaments": [ /* Tournament[] */ ],
"total": 42
}
```
---
#### Start tournament
```
POST /api/tournament/{tournamentId}/start
```
Requires at least 2 registered bots. Computes round 1 pairings (random for round 1; score-based from round 2). Creates one game per pairing via `POST /api/board/game`.
Response `200 OK`: updated `Tournament` object.
---
#### Delete tournament
```
DELETE /api/tournament/{tournamentId}
```
Only allowed while `status == "created"`. Response `204 No Content`.
---
### Bot registration
#### Register bot
```
POST /api/tournament/{tournamentId}/bots
```
Registers a bot for the tournament. Must be called before the tournament starts.
The token subject must match the bot being registered.
Body:
```json
{ "botId": "bot_abc" }
```
Response `200 OK`:
```json
{ "botId": "bot_abc", "tournamentId": "t7kXq2" }
```
---
#### Unregister bot
```
DELETE /api/tournament/{tournamentId}/bots/{botId}
```
Only allowed while `status == "created"`. Response `204 No Content`.
---
#### List registered bots
```
GET /api/tournament/{tournamentId}/bots
```
Response `200 OK`:
```json
{
"bots": [
{ "botId": "bot_abc", "botName": "StockfishClone" }
]
}
```
---
### Standings and pairings
#### Get standings
```
GET /api/tournament/{tournamentId}/standings
```
Response `200 OK`:
```json
{ "standings": [ /* Standing[] */ ] }
```
---
#### Get pairings for a round
```
GET /api/tournament/{tournamentId}/rounds/{round}/pairings
```
Response `200 OK`:
```json
{ "pairings": [ /* Pairing[] */ ] }
```
---
### Bot streaming
#### Stream tournament events
```
GET /api/tournament/{tournamentId}/stream
```
Headers: `Accept: text/event-stream`
Server-Sent Events stream scoped to this tournament. The bot receives `pairingReady` events when it is assigned a game, at which point it should connect to the existing bot game stream:
```
GET /bot/stream/game/{gameId} (existing endpoint)
POST /bot/game/{gameId}/move/{uci} (existing endpoint)
```
The tournament module never sends moves — bots do that themselves through the existing bot endpoints.
---
## Typical bot flow
```
1. POST /api/tournament # director creates tournament
2. POST /api/tournament/{id}/bots # each bot registers
3. POST /api/tournament/{id}/start # director starts
4. GET /api/tournament/{id}/stream (SSE) # each bot opens stream
-- per round --
5. receive: pairingReady { gameId, color }
6. GET /bot/stream/game/{gameId} # existing endpoint
7. POST /bot/game/{gameId}/move/{uci} # existing endpoint, repeated
-- game ends --
8. receive: roundFinished
9. GET /api/tournament/{id}/standings # optional, inspect scores
-- repeat 59 for each round --
10. receive: tournamentFinished
11. GET /api/tournament/{id}/standings # final ranking
```
---
## Error responses
| Status | Meaning |
|--------|------------------------------------------------------|
| 400 | Invalid request body or parameters |
| 401 | Missing or invalid JWT |
| 403 | Action not allowed (wrong director, wrong bot, etc.) |
| 404 | Tournament or bot not found |
| 409 | Tournament already started / bot already registered |