Metadata-Version: 2.5
Name: magmacrunch-cribbage
Version: 0.1.0
Summary: Cribbage against the computer. Terminal version.
Project-URL: Homepage, https://magmacrunch.com/arcade/cribbage/
Author: magmacrunch media
License-Expression: PolyForm-Noncommercial-1.0.0
License-File: LICENSE
License-File: NOTICE
Keywords: arcade,cards,cribbage,games,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'
Requires-Dist: websockets>=14.0; extra == 'dev'
Provides-Extra: online
Requires-Dist: websockets>=14.0; extra == 'online'
Description-Content-Type: text/markdown

# Cribbage (terminal)

Cribbage against the computer, in a terminal. The second version of the game
beside `web/`, on the [magmacrunch](https://github.com/magmacrunch-media/magmacrunch)
engine's terminal backend: a cabinet in the magmacrunch arcade and its own
command, the same either way.

```
pipx install magmacrunch-cribbage
cribbage
```

For working on it, from this folder:

```
node ../tools/link-shared.mjs     # once: the oracle needs ../shared
pip install -e ".[dev]"
python -m cribbage
```

## Keys

| key | |
|---|---|
| `←` `→` | choose a card |
| `SPACE` or `1`-`6` | mark it for the crib, or lay it when pegging |
| `ENTER` | send the two to the crib, cut, lay the chosen card, or carry on after the show |
| `R` | new game |
| `Esc` | the title (from the title, back to the arcade) |
| `Q` | quit |

A card that would take the count past 31 is greyed and refused. The
computer's moves arrive one at a time, under a second apart, so a pegging
exchange can be followed. The show waits for Enter, because a count is the
thing a cribbage player wants to check.

## Playing a person online (a prototype)

PLAY ONLINE on the title, or `cribbage --online`, connects to the arcade's
own multiplayer server -- the one the browser's Multiplayer button uses -- so
a terminal and a browser can share a room. Type a name, then `C` to create a
room and read its four-letter code to your opponent, or `J` to join one by
its code. The host starts the game once two are in.

```
pip install "magmacrunch-cribbage[online]"     # websockets, only for this
cribbage --online
cribbage --online --server ws://127.0.0.1:8766  # a web/server.py on this machine
```

The server keeps the rules. This client sends what you chose and draws what
the server says, so it never disagrees with the browser about a score: the
two are clients of one referee. `cribbage/online.py` has the protocol, split
into `Table` (the server's state, no I/O, tested without a socket) and
`Connection` (the WebSocket, on a thread of its own, polled once a frame).

`tests/test_online.py` runs `web/server.py` in-process on a free port and
plays it over real sockets: two clients through a whole game to 121, and two
full terminal sessions through the lobby and a hand with keys alone. It
never contacts the live server.

What a prototype has not done yet:

- **Nobody has played the live server from a terminal**, only a copy of it
  run locally. The default address is `wss://magmacrunch.duckdns.org/cribbage`,
  which is what `mpServerFor('cribbage', 8766)` gives the public site.
- **No reconnect.** A dropped connection says so and the game is lost.
- **No chat and no colour pick**, both of which the browser lobby has.
- **Online games do not reach the high score table**, here or on the page.
- **The server sends both hands to both players.** `get_state` puts every
  hand in `playerHands`; the browser and this client both decline to draw the
  opponent's, but anyone reading the socket can see it. That wants fixing in
  `server.py`, for the browser's sake as much as this one's.

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

Everything that decides is ported from what the page runs:

| here | there |
|---|---|
| `cribbage/cards.py` | the deck and `CribbageHandEval` from `adenosine-cards`, in the website's `arcade/shared/` |
| `cribbage/ai.py` | `web/js/ai.js`, the opponent |
| `cribbage/game.py` | `web/js/game.js`, and the turn-taking `web/js/main.js` wraps around it |

`tests/test_oracle.py` holds the port to the page. `tools/js_oracle.mjs`
loads `adenosine-cards.js`, `config.js`, `ai.js` and `game.js` unmodified,
plays 120 games against the page's own AI with a scripted player, and the test
replays every choice through the port. It requires the same deck, cut, hands,
crib, count, turn, scores and kept cards after every move: about 15,000 moves,
both sides winning, some games won on his heels and some on a go. `tests/test_cards.py` carries the 50 scoring cases from
`web/tests/test-scoring.js`, converted line for line.

Wins go to the `cribbage` board the page posts to, as the winner's final score
with `finalScore` beside it. Losses are not recorded, as on the page.

## Who leads when a hand runs out

After a series, whoever did not lay the last card leads the next, unless
their hand is empty: then the other side lays out what it has left, alone,
and takes the go for the last card. The page used to hand the lead to the
empty hand, then go straight to the show, so the other hand's cards were
never pegged. The oracle's first 60 games hit that in 32 of them, dropping 42 cards.
It was fixed in `web/js/game.js` and here in the same change, and the oracle
now requires that no game drops a card. The multiplayer server already got
this right.

## Three more, fixed in the page and followed here

cribbage#5 fixed three more bugs in `web/js/game.js`, and the port copies all
three, so the oracle still holds:

- **The show counts the kept cards.** Pegging empties the hands, and the show
  used to score those empty hands, so only the crib counted. A game took about
  21 hands; it now takes about ten.
- **His heels can win.** A jack turned that takes the dealer to 121 ends the
  game at the cut. The page used to play on past 121.
- **So can the go.** The point for a go or the last card can end the game
  too. The page used to reset the series and peg on.

The oracle requires a game a little under 13 hands on average, and its 120
games include wins on his heels and on a go.

Needs a 60x22 terminal.

## Licence

[PolyForm Noncommercial 1.0.0](LICENSE), as the rest of this repository. See
[NOTICE](NOTICE).
