The auction timeout bid a random legal rank, which could hand the auction (and the call) to a disconnected player. An absent bidder now always passes; only the last player standing before any bid cannot pass, and is made to bid the ace - its suit is picked at random by the existing call timeout. This keeps a deal from stalling without a caller while never letting an absent player outbid someone. The deadline log line now says which of the two happened.
tavolo-briscola
Briscola — the Italian trick-taking game — as a
tavolo-platform 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 sharedtavolo.platform.errorssubclass.plugin.py—BriscolaEngine,BriscolaQuattroEngineandBriscolaChiamataEngine(GameEngine): the platform-facing adapters. The first two validate thetarget_scorecreation option (61–120, default 61) and extract theMatchResult(teams, winner, per-player scores and the match summary persisted as the match's JSONresult). 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/)
.venv/bin/python -m unittest discover -s packages/tavolo-briscola/tests
.venv/bin/python -m 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.