From 1082f3336085c15992ca83d5553c0ba52208d70a Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Thu, 1 Oct 2026 14:38:08 -0400 Subject: feat: pai gow poker added to the casino --- building_guide/roulette.md | 164 --------------------------------------------- 1 file changed, 164 deletions(-) delete mode 100644 building_guide/roulette.md (limited to 'building_guide/roulette.md') diff --git a/building_guide/roulette.md b/building_guide/roulette.md deleted file mode 100644 index e3794e2..0000000 --- a/building_guide/roulette.md +++ /dev/null @@ -1,164 +0,0 @@ -# Roulette and casino extension readiness - -The casino has a usable extension seam: `tableGame`, generic `tableCore`, -registered constructors/config validation in `registry.go`, structured snapshots, -shared participation/control routing, and durable per-stake wallet handles. -Roulette uses those seams rather than adding another command dispatcher. - -Example table (one game of each type per room remains the manager's rule): - -```yaml -casino_tables: - - id: roulette_single - game: roulette - min_bet: 1 - max_bet: 10000 - betting_ticks: 15 - roulette: - double_zero: false - spin_ticks: 4 -``` - -`double_zero: true` selects the American 38-pocket wheel; otherwise the -European 37-pocket wheel is used. Spin ticks default to 3; betting ticks use -the shared default. Ticks follow the server's configured tick length. -The wheel order is authentic and the outcome is selected uniformly only when -spinning finishes. - -## Presentation - -Art is rendered per recipient in `internal/game/render_roulette.go`, so color -mode and Unicode preference stay per player (the engine snapshot never embeds -ANSI). The views and betting tracker share one snapshot renderer: - -- **Tracker**: a five-line, 74-column strip above the betting board, rendered - by `render_roulette_tracker.go`. The betting snapshot leads with a blank line - so the tracker is separated from the prose or menu that precedes it. It shows - the latest eight hits (newest first), three hot/cold pockets with hit counts, - and red/black/zero percentages. - All stats use a table-local rolling window of 50 completed spins. Zeros are - included in the denominator; ties use numeric order (0, 00, 1, ...). - History is copied into snapshots, never sampled before settlement, and - survives round resets but not table replacement or server restart. - History is copied into snapshots, never sampled before settlement, and - survives round resets but not table replacement or server restart. -- **Snapshot cadence**: the board and tracker are drawn once per round by the - betting menu (on join and after settlement) and by an - explicit `table`/`table rules`. Placing a wager only confirms the wager; it - does not re-send the board snapshot. -- **Betting board**: numbered grid with zeros in the left lane, red/black - pockets, and outside bets. The whole five-row zero lane (three numbered rows - plus the two separators between them) carries the green background, and its - outer corners are rounded in Unicode mode. A racetrack table draws - `renderRouletteRacetrack` above the board: a numbered oval in physical - clockwise wheel order, with 26 at the right tip. Interior dividers meet - each call bet's pockets, left to right: Tiers du Cylindre, Orphelins, - Voisins du Zero, Zero. Labels are centered on the middle row; a label too - wide to leave a blank cell against its dividers wraps onto the rows above - and below it (e.g. "Tiers du" / "Cylindre"), and only the glyphs are - written so the centering padding never paints over the dividers. Connected - box-drawing steps separate Tiers from Orphelins; Zero has a curved boundary - within Voisins. Neighbors is not drawn on the board, but remains in the - payout reference and help. -- **Menu ordering**: the roulette betting menu prints its balance and - heading, then the tracker and board, and only then the wager reminder - (`bet on `, `bet / rebet`, `table / sitout / stop`) so the - instructions follow the table they describe. Other games keep their menu - above the snapshot. -- **Spinning**: a stylised 17x9 wheel (wider than tall so it reads as a circle - in a terminal cell grid). Only the rim is colored: solid red pockets alternate - with a very dark blue that stands in for black (`rouletteWheelBlack`), because - pure black vanishes on dark terminals. Pockets are two cells wide except the - top and bottom centres, which stay one cell. One pocket is green (plus its - diametrically opposite pocket on American wheels). A simple plus marks the - croupier's spindle. No pocket numbers are drawn; with color off the pockets - fall back to R/B/G so the wheel still reads. -- **Result**: the winning pocket plus two physical wheel neighbours on each - side (five cards) at playing-card size. The pocket number sits on the top row, - a rule separates it from the ball (one row above centre), and the color name - is spelled out on the bottom row only when color is off. There is no - surrounding prose. The smallest (five-row) cards drop the rule so the ball - can sit directly under the number. The wheel and result graphics each get a - blank line before and after so they do not crowd the prose. - -The colored payout tables in `internal/game/render_casino_help.go` use the -shared ANSI-aware `ui.Table` renderer, respecting ASCII/Unicode and wrap width. -They show wheel-specific win chances and net profit odds, including call-bet -exceptions. They appear only with `table` and `table rules`, not ambient art. -`table help` opens the current game's guide; `help casino` indexes the brief -casino guides in `data/help/`. `help racetrack` explains chip placements and -rounding, including `zero`/`jeu zero` versus the straight-number target `0`. -Each table's one-line -overview is kept short (`Label | Limits 1-10000 | n/m seated | state`) so it -fits beside a look minimap; `writeLookSideBySide` also wraps any content line -that would otherwise run into the map column. - -Initial scope is **one wager per player per spin**. A wager covers straight-up -numbers, outside bets (colors, parity, high/low, 1to12/13to24/25to36, rows), -and the combination bets: splits, streets, corners, six-lines, trios, and the -basket. Combination targets are a compact number list (`split 1 2`, -`street 1-2-3`, `corner 1 2 4 5`, `line 1 2 3 4 5 6`, `trio 0 00 2`, -`basket`) joined from the tokens after `on`; the leading name is optional -because the geometry of the covered set identifies the bet. Non-adjacent or -wrong-shaped sets are rejected rather than silently accepted. Multiple -simultaneous wagers remain future work: `Participant.Wager` still holds a -single stake. - -Single-zero tables may set `roulette.racetrack: true`. That adds the four -French call bets (Voisins du Zéro, Tiers du Cylindre, Orphelins, Jeu Zero) -and the Neighbors bet (a chosen number plus four on each side in wheel order). -The racetrack is a single-zero layout only: the validator rejects -`racetrack: true` together with `double_zero: true`. A call bet is **one -wager** whose amount is the total stake spread evenly over the bet's chips -(Voisins 9, Tiers 6, Orphelins 5, Jeu Zero 4, Neighbors 9). Because the -components are ordinary inside bets, each returns `36/covered` when its -number hits, so a winning pocket can pay a different multiple of the stake -than another (for example 0 on Voisins pays 24/9, while 4 pays 18/9). The -engine therefore resolves a wager to exact per-pocket `num/den` shares -(`roulette_racetrack.go`) instead of a single multiplier; `bet` validates -every possible return before committing and `settle` floors -`stake*num/den`. The rack panel is drawn by `renderRouletteRacetrack` above -the board and lists each bet's wheel-order series. - -All returns are checked before debiting a wager. Both winning and losing stakes -settle through `PayCredits(return, stakeID)`, including a zero return for losses. -Failures leave the stake pending and do not claim that payment succeeded. -Shutdown and eviction refund outstanding stakes and reset the round. -Disconnect refunds are safe because no partial outcome is sampled or exposed. - -Settlements are rendered by `settlementText` in `internal/casino/table_ux.go`: -a colored win/loss/push headline gives the round's net once (the old -"Net ... | ... returned (stake included)" split is gone), then each named bet -— a blackjack hand, insurance, or the table's spot — is listed with its own -net. Wins are green, losses red, pushes white, and a natural keeps the jackpot -gradient. The engine emits inline color tags (`{casino_result_win}WIN{/}`), -which `Game.casinoProse` expands per recipient against the session palette, so -the engine still never embeds ANSI. Casino prose defaults to white -(`casino_action`), leaving color for emphasized outcomes. - -## Assessment and boundaries - -This is a sound foundation for more testing-stage table games, not a blanket -production-readiness certification. New games must test payout math, overflow, -phase boundaries, disconnects, queued departure, refunds, persistence failure, -and spectator output. Shared card-game loss settlement still uses the void -`ResolveStakesByIDs` API: it preserves failed stakes for recovery but cannot -report that failure to its caller. Roulette avoids that limitation with the -existing error-returning payment API. Uniform per-stake error-returning -settlement would be a worthwhile follow-up for older games. - -Multiple concurrent wagers need their own explicit ledger/limit model instead -of reusing the single `Participant.Wager`. Multiple same-game tables in one room -would also require changing current addressing/validation assumptions. - -## Testing shortcut - -`data/objects/casino_test_portal.yaml` is placed in starting room 1001. -`use casino` gives 1000 chips and teleports to vestibule 6100. It is intentionally -repeatable and uses ordinary trigger/drop/teleport machinery. Keep an inventory -slot free (ordinary trigger drops fall to the ground when inventory is full). -Remove the room's `casino_test_portal` reference before production: this grants -unlimited test funds and bypasses travel/progression. No account roles are needed. - -Single-zero room 6182 and double-zero room 6183 are north of ground-floor -landing 6102. `help roulette` describes player syntax and supported bets. -- cgit v1.2.3