games: a gallows you can bet on
Hangman, and it plays for chips — which the plan had down as a free game, on the grounds that trivia has no euro coupling in gogobee. But a free game in a casino reads as a demo, so it stakes like everything else. The idea that makes it a casino game rather than hangman with a wager stapled on: the gallows is the payout meter. A wrong guess draws a limb *and* takes a tenth off what a win is worth, because those are the same event and showing them as one is the entire reason to bet on this. Short phrases pay 2.6x (fewer letters, less to go on), long ones 1.6x — the floor is 1x, so a win never hands back less than the stake, and the rake still comes out of winnings only. State.Pays() is the number the felt quotes and the number settle() lands on. They were briefly two sums, and the table spent an afternoon advertising a pre-rake payout it didn't honour. Two things the storage layer had already decided for us, and one it hadn't: game_live_hands is keyed on the player, so "one game at a time" holds across games for free (a live hangman 409s a blackjack deal, stake intact). But table() unmarshalled every live row as a blackjack hand, which does not fail on a hangman row — it quietly yields an empty hand. It dispatches on the game now. commit() is the settle path both games share, and casinoRoutes() the one route list, since the dev rig wires its own mux and a second copy is a copy that stops including the newest game. Driven in a browser, win and loss: 200 at 2.34x paid 455 and the bar landed on it; six wrong took the stake and no more; a reload mid-phrase brought back the board, the limbs, the multiple, the spent keys and the chips on the spot. The browser found the two bugs a Go test can't — a lives counter under the house rack, and a word wrapping early because the rack's clearance was on the whole column instead of the one row beside it.
This commit is contained in:
@@ -147,9 +147,55 @@ A multi-session build. This section is the handover; read it before anything els
|
||||
that adventure news already set. Restarted, it logs
|
||||
`pete games: escrow loop started interval=3s`.
|
||||
|
||||
- **Hangman, and it plays for chips.** *(2026-07-14. This revises §7's "Phase 2 —
|
||||
no escrow": the decision was that a free game in a casino reads as a demo, so
|
||||
hangman stakes chips like everything else and reuses the money path whole.)*
|
||||
- **The gallows is the payout meter.** You pick a tier, stake, and get six
|
||||
lives. Every wrong guess draws a limb *and* takes a tenth off the base
|
||||
multiple — one event, shown as one event. Short phrases pay 2.6×, medium 2.0×,
|
||||
long 1.6× (short is hardest: fewer letters, less to go on). Floored at 1×, so
|
||||
a win never hands back less than the stake, and the rake still comes out of
|
||||
winnings only.
|
||||
- `internal/games/hangman` — the same pure reducer as blackjack, phrases
|
||||
embedded (`phrases.txt`, 205 of them, video-game flavoured, lifted from
|
||||
gogobee). `State.Pays()` is the number the felt quotes *and* the number
|
||||
settle() lands on: they were briefly two sums and the table advertised a
|
||||
pre-rake payout it didn't honour. One function now, and a test that walks a
|
||||
game asserting the quote equals the payout at every step.
|
||||
- **The browser never sees the phrase.** Cells carry the letter or an empty
|
||||
string — not the letter with a hidden flag — and the phrase itself is only
|
||||
added to the payload once the game is over and it decides nothing.
|
||||
- Two things the storage layer already gave us for free, and one it didn't:
|
||||
`game_live_hands` is keyed on the *player*, so "one game at a time" holds
|
||||
across games with no new code (a live hangman 409s a blackjack deal). But
|
||||
`table()` used to unmarshal any live row as a blackjack hand — which does not
|
||||
*fail* on a hangman row, it just silently yields an empty hand. It now
|
||||
dispatches on `live.Game`.
|
||||
- `commit()` in games_play.go is the shared settle path (seat → pay → audit →
|
||||
clear → touch). Both games go through it so neither re-derives an ordering
|
||||
that took a while to get right. `casinoRoutes()` is likewise the single route
|
||||
list, because devcasino_test.go has to wire its own mux and a second copy is
|
||||
a copy that stops including the newest game.
|
||||
- Driven in a real browser, win and loss: a 200 stake at 2.34× paid 455 and the
|
||||
bar landed on it; six wrong took the stake and nothing more; a reload
|
||||
mid-phrase brought back the board, the limbs, the multiple, the spent keys and
|
||||
the stake on the spot. Two layout bugs only the browser could show: the lives
|
||||
counter ran under the house rack, and the board wrapped a word early because
|
||||
the rack's clearance padding was on the whole column instead of the one row
|
||||
level with it.
|
||||
|
||||
### Next, in order
|
||||
|
||||
1. Phase 2 (trivia, hangman), 3 (UNO), 4 (hold'em) as below.
|
||||
1. 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
|
||||
**ladder**: stake once, answer a run of questions, each right answer compounds
|
||||
the multiple and a wrong one loses the lot, with the option to walk. Note the
|
||||
real risk — trivia answers are googlable, so a tight per-question clock is the
|
||||
only thing making a slow-but-correct answer worth less than a fast one. Score
|
||||
the clock server-side; the browser's countdown is decoration.
|
||||
2. Phase 3 (UNO), Phase 4 (hold'em) as below.
|
||||
|
||||
Still open on the table itself, none of it blocking: **split** isn't implemented (the
|
||||
engine has no move for it), the felt is roomy at desktop widths with only one seat on
|
||||
|
||||
Reference in New Issue
Block a user