Files
tavolo/server/packages/tavolo-briscola/README.md
T
woggioni 4d940add1c Migrate the backend from pip to uv
Replace requirements.txt with a uv workspace and lockfile, install the five tavolo packages from packages/* in development and non-editable in the image, and build the Docker image with uv. Move dev tools to a PEP 735 dependency group and refresh the locked dependency versions.
2026-10-19 18:19:20 +00:00

101 lines
5.1 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.
# tavolo-briscola
Briscola — the Italian trick-taking game — as a
[`tavolo-platform`](../tavolo-platform/README.md) game implementation.
Three variants share one rules core:
- `briscola` — the classic two-player game;
- `briscola_a_quattro` — four-player fixed-partnership briscola;
- `briscola_chiamata` — five-player briscola with a calling auction and a
secret partner.
## Contents
- `state.py` — `BriscolaState` (pure game data: phases, players, hands,
stock, face-up briscola, tricks, scores, deadlines), `PlayerState`,
`Card`, `TrickCard`, `Trick`, with JSON (de)serialization. No session
envelope, no transport, no I/O.
- `engine.py` — the pure rules engine for two- and four-player briscola:
deck, deals, trick resolution (trump first, then the led suit; strength
ace > three > king > horse > jack > 7 > 6 > 5 > 4 > 2), the draw order
(trick winner first, the face-up briscola last), card points (ace 11,
three 10, king 4, horse 3, jack 2), patta re-deals and the
target-score match end, deal-end acknowledgements and auto-play.
Deterministic and I/O-free apart from logging, so the whole rule set is
unit-testable.
- `chiamata.py` — the pure rules engine for five-player briscola
chiamata: `ChiamataState`/`ChiamataPlayer`/`Auction` (JSON
round-trip), the 8-card deal with no stock, the descending calling
auction with point raises, the winner's call (including calling one's
own card, which means playing alone), the called-card reveal, single-
deal scoring with the 60–60 patta and the auto-actions for timeouts.
- `errors.py` — briscola-specific errors (`CardNotInHand`); every other
failure mode is a shared `tavolo.platform.errors` subclass.
- `plugin.py` — `BriscolaEngine`, `BriscolaQuattroEngine` and
`BriscolaChiamataEngine` (`GameEngine`): the platform-facing adapters.
The first two validate the `target_score` creation option (61–120,
default 61) and extract the `MatchResult` (teams, winner, per-player
scores and the match summary persisted as the match's JSON `result`).
Briscola chiamata takes no creation options.
Timeouts are constructor arguments (`turn_timeout_seconds`,
`hand_ack_timeout_seconds`), wired from the environment by the
application composition root. Briscola chiamata uses the turn timeout
for its auction turns, the called-card choice and the plays: an absent
bidder passes, the last player standing with no bid is made to bid the
ace, and an absent winner's call names a random suit of the won rank. It
never enters the between-deals acknowledgement phase (a single deal ends
the match), so the hand-ack timeout is accepted but unused.
## Rules
### Briscola (two-player and four-player)
- Three cards per player; the next card is turned face up: its suit is
the trump (briscola) for the deal and it is the last card drawn.
- The winner of a trick is the highest trump, or the highest card of the
led suit when no trump was played. The winner takes the trick and leads
the next one.
- After each trick everyone draws back up to three cards, starting with
the trick winner.
- Card points total 120 per deal; a 60–60 deal is a *patta*, its points
are discarded and the deal is replayed. The first team to the target
score wins; a tie at or above the target is broken by another deal.
- Between deals of an unfinished match every player acknowledges the
summary (or the hand-end timeout deals on), exactly like scopone.
### Briscola chiamata (five-player)
- The whole 40-card deck is dealt out, eight cards each: there is no
stock and no face-up card.
- Starting with the player after the dealer, each player either passes
(leaving the auction for good) or bids a **lower** rank than the
standing bid on the ladder *A > 3 > K > H > J > 7 > 6 > 5 > 4 > 2*;
the first bidder may bid any rank. Once the standing bid is the two,
the auction continues on points instead (62, 64, …): the calling side
then has to reach the raised target. The last player standing must
bid, so a deal always gets a caller.
- The auction winner names any card of the winning rank. If the winner
holds it, they **play alone** against the other four; otherwise its
holder is the **secret partner** and the two form the calling side.
The called card's suit is the trump for the deal. The partner's
identity is hidden until the called card is played.
- Play follows briscola: the player after the dealer leads, the highest
trump (or highest led-suit card) takes the trick and leads the next
one; eight tricks exhaust every hand.
- The calling side (caller plus partner, or the lone caller) must reach
the declared target — 61 unless the auction raised it. With the plain
target a 60–60 deal is a patta and is replayed with the next dealer.
A match is a single deal, so the two sides map onto the platform's
two-team result (Elo rating, match history).
## Development (from `server/`)
```sh
uv run python -m unittest discover -s packages/tavolo-briscola/tests
uv run mypy -p tavolo.briscola
```
`test_engine.py`/`test_plugin.py` cover the two classic variants;
`test_chiamata.py`/`test_chiamata_plugin.py` cover briscola chiamata.