19255d933a75bb8773f355b28a8071ed5217d33f
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8504c4f47a |
games: burial send-offs, money rain on wins, and honest stack targeting
Three front-end changes to the casino games:
- uno: a draw-card stack now names the seat it's actually pointed at
("+N on Beep") instead of always reading "+N on you". The bill reads
v.turn, which the engine advances to the target when a draw card lands.
- uno: the No Mercy rule buries a seat with some ceremony now. bury()
rolls one of four send-offs over the seat (or your hand, if it's you) —
a rockslide that piles up, a tombstone, a coffin, or the Mega Man death
burst — each with its own synth sound, and a static headstone under
reduced motion. The "Buried on N" badge still rides alongside.
- all games: winning makes it rain. FX.moneyRain drops a curtain of bills
and coins with a jackpot coin-cascade sound, gated by significance so it
reads as a win and not as noise: hold'em keeps its light per-pot confetti
and only rains on a real haul (>= 20bb), while the confetti stays the
rare cherry (natural 21, full board clear, phrase solved outright).
|
||
|
|
a5b7e41929 |
games: the card the dealer never turned over, and the bet that came back doubled
Bust every hand and the dealer doesn't draw, which is right, but it was also not turning over: reveal is only emitted by dealerPlay, and busting out skips the dealer entirely. The browser kept the hole card face down while the settled state printed the dealer's whole total under it. Emit the reveal on that path. And standing your bet back up after a reload read the hand's bet straight off the settled state, which a double has already doubled. Reload, double 200, and the next hand starts with 400 on the spot. Plus: the share card was hand-writing a Content-Length that ServeContent overwrites anyway, and serving a zero-byte 200 for a room with no card. Claude-Session: https://claude.ai/code/session_013M5nD7PgUboJXoDcYHzpuJ |
||
|
|
6f34a89622 |
games: the hand that becomes two, and the bet that has to follow it
Blackjack has a split. It was the last rule missing from a game that has been live for a week, and it is the only move in blackjack that takes chips out of your stack *after* the cards are out — which is most of what there is to get wrong about it. So the state stops pretending. State.Player is gone; there is a slice of Hands, each with its own cards, its own bet, its own outcome and its own payout, and an Active index the player works left to right. Settle runs per hand and rakes per hand: netting them against each other first would mean a player who won one and lost one paid no rake at all, which is not a rake, it's a discount for splitting. The web layer takes the second bet before the move and hands it straight back if the engine refuses — the same shape double already used, except double was staking st.Bet, the whole table's stake, which was the same number as the hand's until today and is now emphatically not. DoubleCost/SplitCost are the active hand's, and the felt would have found this by charging you 300 to double the third hand of a split. The rules that cost money if you guess them: split aces get one card each and no say (a pair of aces is otherwise the best hand in the game, forever), 21 on a split hand is twenty-one and not a natural (it does not pay 3:2 — the test that pins this is the most expensive one in the file), same rank rather than same value (a king and a queen are not a pair), four hands maximum, double after split allowed, and if every hand busts the dealer does not turn over. A live hand outlives a deploy, so State.UnmarshalJSON still reads the old blobs: "player" with no "hands" becomes one hand holding the whole stake. Without it, a player mid-hand at restart is a player whose cards vanished — which is not a decode error, and would not have looked like one. On the felt a hand is now a box with its own spot, and a split is a card lifting out of one hand into a new one with a second stack of chips flying after it from your pile. Verified in a browser against a real pair: chips 4738 -> 4638 on the split, two hands played out, one push and one loss, "Down on the deal. -100", 4738 back. Three hands stack without collision at 390px. Settled hands come back to full brightness — dimming means "not your turn", and when the deal is over they are the thing you are reading. Claude-Session: https://claude.ai/code/session_013M5nD7PgUboJXoDcYHzpuJ |
||
|
|
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 |
||
|
|
3e9b93af55 |
games: the clock beats the walk button, and the rack isn't betting
The trivia ladder handled a walk before it looked at the clock, so the timeout only ever bit if the browser volunteered it. Sit on a question, look it up, answer if you find it and walk if you don't, and you never lose a ladder. The clock is now the first thing that happens to a move. The house's chip rack was wired up as bet buttons on blackjack and hangman: it's four spans with data-chip on them and nothing said the handler only wanted the real ones. Clicking the house's money raised your bet. Hangman had two definitions of "a letter you'd guess" — unicode in the engine, ASCII in the renderer — and a phrase with an accent in it would have had no tile to fill and no key to fill it with. One definition now. Plus: trivia's countdown no longer freezes at zero when the server turns down a timeout report it was early for, questions whose wrong answer decodes into the right one are dropped at the door, and hangman bets on PeteFX's spot like every other table instead of its own copy of it. |
||
|
|
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. |
||
|
|
6961f90634 |
games: the money moves
The table dealt cards but settled money by editing a number. So the felt got the two things it was missing: a bet spot in front of you, and the house's rack beside the shoe. Every chip is now always travelling between one of those and the other. You build a bet by throwing chips onto the spot — the chip you clicked is the chip that flies. The stake sits there through the hand. The house pays out of its rack into the spot, and the pile is then swept back to your stack. A loss goes to the rack and does not come back. Two rules hold it together. The number under the pile is a readout of the pile, never the other way round: the bet starts at nothing rather than at a default nobody put down, and a settled hand leaves your stake back up on the spot, because otherwise the panel prints "your bet: 300" over an empty circle. And the chip bar does not move until the chips that justify it have landed — a counter that pays you before the dealer turns over is a counter that has told you the ending. casino-fx.js is the engine underneath: chips fly on an arc, out of a fixed overlay so no container clips one crossing from a button to the felt. It knows nothing about blackjack. Also: cards land with weight and a degree or two of tilt, so a hand looks dealt rather than typeset; the dealer takes a beat before drawing out; and a natural gets confetti, which is the only thing in the room that does. Driven in a real browser, which is the only way to review an animation — and which is what caught the verdict pill rendering white on white in a dark room, a chip rack sitting on top of the dealer, and Hit being offered over a table that was still being paid out. devcasino_test.go is that harness, kept. |
||
|
|
b00da21a47 |
games: draw the card, don't type it
The last attempt built a card face out of text: a "♠" in a span for every pip. At the size a card actually is, a suit character renders as a speck — the shape is whatever font answered, it doesn't scale, and it can't be put on the half-row a real pip layout needs. The result read worse than the plain rank it replaced. So each face is one SVG on a 100×140 field, suits as vector shapes, pips at the coordinates a printed deck puts them. Courts get a framed panel with the suit above the letter and again below it upside down — mirroring a letter, which is what the first pass did, just stacks two of them into a blob; a real court mirrors a figure. Also restores .pete-card-back, which went out with the text rules it was sitting among: without it a face-down card had no back at all, so the dealer's hole card was invisible on the felt. Caught by driving a hand. |
||
|
|
8ec13eab5b |
games: the casino moves out, and gets a clock of its own
The tables were living in the news app's shell: Pete's face in the header and the footer, the channel nav, search, the reader, the weather canvas, the PWA. A casino is not a news page with a felt on it. So it gets its own layout. What carries over is the design language — the four palette vars, Fredoka/Nunito, the fat rounded cards, the dropped shadow. What doesn't is every control it has no use for. gamesPage stops embedding the news pageData, which is what keeps the furniture from drifting back one convenient field at a time. It keeps a clock, but tells a different joke with it: Casinopolis by day, Casino Night Zone from six, palette and felt and the sign over the door all changing together. The rule lives in roomAt() for the first paint and again in the browser, so a player abroad gets their own evening. And the cards are cards now — corner indices in both corners, the bottom one upside down as printed, pips on the three-by-seven grid every real deck has used for four hundred years, courts as a letter with the suit over each shoulder. Driven in a real browser, both rooms, dealt through to a payout. |
||
|
|
c69fbb63db |
games: a blackjack table you can actually sit down at
The engine, the escrow and the wire were all in place; nothing had a browser on the end of it. This is that end: a lobby, a table, and the five endpoints between them. The browser holds no game. It sends intents and gets back a view — the cards it is entitled to see, and the script of how they arrived, one event per card off the shoe. The dealer's hole card is not in the payload at all until the reveal, because a field the client is told to ignore is a field somebody reads in devtools. The shoe lives in game_live_hands, which also means a redeploy mid-hand no longer costs a player their stake: the hand is still there when they come back. The money is ordered so nothing can be spent twice. The stake leaves the stack in the same statement that checks it exists, before a card is dealt. Every new hand is seated with a plain INSERT, so a double-clicked Deal is decided by the primary key rather than by a read that raced — it loses, gets its chips back, and the hand in progress is untouched. A double takes its raise up front and hands it straight back if the engine refuses the move. Cards are dealt rather than swapped in — they fly out of the shoe and turn over, which was a requirement and not a flourish. The faces and the chips are still plain; that's next. |