Files
tavolo/README.md
T
woggioni d9cdba33a1 Add hand-end scoring summary screen with acknowledgement
After each hand of an unfinished match the game now pauses in a new
hand_end phase instead of dealing immediately:

- engine: hand_points gains an 'award' map (which team won each category),
  _end_hand stops at hand_end with a deadline, new acknowledge_hand deals
  the next hand once all four players have acked; plays are rejected while
  the summary is up
- state: acked seats, hand_end_deadline and hand_ack_timeout are persisted
  and exposed in the personalized view (also on the finished state, so the
  final hand is explained before the result)
- ws: new {"action": "ack"}; a per-hand timer force-deals the next hand
  after HAND_ACK_TIMEOUT_SECONDS (new env var, default 30s) so an away
  player cannot stall the match
- web: modal explaining each category in plain language with icons (card
  images for denara/settebello/primiera), team-coloured rows, running
  totals with progress bars, an 'Understood — next hand' button that turns
  into 'Waiting for …' plus an auto-continue countdown; the final screen
  shows the last hand's breakdown too

Verified in the browser against the compose stack: hand played to
completion, summary rendered (including a carte tie), ack from all four
players dealt the next hand live, and the auto-continue path fired when
nobody acked. 60 backend tests + mypy + cargo tests green.
2026-09-16 21:46:01 +08:00

72 lines
2.3 KiB
Markdown

# scopa
Multiplayer **scopone scientifico** — the four-player, fixed-partnership
Italian card game — as a web application:
- **`server/`** — backend: Python + [kaya](https://github.com/woggioni/kaya)
framework, OIDC login, live games in Redis, match statistics in Postgres.
See [server/README.md](server/README.md).
- **`web/`** — frontend: Rust + [Sycamore](https://sycamore.dev) compiled to
WebAssembly, built with [Trunk](https://trunkrs.dev). Card images are the
CC0 *woodcut napoletane* deck traced from a 1902 Naples print
([SONDLecT/woodcut-napoletane](https://github.com/SONDLecT/woodcut-napoletane)).
## Run the whole stack
```sh
docker compose up --build
```
Postgres, Redis, a mock OIDC provider (test users `alice`, `bob`, `carol`,
`dave`), the database migrator and the app all come up together. The app —
frontend and API — listens on `http://127.0.0.1:8080`.
Because both the browser and the app talk to the OIDC issuer at
`http://mockoauth:8180/scopa`, add a host entry once:
```sh
echo "127.0.0.1 mockoauth" | sudo tee -a /etc/hosts
```
## Between hands
When a hand ends but the match is not decided, the game pauses on a
**scoring summary screen**: every player sees how each category was won
(carte, denara, settebello, primiera, scope) with the running totals and
must click "Understood" before the next hand is dealt. If someone is away
the next hand is dealt automatically after `HAND_ACK_TIMEOUT_SECONDS`
(default 30s). The match-ending hand is explained on the final screen.
## Development
Backend (from `server/`):
```sh
cd server
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pip install -e '.[dev]'
.venv/bin/python -m unittest discover -s tests -t .
.venv/bin/mypy src
```
Frontend (from `web/`), with the backend running on :8080:
```sh
cd web
trunk serve # SPA on http://localhost:8000, /api /auth /ws proxied
```
Trunk proxies `/api`, `/auth` and `/ws` to `127.0.0.1:8080` (see
`web/Trunk.toml`). For the login redirect to land back on the dev server,
run the backend with:
```sh
OIDC_POST_LOGIN_REDIRECT=http://localhost:8000/ \
OIDC_POST_LOGOUT_REDIRECT=http://localhost:8000/ \
.venv/bin/granian --host 127.0.0.1 --port 8080 scopa.app:app
```
Card images are committed under `web/assets/cards/`; `web/fetch-cards.sh`
re-downloads them if needed.