From c94728c929045f7c1bdf0166df152d944e5f99bc Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Thu, 1 Oct 2026 15:47:17 -0400 Subject: feat: better casino game look output, more concise table instructions --- internal/casino/craps_variants.go | 3 +++ internal/casino/pai_gow.go | 24 ++++++++++++++++++++++-- internal/casino/table.go | 10 ++++++++++ internal/casino/table_ux.go | 26 ++++++++++++++++++-------- internal/casino/types.go | 2 +- internal/casino/video_poker.go | 19 +++++++++++++++++++ 6 files changed, 73 insertions(+), 11 deletions(-) (limited to 'internal/casino') diff --git a/internal/casino/craps_variants.go b/internal/casino/craps_variants.go index 7d4d246..4818231 100644 --- a/internal/casino/craps_variants.go +++ b/internal/casino/craps_variants.go @@ -55,6 +55,9 @@ func (c CrapsConfig) title() string { return "Bank Craps" } } + +// Title is the human-readable edition name shown by the look command. +func (c CrapsConfig) Title() string { return c.title() } func (c CrapsConfig) allows(kind string) bool { switch c.variant() { case "simplified", "high_point", "die_rich": diff --git a/internal/casino/pai_gow.go b/internal/casino/pai_gow.go index 21abf4b..17d8971 100644 --- a/internal/casino/pai_gow.go +++ b/internal/casino/pai_gow.go @@ -70,7 +70,21 @@ func (t *paiGowTable) hasParticipant(name string) bool { func (t *paiGowTable) overview() string { t.mu.Lock() defer t.mu.Unlock() - return fmt.Sprintf("%s | %s | %d/%d seats", t.Config.Label(), t.phase, len(t.players), t.Config.MaxPlayers) + return fmt.Sprintf("%s | %d/%d seated", t.Config.Label(), len(t.players), t.Config.MaxPlayers) +} +func (t *paiGowTable) seats() []string { + t.mu.Lock() + defer t.mu.Unlock() + names := make([]string, 0, len(t.players)) + for _, p := range t.players { + names = append(names, p.name) + } + return names +} +func (t *paiGowTable) state() string { + t.mu.Lock() + defer t.mu.Unlock() + return t.phase } func (t *paiGowTable) hintsLocked(name string) []MenuCommand { p := t.player(name) @@ -145,7 +159,13 @@ func (t *paiGowTable) statusLocked(name string) []Event { } e := t.private(name, t.Config.Label()) e.Snapshot = s - e.Menu = &MenuView{Heading: t.Config.Label() + " | " + t.phase, ShowBalance: true, Commands: t.hintsLocked(name)} + // Context-aware hints are attached as a live menu; the shared board + // renderer places them below the felt. Stand-ins (for example a watcher) + // get no menu, so they cannot be told how to bet a game they have not joined. + if commands := t.hintsLocked(name); len(commands) > 0 { + e.Color = "casino_menu" + e.Menu = &MenuView{Heading: t.Config.Label() + " | " + t.phase, ShowBalance: true, Commands: commands} + } return []Event{e} } func (t *paiGowTable) status(name string) []Event { diff --git a/internal/casino/table.go b/internal/casino/table.go index 719bed3..88ffc62 100644 --- a/internal/casino/table.go +++ b/internal/casino/table.go @@ -21,6 +21,10 @@ type tableGame interface { idle() bool status(name string) []Event overview() string + // seats and state expose the live occupancy and lifecycle phase for the + // look command's table-details view. They are read-only. + seats() []string + state() string // wagers is the game's live list of bets the player could make right // now (the "list" command's data; empty when nothing is worth naming, // which also suppresses the "list" hint). hints is the full live hint @@ -118,6 +122,12 @@ func (t *Table) idle() bool { return t.game.idle() } func (t *Table) Status(name string) []Event { return t.game.status(name) } func (t *Table) Overview() string { return t.game.overview() } +// Seats lists the seated players in seat order for the look command. +func (t *Table) Seats() []string { return t.game.seats() } + +// State is the table's live lifecycle phase for the look command. +func (t *Table) State() string { return t.game.state() } + // RulesText is this table's full rule text (the "table rules" wording). func (t *Table) RulesText() string { return rulesTextOnTable(t.Config) } diff --git a/internal/casino/table_ux.go b/internal/casino/table_ux.go index 55c491b..d0211f3 100644 --- a/internal/casino/table_ux.go +++ b/internal/casino/table_ux.go @@ -77,17 +77,27 @@ func (c *tableCore[P]) overview() string { func (c *tableCore[P]) overviewLocked() string { // Concise enough to sit beside a look minimap without wrapping into it. - // The betting countdown lives in the menu heading and warning lines. - return fmt.Sprintf("%s | Limits %s | %d/%d seated", c.Config.Label(), compactBetRange(c.Config.MinBet, c.Config.MaxBet), c.players.count(), c.Config.MaxPlayers) + // The betting countdown lives in the menu heading and warning lines; the + // wager range is shown in the betting menu and on the rule card instead. + return fmt.Sprintf("%s | %d/%d seated", c.Config.Label(), c.players.count(), c.Config.MaxPlayers) } -// compactBetRange renders the wager range in the short form used by the -// one-line table overview (the verbose form stays in the betting menu). -func compactBetRange(minBet, maxBet int) string { - if maxBet == maxBetUnlimited { - return fmt.Sprintf("%d+", minBet) +// seats lists the seated players in seat order for the look details view. +func (c *tableCore[P]) seats() []string { + c.mu.Lock() + defer c.mu.Unlock() + names := make([]string, 0, c.players.count()) + for _, p := range c.players.ordered() { + names = append(names, p.participantPtr().Name) } - return fmt.Sprintf("%d-%d", minBet, maxBet) + return names +} + +// state is the shared lifecycle phase for the look details view. +func (c *tableCore[P]) state() string { + c.mu.Lock() + defer c.mu.Unlock() + return string(c.phase) } func (c *tableCore[P]) warning(name, text string) Event { diff --git a/internal/casino/types.go b/internal/casino/types.go index 1b0826a..9c000b5 100644 --- a/internal/casino/types.go +++ b/internal/casino/types.go @@ -14,7 +14,7 @@ func betRangeText(minBet, maxBet int) string { type GameName string const ( - GamePaiGow GameName = "pai_gow" + GamePaiGow GameName = "paigow" GameVideoPoker GameName = "video_poker" GameSlots GameName = "slots" GameBlackjack GameName = "blackjack" diff --git a/internal/casino/video_poker.go b/internal/casino/video_poker.go index 8c9f66c..e5012f5 100644 --- a/internal/casino/video_poker.go +++ b/internal/casino/video_poker.go @@ -109,6 +109,25 @@ func (t *videoPokerTable) overview() string { func (t *videoPokerTable) overviewLocked() string { return t.settings.CabinetName() + " machine" } +func (t *videoPokerTable) seats() []string { + t.mu.Lock() + defer t.mu.Unlock() + names := make([]string, 0, len(t.players)) + for name := range t.players { + names = append(names, name) + } + return names +} +func (t *videoPokerTable) state() string { + t.mu.Lock() + defer t.mu.Unlock() + for _, p := range t.players { + if p.active { + return "playing" + } + } + return "waiting" +} func (t *videoPokerTable) wagers(string) []WagerRow { return nil } func (t *videoPokerTable) hints(name string) []MenuCommand { t.mu.Lock() -- cgit v1.2.3