games: you buy the deck, and win it back a card at a time
Solitaire, Vegas rules — the only shape solitaire has ever had as a gambling game. You don't win or lose the deal: the stake buys the deck outright, and every card you get home to a foundation pays a fifty-second of the tier's multiple back. Cash the board whenever you like and keep what you've banked, so a board that has gone dead is a decision rather than a wall. No undo: the stake is spent the moment the deck is bought, and an undo would be a way to walk a losing board backwards until it wins. Three deals, and the two dials are the whole difficulty of Klondike. Patient draws one with unlimited passes and pays 1.4x, so it takes 38 cards home to get square. Vegas draws three, three times round, 2.2x, square at 24. Cutthroat draws three and gives you one pass, 3.4x, square at 16 — most of those boards never clear, and you're ahead long before they would. internal/games/klondike is the same pure reducer as the other two, and Pays() is one function for the same reason hangman's is. Two fuzzers hold the deck together: no sequence of moves can lose or duplicate a card, and the board stays well-formed. They earned their keep immediately — the first thing they caught was a recycle that reversed the waste. It flips as a block, so the card drawn first comes out first, and reversing it would have dealt a different game on every pass and quietly broken the seed in the audit log. The browser never sees the stock or a face-down card, which here is most of the deck rather than blackjack's one hole card: a column sends how many cards are under it, never which. The table re-renders and animates the difference. Blackjack plays back a script because a hand only ever grows at one end; solitaire moves runs from anywhere to anywhere and an auto-finish moves eleven cards at once, so a script of "append this card there" would be a second engine over here and it would be the one that's wrong. Instead the board on screen is always exactly the board the server says exists, and each card is played from where it just was to where it now is. The events supply only what a diff can't: where a newly-revealed card came from, and what the board is worth. The rules are mirrored in JS on purpose, and only to light up the columns a held card can go to. Being shown where a card goes is the game teaching you; being told no after you commit is the game scolding you. The server still decides, and a disagreement snaps the board back to what it says. Two things came out into the open rather than being copied, which is the rule this room runs on: casino-cards.js (the deck — faces, pips, the flip) and PeteFX.spot() (the pile of chips and the number under it, which now owns the rule that the number is a readout of the pile). Blackjack uses both. Not yet driven in a browser.
This commit is contained in:
@@ -184,9 +184,60 @@ A multi-session build. This section is the handover; read it before anything els
|
||||
the rack's clearance padding was on the whole column instead of the one row
|
||||
level with it.
|
||||
|
||||
- **Solitaire, and it plays for chips.** *(2026-07-14, jumping the queue ahead of
|
||||
trivia because the user asked for it.)*
|
||||
- **Vegas scoring**, which is the only way solitaire has ever actually been a
|
||||
gambling game. You do not win or lose the deal — you **buy the deck** for your
|
||||
stake, and every card you get home to a foundation pays a fifty-second of the
|
||||
tier's multiple back. Cash the board whenever you like and keep what you've
|
||||
banked; a board that has gone dead is therefore a decision, not a wall. There
|
||||
is no undo, because the stake is spent the moment the deck is bought and an
|
||||
undo would be a way to walk a losing board backwards until it wins.
|
||||
- Three deals, and the two dials are the whole difficulty of Klondike: **Patient**
|
||||
(draw 1, unlimited passes, 1.4×, square at 38 cards), **Vegas** (draw 3, three
|
||||
passes, 2.2×, square at 24), **Cutthroat** (draw 3, one pass, 3.4×, square at
|
||||
16). `Tier.BreakEven()` is what the felt quotes, because "2.2×" tells a player
|
||||
nothing about a game where the multiple is paid a card at a time.
|
||||
- `internal/games/klondike` — the same pure reducer. `Pays()` is one function for
|
||||
the same reason hangman's is. Two fuzzers hold the deck together: no card is
|
||||
ever lost or duplicated by any sequence of moves, and the board stays
|
||||
well-formed (every face-up run is a run, no column has cards face-down under
|
||||
nothing). The first thing a test caught was a **recycle that reversed the
|
||||
waste** — it flips as a block, so the card drawn first comes out first, and
|
||||
reversing would have dealt a different game on every pass and broken the seed.
|
||||
- **The browser never sees the stock or a face-down card.** Bigger than
|
||||
blackjack's hole card: that's most of the deck. Columns send a face-down
|
||||
*count*, never the cards. The events, unlike blackjack's, need no filtering —
|
||||
every card they carry is one the move just turned face up.
|
||||
- **The table re-renders and animates the difference (FLIP).** Blackjack plays
|
||||
back a script because a hand only grows at one end; solitaire moves runs from
|
||||
anywhere to anywhere and an auto-finish moves eleven cards at once. So
|
||||
`solitaire.js` measures where every card is, re-renders the board the server
|
||||
sent, and plays each card from its old place to its new one. The board on
|
||||
screen is therefore always exactly the board the server says exists. The events
|
||||
supply only what a diff can't: where a *newly revealed* card came from (the
|
||||
stock, or a flip in place) and what the board is now worth.
|
||||
- **The rules are mirrored in JS**, deliberately, and only to light up the columns
|
||||
a held card can go to. The server still decides every move; a disagreement
|
||||
snaps the board back to whatever it says. Being shown where a card goes is the
|
||||
game teaching you; being told no after you commit is the game scolding you.
|
||||
- Two things got extracted rather than copied, which is the rule this room runs
|
||||
on: **`casino-cards.js`** (the deck — faces, pips, the flip; was inside
|
||||
blackjack.js) and **`PeteFX.spot()`** (the pile of chips and the number under
|
||||
it, which owns the "the number is a readout of the pile" rule so no table can
|
||||
break it). Blackjack now uses both.
|
||||
- **NOT YET DRIVEN IN A BROWSER.** Everything compiles and every Go test passes,
|
||||
but per this plan's own hard-won rule, *that means nothing about the table*.
|
||||
Next session: `PETE_DEV_CASINO=:7788 go test ./internal/web -run TestDevCasino
|
||||
-timeout 0` and play it. Watch especially: the seven columns fitting on a phone
|
||||
(`--card-w` is a clamp on vw), the FLIP not jumping on a re-render, the rail not
|
||||
colliding with anything, and **blackjack still settling correctly** — its money
|
||||
was rewired onto the shared spot and it is the thing most likely to have broken.
|
||||
|
||||
### Next, in order
|
||||
|
||||
1. Phase 2's other half: **trivia**. Decided but not built: the question bank is
|
||||
1. **Drive solitaire in a browser** (see above) — it has never been played.
|
||||
2. Phase 2's other half: **trivia**. Decided but not built: the question bank is
|
||||
**prefetched from OpenTDB into a local table** (a per-question fetch in a web
|
||||
game loop is a latency and rate-limit problem gogobee never had), through
|
||||
`internal/safehttp`. It stakes chips too. The shape that fits the room is a
|
||||
|
||||
Reference in New Issue
Block a user