aboutsummaryrefslogtreecommitdiff
path: root/building_guide/rooms.md
diff options
context:
space:
mode:
Diffstat (limited to 'building_guide/rooms.md')
-rw-r--r--building_guide/rooms.md347
1 files changed, 140 insertions, 207 deletions
diff --git a/building_guide/rooms.md b/building_guide/rooms.md
index 635c43c..9cb93b1 100644
--- a/building_guide/rooms.md
+++ b/building_guide/rooms.md
@@ -234,25 +234,37 @@ Mobs stop wandering while in combat. Dead mobs respawn at their home room.
### Casino Tables
-Rooms may host one or more multiplayer casino tables. Players join by typing
-`play <game>`; the table ID is an internal room-local identifier and does not
-need to be typed. Blackjack and baccarat are the multiplayer table games;
-slots run as single-player machines (see below), and older rooms that declare
-slots under `casino_tables` are migrated to machines automatically.
+Rooms host multiplayer tables under `casino_tables` and single-player slot
+machines under `casino_machines`. Players join a table with `play <game>`, and
+a machine by its `variant` (or by its bare game name when it is the room's
+only machine of that game). Because a table is addressed by its game name, one
+room may host at most one table per game; video poker cabinets are addressed
+by their `id`.
-`max_players`, `betting_ticks`, `min_bet`, and `max_bet` use defaults when omitted.
-Game-specific rule configuration is nested alongside the table definition.
+Multiplayer table fields (`casino_tables`):
-Blackjack tables support sequential player turns and configurable house rules:
+| Field | Type | Default | Notes |
+|---|---|---|---|
+| `id` | string | — | Required. Nonempty, no surrounding whitespace, unique among the room's tables and machines. |
+| `game` | string | — | Required. `blackjack`, `baccarat`, `roulette`, `craps`, `paigow`, or `video_poker`. |
+| `max_players` | int | 8 | 0–64. Video poker is forced to 1; Pai Gow caps at 6. |
+| `betting_ticks` | int | 20 | 0–3600. |
+| `min_bet` | int | 1 | Must not exceed `max_bet`. |
+| `max_bet` | int | 1000000 | Must be at least `min_bet`. The sentinel `9223372036854775807` means no cap. |
+
+Game rules are nested in a block named after the game. `id` and `game` are the
+only required keys; omitted numeric fields keep their defaults.
+
+#### Blackjack
```yaml
casino_tables:
- id: blackjack
game: blackjack
max_players: 6
- betting_ticks: 20
min_bet: 1
max_bet: 1000
+ betting_ticks: 20
blackjack:
decks: 8
shuffle_at: 0.25
@@ -268,166 +280,112 @@ casino_tables:
split_unlike_ten_values: true
```
-Blackjack supports `hit`, `stand`, `double`, `split`, `insurance`, and
-`surrender`. A player blackjack against a dealer blackjack pushes. When the
-dealer's up card is an ace, an insurance window opens before anyone plays:
-each bettor may type `insurance` to buy cover for half their base bet, and
-any other action declines. Once every bettor has decided (or the window times
-out), a dealer blackjack settles the round immediately with insurance paying
-its configured profit multiplier; otherwise play begins. Split hands can be
-configured independently for re-splitting aces and doubling, and split aces
-receive one card each with no further actions beyond a configured re-split.
-
-Two blackjack knobs are player-favourable and on by default, so set them
-explicitly when authoring a standard-rules table:
-
-- `split_unlike_ten_values: true` lets any two 10-valued cards split
- (K+Q, 10+J). Casinos vary: some permit any ten-valued pair, others
- require identical ranks.
-- `double_split_blackjack: true` pays a 3+ card hand that reaches 21 after a
- split and double as a blackjack (3:2). Standard play pays 1:1 for any 21 on
- a split hand, doubled or not. Both raise the table's actual RTP above the
- nominal blackjack payout.
-
-Baccarat's `banker_payout` (0.95 = a 5% commission) rounds the commission UP:
-a winning banker bet too small to carry a whole credit of commission returns
-only the stake rather than paying a commission-free 1:1. Payout multipliers
-are capped (`blackjack_payout`/`insurance_payout` ≤ 5, `tie_payout` ≤ 10,
-commission-table `banker_payout` ≤ 0.99) so a typo cannot silently create an
-absurd table. No-commission baccarat variants require `banker_payout: 1`
-(or omit it); their compensating exceptions are enforced by the engine.
-
-### Blackjack rule cards and test tables
-
-`look blackjack`, `look blackjack table`, or `look <table-id>` displays the
-fixture's complete effective rule card (including omitted/default settings,
-fixed engine rules, limits, and timers). Joining with `play blackjack` also
-shows it privately. No scenery object or room-description text is required:
-placing a `casino_tables` entry in any room brings the rule card with it.
-Currently only one table of each game is allowed per room; use separate rooms
-for different blackjack tables.
-
-The casino has these test profiles:
-
-| Room | Table | Decks | Blackjack | Soft 17 | DAS | Maximum hands | Re-split aces | Surrender |
-| --- | --- | --- | --- | --- | --- | --- | --- | --- |
-| 6110 | Classic | 6 | 3:2 | Stand | Yes | 4 | No | No |
-| 6112 | Mercy | 6 | 3:2 | Stand | Yes | 4 | Yes | Late |
-| 6114 | Single Deck | 1 | 6:5 | Hit | No | 2 | No | No |
-| 6116 | Midnight | 8 | 3:2 | Hit | Yes | 4 | No | No |
-
-All four disable the nonstandard split-and-double blackjack bonus. Use
-`blackjack_payout: 1.5` for 3:2 or `1.2` for 6:5. `max_split_hands: 1`
-disables splitting; 2 allows one split, and larger values allow re-splitting
-within the cap (aces additionally require `resplit_aces`).
-
-Common casino variations not currently configurable are restricted double-down
-totals (e.g. 9–11 or 10–11 only), hitting split aces, disabling insurance,
-and a separate even-money action for a natural against an ace. Doubling is
-always for the full original stake, not “double for less.” Late surrender is
-supported; early surrender is not. Side bets are not implemented.
-
-Casino-fidelity caveats: fractional credits round down. Shoe coverage may
-trigger an early shuffle, and exhaustion rebuilds the shoe mid-round rather
-than guaranteeing a casino-style finite shoe for the whole round. A connected
-solo player's action timer also has special grace behavior. None of these
-mechanics were changed by adding rule cards.
-
-At a blackjack table, `bet` uses the configured minimum wager; `bet min`,
-`bet max`, and `bet <amount>` select the wager explicitly.
-
-Card art is loaded from `data/cards.yaml` and is shared by every card game
-(blackjack, baccarat). Each of the 52 rank/suit
-keys may define Unicode art for sizes 5, 7, and 9. Missing art uses the
-generated renderer. Players can choose `option card_size 1`, `5`, `7`, or `9`;
-size 1 uses Unicode playing-card symbols. ASCII mode always uses generated
-5x5 cards. `option card_style light` adds a white card background; `dark` is
-the default style. The same art renders baccarat hands. `option casino_display`
-only controls menu verbosity (`detailed` is the default; `compact` shortens
-menus and action hints) and never changes card size.
-
-Baccarat tables play punto banco: every participant bets on the player, the
-banker, or a tie, then the dealer plays out both hands by fixed drawing rules
-— there are no mid-round player decisions:
+| Field | Type | Default | Notes |
+|---|---|---|---|
+| `decks` | int | 8 | 0–64. |
+| `shuffle_at` | float | 0.25 | Shoe fraction; must be greater than 0 and less than 1. |
+| `blackjack_payout` | float | 1.5 | Profit multiplier; at most 5. |
+| `dealer_hit_soft_17` | bool | false | |
+| `max_split_hands` | int | 4 | `1` disables splitting, `2` allows one split, and larger values allow re-splitting up to the cap. |
+| `resplit_aces` | bool | false | |
+| `double_after_split` | bool | true | |
+| `double_split_blackjack` | bool | true | Pays a split and doubled 3+ card 21 as a blackjack. |
+| `insurance_payout` | float | 2 | Profit multiplier; at most 5. |
+| `surrender` | bool | false | Requires `min_bet` of at least 2. |
+| `action_ticks` | int | 20 | 0–3600. |
+| `split_unlike_ten_values` | bool | true | Allows any two 10-valued cards to split. |
+
+#### Baccarat
```yaml
casino_tables:
- id: baccarat
game: baccarat
max_players: 6
- betting_ticks: 20
min_bet: 1
max_bet: 1000
+ betting_ticks: 20
baccarat:
- variant: commission # commission (default), super_6, ez_baccarat
+ variant: commission
decks: 8
shuffle_at: 0.25
- tie_payout: 8 # 8:1 or 9:1 are common
- banker_payout: 0.95 # omit or use 1 for no-commission variants
- pair_bets: false # player_pair and banker_pair, 11:1
- dragon_7: false # winning three-card Banker 7, 40:1
- panda_8: false # winning three-card Player 8, 25:1
- super_6_bet: false # winning Banker 6, 12:1
-```
-
-At a baccarat table, `bet <amount> on player`, `bet <amount> on banker`, and
-`bet <amount> on tie` place wagers (the words and amounts may come in any
-order, and `min`/`max` work as usual). A bare `bet` repeats the previous
-amount and target. Winning player bets pay 1:1; on commission tables winning
-banker bets pay `banker_payout`:1 (0.95 is the classic 5% commission); winning tie bets pay
-`tie_payout`:1. On a tie, player and banker bets push. When the betting
-window closes, both hands are dealt by the standard tableau: naturals (8 or
-9) stand immediately, the player hand draws on 0-5, and the banker hand
-follows the usual third-card rules.
-
-#### Baccarat variants, rule cards, and test tables
-
-- **Commission punto banco:** default 5% commission on Banker winnings;
- reduced commission can be configured with `banker_payout` (e.g. 0.98).
-- **Super 6 (`super_6`):** no commission; Banker wins pay 1:1 except a
- winning Banker 6, with either two or three cards, pays 1:2.
-- **EZ Baccarat (`ez_baccarat`):** no commission; Banker wins pay 1:1 except
- a winning **three-card** Banker 7 pushes the Banker wager. Player and Tie
- wagers still lose on that outcome. A two-card Banker 7 pays normally.
-
-All variants use the same automatic punto banco drawing tableau. Standard,
-mini-, and midi-baccarat mainly differ in seating, limits, dealing pace, and
-card handling, not this tableau; they do not need separate settlement engines.
-Deck count, cut threshold, limits, seats, and Tie payout remain table-specific.
-
-Optional wagers default off and may be enabled independently on any variant.
-Use `bet 10 on player_pair`, `banker_pair`, `dragon_7`, `panda_8`, or `super_6`
-where offered. Pairs mean matching **ranks** of the first two cards (not merely
-matching baccarat values); they settle independently of which hand wins and
-can win on a tie. Dragon 7, Panda 8, and Super 6 require an outright hand win,
-not a tie. Their fixed profit odds are listed in the configuration example.
-The separate Super 6 optional bet is not the main Banker's half-pay rule.
-
-**Current wagering limitation:** one wager per player per round. Optional
-bets are selectable wagers, not additions to a simultaneous Player/Banker bet.
-All use the table's bet limits. Fractional winnings round down; for exact
-5% commissions use multiples of 20 credits, and for half-pay wins use even
-stakes. Commission is deducted immediately, not collected later on a ledger.
-
-`look baccarat`, `look baccarat table`, and `look <table-id>` display the full
-rule card, as does joining with `play baccarat`. It lists every supported
-setting, including disabled optional bets, automatic drawing rules, rounding,
-and operational limits. It belongs to the table and works in any room without
-a plaque or special room text. One baccarat table is allowed per room.
-
-| Room | Table | Variant | Decks | Tie pays | Optional wagers |
-| --- | --- | --- | --- | --- | --- |
-| 6127 | Harbor | 5% commission | 8 | 8:1 | Player/Banker pair |
-| 6128 | Charter | Super 6 | 6 | 9:1 | Pairs, Super 6 |
-| 6129 | Crown | EZ Baccarat | 8 | 8:1 | Dragon 7, Panda 8 |
-
-Not modeled: player squeeze/shoe-passing ceremonies, burn-card rituals,
-deferred commission, simultaneous wagers, and other casino-specific side-bet
-paytables (such as Dragon Bonus or progressive jackpots). These are not
-implied by selecting an implemented variant.
-
-Single-player machines use `casino_machines` and are separate from multiplayer
-tables:
+ tie_payout: 8
+ banker_payout: 0.95
+ pair_bets: false
+ dragon_7: false
+ panda_8: false
+ super_6_bet: false
+```
+
+| Field | Type | Default | Notes |
+|---|---|---|---|
+| `variant` | string | `commission` | `commission`, `super_6`, or `ez_baccarat`. |
+| `decks` | int | 8 | 0–64. |
+| `shuffle_at` | float | 0.25 | Must be greater than 0 and less than 1. |
+| `tie_payout` | float | 8 | Profit multiplier; at most 10. |
+| `banker_payout` | float | 0.95 | Commission tables only; at most 0.99. The `super_6` and `ez_baccarat` variants require 1 (or omit it). |
+| `pair_bets` | bool | false | Enables the player and banker pair wagers. |
+| `dragon_7` | bool | false | Enables the Dragon 7 wager. |
+| `panda_8` | bool | false | Enables the Panda 8 wager. |
+| `super_6_bet` | bool | false | Enables the optional Super 6 wager; separate from the `super_6` variant. |
+
+#### Roulette
+
+```yaml
+casino_tables:
+ - id: roulette
+ game: roulette
+ max_players: 8
+ min_bet: 1
+ max_bet: 10000
+ betting_ticks: 15
+ roulette:
+ double_zero: false
+ racetrack: false
+ spin_ticks: 4
+```
+
+| Field | Type | Default | Notes |
+|---|---|---|---|
+| `double_zero` | bool | false | false selects the European 37-pocket wheel; true selects the American 38-pocket wheel. |
+| `racetrack` | bool | false | Adds the French call bets; single-zero tables only. |
+| `spin_ticks` | int | 3 | 0–3600. |
+
+#### Craps
+
+```yaml
+casino_tables:
+ - id: craps
+ game: craps
+ max_players: 12
+ min_bet: 1
+ max_bet: 10000
+ craps:
+ variant: bank
+ odds: 0
+ field_12: 2
+```
+
+| Field | Type | Default | Notes |
+|---|---|---|---|
+| `variant` | string | `bank` | `bank`, `simplified`, `high_point`, `crapless`, `new_york`, `die_rich`, or `diceless`. Each is a separate house edition. |
+| `odds` | int | 0 | `0` selects 3-4-5x odds; `1`–`1000000` selects a uniform multiple. Editions without free odds require `0`. |
+| `field_12` | int | 2 | `2` or `3`: the field's payout on a 12. |
+
+#### Pai Gow Poker
+
+Pai Gow tables are configured under `pai_gow`; see
+[Pai Gow Poker tables](pai_gow.md) for every option.
+
+#### Video poker
+
+Video poker cabinets are single-seat `casino_tables` entries; see
+[Video poker cabinets](video_poker.md) for every option.
+
+#### Slot machines
+
+Slots are single-player machines and must be declared under `casino_machines`,
+not `casino_tables`:
```yaml
casino_machines:
@@ -440,57 +398,32 @@ casino_machines:
payout: 0.97
```
+| Field | Type | Default | Notes |
+|---|---|---|---|
+| `id` | string | — | Required; same rules as table IDs. |
+| `game` | string | — | Required; only `slots`. |
+| `variant` | string | `huff_and_even_more_puff` | Registered in Go (`slotVariants` in `internal/casino/registry.go`); an unregistered name is rejected. |
+| `min_bet` | int | 1 | |
+| `max_bet` | int | 1000000 | Must be at least `min_bet`. |
+| `spin_ticks` | int | 2 | 0–3600. |
+| `payout` | float | 0.97 | Target return-to-player fraction; greater than 0 and at most 1. |
+
#### Casino configuration validation
-Room create/update requests validate casino definitions before writing; startup
-validation reports invalid definitions, and game lookup refuses invalid room
-casino configurations. Raw file editing is not a validated save endpoint.
-
-- IDs must be nonempty, have no surrounding whitespace, and be unique across
- the room's tables and machines.
-- Supported table games are `blackjack`, `baccarat`, `roulette`, `craps`, and
- `paigow` ([Pai Gow configuration and variants](pai_gow.md)). Single-seat
- `video_poker` cabinets also use the table adapter ([cabinet guide](video_poker.md)).
- Roulette configuration basics: `roulette.double_zero` selects the American
- 38-pocket wheel (otherwise the European 37-pocket wheel), `roulette.spin_ticks`
- defaults to 3, and `roulette.racetrack: true` adds the French call bets on
- single-zero tables only. Craps rules and house editions are listed in-game via
- `help craps` and `table rules`; slots are machines only
- and must be declared under `casino_machines`. A `slots` entry under
- `casino_tables` is rejected. Machines use `slots`
- with the optional variant `huff_and_even_more_puff`. A machine variant is
- registered in Go (`slotVariants` in `internal/casino/registry.go`); naming an
- unregistered variant is a validation error.
-- A multiplayer table is addressed by its game name (`play blackjack`), so a
- room may host only one table per game.
-- A machine is addressed by its `variant` when one is declared, so a room may
- host several variants of one game, each reached by its own variant name. A
- bare game name addresses a machine only when it is the sole machine of that
- game (or declares no variant). Two variant-less machines of one game, or two
- machines sharing a variant, are rejected as unreachable duplicates.
-- Omitted/zero numeric values keep their existing defaults. Negative bets,
- timers, seat counts, split counts, decks, and payouts are rejected.
-- An explicit `max_bet` must be at least `min_bet`. An omitted or zero
- `max_bet` uses the default cap (1,000,000); the explicit sentinel
- `9223372036854775807` means no cap.
-- A blackjack table that offers `surrender` must set `min_bet` to at least 2:
- a surrendered hand returns half the bet, which rounds down to zero below
- that (the omitted default of 1 is rejected too).
-- Shoe coverage is best-effort, not a hard floor. The shoe reshuffles at the
- start of a round when it is smaller than the heuristic round size (eleven
- cards per hand--an all-ace hand can exceed that), and `draw()` rebuilds an
- exhausted shoe in place. A small `decks` value therefore never invalidates
- a table or darkens the room's other casino games.
-- `decks` is capped at 64 to bound shoe allocation. An explicit `shuffle_at`
- must be greater than zero and less than one.
-- Payouts and shuffle fractions must be finite (no NaN/infinity). Slots `payout`
- is an RTP fraction greater than zero and at most one; zero defaults to 0.97.
-- Unknown casino fields are rejected by the room create/update API.
-
-Editing a room hot-reconfigures an **idle** table or machine on the next
-lookup; an occupied game keeps its live rules until it becomes idle, so a
-committed wager is never discarded. Use explicit, modest wager limits:
-validation does not yet make currency arithmetic overflow-safe.
+Room create/update requests validate casino definitions before writing;
+startup validation reports invalid definitions, and game lookup refuses
+invalid room casino configurations. Unknown fields are rejected.
+
+- IDs share one namespace across the room's tables and machines.
+- One table per game per room. Machines address by `variant`, so several
+ variants may share a room; two variant-less machines of one game, or two
+ machines sharing a variant, are rejected as unreachable.
+- Omitted or zero numeric fields keep their defaults; negative bets, timers,
+ seat counts, split counts, decks, and payouts are rejected.
+- Payouts and shuffle fractions must be finite (no NaN or infinity).
+- Editing a room hot-reconfigures an **idle** table or machine on the next
+ lookup; an occupied game keeps its live rules until it becomes idle, so a
+ committed wager is never discarded.
### Objects — interactive fixtures