Metadata-Version: 2.5
Name: magmacrunch-tarot
Version: 0.1.0
Summary: French tarot against three computers. Terminal version.
Project-URL: Homepage, https://magmacrunch.com/arcade/tarot/
Author: magmacrunch media
License-Expression: PolyForm-Noncommercial-1.0.0
License-File: LICENSE
License-File: NOTICE
Keywords: arcade,cards,games,tarot,terminal,tui
Requires-Python: >=3.10
Requires-Dist: magmacrunch>=0.7.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# French Tarot (terminal)

Four-player French Tarot against three computers, in a terminal. It is the
second version of the game, beside `web/`. It runs on the
[magmacrunch](https://github.com/magmacrunch-media/magmacrunch) engine's
terminal backend, either as a cabinet in the magmacrunch arcade or as its own
command, and plays the same either way.

```
pipx install magmacrunch-tarot
tarot              # the title
tarot --play       # straight into a game
tarot --seed 7     # the same deals every time
```

For working on it, from this folder:

```
pip install -e ".[dev]"
python -m tarot
```

## Keys

| key | |
|---|---|
| `←` `→` | along the bids, or along your hand |
| `ENTER` | place the bid, play the card, or mark a card for the dog |
| `C` | with six cards marked, put them in the dog |
| `Esc` | the title (and from there, the arcade) |
| `Q` | quit |

You sit south, with West, North and East round the table. Cards are written
`R♥`, `10♠`, `21` for a trump and `Ex` for the Excuse, and your hand is sorted
as on the page: trumps, the Excuse, then the suits. Only the cards you may
play are lit. The computers bid after half a second and play after 0.6, a
finished trick stays on the table for a second, and a hand nobody takes is
redealt after 1.5, all as on the page. At the end of each hand the result is
shown: the taker's oudlers and points against the threshold, and everyone's
running total. When you were the taker and your total makes the table, you
are asked for initials, which is when the page saves a score too.

Needs a 76x24 terminal.

## It is the browser's game, and the browser's opponents

`tarot/rules.py` is `web/js/config.js`, `tarot-deck.js`, `trick-logic.js`,
`scoring.js`, `ai.js` and `game.js`, ported function for function, including
the order the computers look through their hands in, because that is how they
break ties. Two tests hold the port to the page:

- **`tests/test_oracle.py`.** `tools/js_oracle.mjs` loads those files
  unmodified, with a seeded `Math.random`, and plays 30 games of five hands
  through `TarotGame`'s own API in `main.js`'s order: 150 hands and about
  12,000 moves. In odd games your seat plays at random; in even games it bids
  what `scoring.js` suggests and plays the page's own computer moves. Between
  them they cover 175 redeals, 83 hands you took, 23 Garde Sans, 14 Garde
  Contre and 148 Excuses. The test replays every move through the port and
  requires the same state after each: phase, dealer, bidder, player, taker,
  bids, every hand, the dog, the trick, every trick pile, the scores and the
  poignée. Each hand's result must match too, line by line.
- **`tests/test_rules.py`.** It sets up by hand the cases worth pinning,
  including the page's own rules below, so that fixing one shows as a change.

## The page's rules

These are the page's own, kept because the oracle requires them. Each is a
place where the page is not French Tarot:

- **After a redeal, the first bid ends the auction.** `deal()` does not clear
  the count of bids placed, so it is still 4. Whoever bids first after a hand
  nobody took is the only one who bids, Passe included: a Passe there is a
  second redeal.
- **Petit au bout is never paid.** The round's score looks for the last trick
  in the history under a type that is never written, so the bonus is always
  off. In 400 played hands the Petit fell in the last trick 44 times and was
  paid none of them.
- **The Excuse is lost to the trick.** It goes into the winner's pile, and its
  player is still owed a low card from the winner, even when the two are on
  the same side. In French Tarot the Excuse goes back to its player's side,
  who give a half-point card for it.
- **Garde Contre puts the dog in every defender's pile.** It is the same six
  cards three times. The score counts only the taker's pile, so it changes
  nothing that shows.
- **Chelem is not played.** `config.js` defines the bonuses, and nothing uses
  them.

**The page used to stop when your bid closed the auction.** `main.js`
ignored what your bid returned and asked the computers to bid again, which
they couldn't, for ever. It happened whenever you bid last: every fourth hand,
and after a redeal or a Garde Contre too. Now `main.js` hands your bid's
result to `handleBidResult`, as it does a computer's, and the game goes on to
the dog or the play. Here the game always went on; `test_app.py` bids last
and bids Garde Contre by keys and checks it does. On the page, headless
Chromium bid Garde Contre in three deals: before the fix the computers tried
six more bids, all refused, and the table sat at "You bid: Garde Contre";
after it, "Your turn — play a card".

**The computers take on hands they can't make.** In the oracle's 150 hands,
a computer taker made 6 contracts of 67, with 28 points on average against
about 49 needed. They bid on weak hands, and when leading they take their
"highest" card from the hand in dealt order rather than sorted.

## Licence

PolyForm Noncommercial 1.0.0, as for the rest of this repository. See `LICENSE`
and `NOTICE`.
