dbde827f756ffeddebb6aac998ad5973723ef9aa
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
39ed293f4f |
games: the word you owe the table, and the hand you were already holding
Three things, and the first one was a bug. Your own hand didn't move until the lap ended. bump() keeps the bots' fans honest and has always refused seat zero, and nothing else touched yours — so a +4 landing on you at the top of a lap put four backs into your hand and then nothing, and the cards themselves turned up seconds later when the script finished and paint() finally ran. You spent the whole lap looking at a hand you no longer held. The engine now stamps your hand onto every event that changes it (Event.Hand, seat zero only, which is the one hand the browser is already entitled to see) and the table redraws as the cards land. Measured in the running app: 2 -> 3 cards at 414ms into a 1791ms lap. You couldn't call UNO, and not because the button was missing: going down to one card *was* the call. discard() fired the uno event by itself, which made it a thing that happened to you rather than a thing you did, and a rule nobody can fail is not a rule. So now you say it or you don't (Move.Uno), and if you don't, every bot still in the game gets one look at you before any of them plays — because a bot that has moved on is a bot that has stopped watching your hand. It runs the other way too, and that half is the fun one: a bot forgets often enough to be worth watching for, and when it does it says *nothing*. No event, no badge, no tell on the felt except the count beside its fan reading "1 card". Catch it and it takes two; call a seat that had nothing to hide and you take two yourself, which is what stops the catch button from being a thing you simply mash. Which cards owe the call is the engine's answer, not a count of your hand: No Mercy's "discard all" takes every card of its colour with it, so a six-card hand can land on one, and a browser subtracting one from six walks you into a catch it never warned you about. And the room was silent. Every sound in here is *made* — an oscillator, a burst of filtered noise, an envelope — the same bargain the weather engine takes with its clouds. A card is a slap of noise through a bandpass, a chip is two detuned sines with a knock on the front, a win is four notes going up. No asset files, no round trips, and a sound can be pitched and detuned per call instead of being the same wav three hundred times. Hooked into the FX layer rather than into the games, so every table that throws a chip or turns a card got it at once. The multiples moved, and the test that exists to catch that caught it. The naive strategy now calls UNO, because calling is a button and not a strategy — what these tiers price is bad card play, not a player who ignores the felt shouting at them — and on that footing the normal tables come back to where they were (40.1 / 28.5 / 23.1). No Mercy Full House did not: it was paying a *negative* house edge, which is the house paying you to sit down. Re-priced 3.8 -> 3.5. Claude-Session: https://claude.ai/code/session_013M5nD7PgUboJXoDcYHzpuJ |
||
|
|
5ca056bf20 |
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. |