games: a ladder you climb against the clock

This commit is contained in:
prosolis
2026-07-14 02:11:09 -07:00
parent feb353f789
commit c62d736223
17 changed files with 2292 additions and 4 deletions

View File

@@ -244,9 +244,61 @@ A multi-session build. This section is the handover; read it before anything els
hits the rack, nothing happens, and it looks like the bet is broken. Blackjack's
action buttons are also `[data-move="stand"]`, not `[data-stand]`.
- **Trivia, and it plays for chips.** *(2026-07-14. Built, tested, and — say it
plainly — **NOT YET DRIVEN IN A BROWSER**. Every Go test passes and `go vet` is
clean, which per this plan's own hard-won rule means nothing at all about the
table. Do that first next session; see the bottom of this entry.)*
- **A ladder.** Stake once, then answer a run of twelve. Every right answer
multiplies what you're holding, a wrong one loses the lot, and you may walk
with what you've built. Clearing all twelve ends the run and banks it — a
ladder with no top is a slot machine you can't stop playing, and eventually
every player loses everything to one bad question.
- **The clock is the game, and it is the anti-google mechanism.** Trivia answers
are lookupable, so a right answer is worth what it's worth *when you give it*:
the multiple decays from Fast to Buzzer across the tier's limit (easy 1.30→1.10
over 20s, medium 1.55→1.20 over 18s, hard 1.90→1.30 over 15s), and running out
of time loses exactly as much as being wrong. A timeout that merely cost you the
speed bonus would make "look it up in the other tab" the strongest way to play.
The countdown in the browser is decoration; the clock that scores is
`time.Now()` against the `AskedAt` the server stamped. A reload does not restart
it.
- **A pure reducer still, but the time is an argument** — `ApplyMove(state, move,
now)`. A reducer cannot own a timer, so it doesn't: the only thing that knows
what o'clock it is remains the caller, and the engine stays value-in, value-out.
- **You cannot walk off the first rung** (`ErrNothingBanked`). If you could, seeing
question one and walking would be a free look: stake, peek, walk, restake, and
reshuffle until the question is one you happen to know. The first question is the
price of sitting down.
- **The browser never learns which answer is right.** The four answers cross the
wire without the index; that index is in the engine state, on the server. It
comes back only in the event that *decides* the question, by which point knowing
it is worth nothing. The ladder's remaining questions are never sent at all.
- `internal/games/trivia` — engine, 11 tests. The one that matters most is the
same one hangman needed: the number the felt quotes (`Pays()`) is asserted equal
to the number `settle()` lands on, at every rung.
- **The bank is prefetched, not fetched per question** (`internal/opentdb`,
`storage.DrawTrivia`, table `trivia_questions`). A ladder asks a question every
fifteen seconds with money on a clock the player is scored against; a live fetch
would put OpenTDB's latency and rate limit *inside* that clock. The refill is a
slow background drip (`StartTriviaBank`, 400 per difficulty, one request per six
seconds, stops early when a batch adds nothing new), and a round never waits on
it. Answers are shuffled per-game against the game's own seed, so where the right
answer sits in the table tells a player nothing.
- **Left to do, and it is the whole of the risk:** `PETE_DEV_CASINO=:7788 go test
./internal/web -run TestDevCasino -timeout 0` and *play it*. Watch especially:
the bank is empty on a fresh dev database, so the first start will 503 with "the
question bank is still filling up" until the refill loop has run — the dev rig
does not start that loop, so it likely needs seeding by hand or a call to
`refillTriviaBank`. Then: does the clock bar drain honestly, does the auto-submit
at zero land as a timeout rather than a "that move isn't legal" (the GRACE window
in trivia.js is what defends this), does the reveal mark the right answer, and
does the money settle onto the shared spot the way the other three tables do.
### Next, in order
1. Phase 2's other half: **trivia**. Decided but not built: the question bank is
1. **Drive trivia in a browser** (see above) — it has never been played, and that
means nothing about it is known.
2. Phase 2's other half is now built but unproven: 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