diff options
Diffstat (limited to 'internal/casino')
| -rw-r--r-- | internal/casino/baccarat.go | 8 | ||||
| -rw-r--r-- | internal/casino/baccarat_test.go | 3 | ||||
| -rw-r--r-- | internal/casino/blackjack.go | 25 | ||||
| -rw-r--r-- | internal/casino/blackjack_test.go | 15 | ||||
| -rw-r--r-- | internal/casino/machine.go | 10 | ||||
| -rw-r--r-- | internal/casino/machine_test.go | 2 | ||||
| -rw-r--r-- | internal/casino/types.go | 16 |
7 files changed, 67 insertions, 12 deletions
diff --git a/internal/casino/baccarat.go b/internal/casino/baccarat.go index 729851d..4261270 100644 --- a/internal/casino/baccarat.go +++ b/internal/casino/baccarat.go @@ -65,12 +65,16 @@ func (t *baccaratTable) seats() []*Participant { func (t *baccaratTable) menuEvent(name string) Event { e := t.private(name, t.menu()) e.Color = "casino_menu" - e.Menu = &MenuView{Kind: MenuBaccaratBet} + e.Menu = &MenuView{Kind: MenuBaccaratBet, Commands: []MenuCommand{ + {Command: "bet <amount>", Desc: "Place a wager (min/max accepted)"}, + {Command: "bet <amount> on player|banker|tie", Desc: "Wager on a specific outcome"}, + {Command: "stop", Desc: "Leave the table"}, + }} return e } func (t *baccaratTable) menu() string { - return "New hand! You can bet, bet <amount> on player|banker|tie (inc. min/max), or stop." + return "New baccarat hand! Place your bet." } func (t *baccaratTable) join(name string) []Event { diff --git a/internal/casino/baccarat_test.go b/internal/casino/baccarat_test.go index 6d3acb1..2cb932e 100644 --- a/internal/casino/baccarat_test.go +++ b/internal/casino/baccarat_test.go @@ -319,6 +319,9 @@ func TestBaccaratMenuAndActions(t *testing.T) { if menu == nil || menu.Kind != MenuBaccaratBet { t.Fatalf("join menu=%+v, want MenuBaccaratBet", menu) } + if len(menu.Commands) != 3 || menu.Commands[0].Command != "bet <amount>" || menu.Commands[2].Command != "stop" { + t.Fatalf("baccarat menu commands=%+v", menu.Commands) + } if events := table.action("alice", "hit"); !hasMessage(events, "no player actions") { t.Fatalf("baccarat action not rejected: %+v", events) } diff --git a/internal/casino/blackjack.go b/internal/casino/blackjack.go index ff334c9..3c4dc09 100644 --- a/internal/casino/blackjack.go +++ b/internal/casino/blackjack.go @@ -93,7 +93,11 @@ func (t *blackjackTable) seats() []*Participant { func (t *blackjackTable) menuEvent(name string) Event { e := t.private(name, t.menu()) e.Color = "casino_menu" - e.Menu = &MenuView{Kind: MenuBlackjackBet} + e.Menu = &MenuView{Kind: MenuBlackjackBet, Commands: []MenuCommand{ + {Command: "bet", Desc: "Place a wager (min/max accepted)"}, + {Command: "bet <amount>", Desc: "Place a specific wager"}, + {Command: "stop", Desc: "Leave the table"}, + }} return e } @@ -446,10 +450,25 @@ func (t *blackjackTable) prompt() []Event { actions := t.availableActions(p, h) e := t.private(name, fmt.Sprintf("Your turn (%s).\nActions: %s", strings.Join(ranks, ","), strings.Join(actions, ", "))) e.Color = "casino_menu" - e.Menu = &MenuView{Kind: MenuBlackjackTurn, Title: fmt.Sprintf("Your turn (%s)", strings.Join(ranks, ",")), Actions: actions} + e.Menu = &MenuView{Kind: MenuBlackjackTurn, Title: fmt.Sprintf("Your turn (%s)", strings.Join(ranks, ",")), Actions: actions, Commands: blackjackActionCommands(actions)} return []Event{e} } +func blackjackActionCommands(actions []string) []MenuCommand { + descriptions := map[string]string{ + "hit": "Take another card", + "stand": "Hold your total", + "double": "Double your bet and take one card", + "split": "Split your pair into two hands", + "surrender": "Forfeit half your bet", + } + commands := make([]MenuCommand, 0, len(actions)) + for _, action := range actions { + commands = append(commands, MenuCommand{Command: action, Desc: descriptions[action]}) + } + return commands +} + func (t *blackjackTable) availableActions(p *blackjackPlayer, hand *blackjackHand) []string { if fromSplitAces(hand) { // Split aces receive one card each and may only be re-split. @@ -657,7 +676,7 @@ func (t *blackjackTable) snapshot(revealDealer bool) *BlackjackSnapshot { } func (t *blackjackTable) menu() string { - return "New hand! You can bet, bet <amount> (inc. min/max), or stop." + return "New blackjack hand! Place your bet." } func handScore(hand *blackjackHand) string { diff --git a/internal/casino/blackjack_test.go b/internal/casino/blackjack_test.go index 89b5d06..a6735c0 100644 --- a/internal/casino/blackjack_test.go +++ b/internal/casino/blackjack_test.go @@ -110,6 +110,21 @@ func TestBlackjackMenuEventCarriesStructuredMenu(t *testing.T) { if event.Menu == nil || event.Menu.Kind != MenuBlackjackBet { t.Fatalf("menu event=%+v", event.Menu) } + if len(event.Menu.Commands) != 3 || event.Menu.Commands[0].Command != "bet" || event.Menu.Commands[2].Command != "stop" { + t.Fatalf("bet menu commands=%+v", event.Menu.Commands) + } +} + +func TestBlackjackActionCommandsDescribeEachAction(t *testing.T) { + commands := blackjackActionCommands([]string{"hit", "stand", "double", "split", "surrender"}) + if len(commands) != 5 { + t.Fatalf("commands=%+v", commands) + } + for _, c := range commands { + if c.Desc == "" { + t.Fatalf("action %q missing a description", c.Command) + } + } } func TestBlackjackInsurancePaysOnDealerBlackjack(t *testing.T) { diff --git a/internal/casino/machine.go b/internal/casino/machine.go index 0ee4cde..79e4cb6 100644 --- a/internal/casino/machine.go +++ b/internal/casino/machine.go @@ -340,13 +340,19 @@ func formatWins(wins []SlotWin, ways int) string { return message[:len(message)-1] } -const slotMenuText = "Slot machine commands:\n bet <amount> Set your wager\n bet max Set the maximum wager\n spin Spin using your current wager\n autospin Start automatic betting\n stop Leave the slot machine" +var slotCommands = []MenuCommand{ + {Command: "bet <amount>", Desc: "Set your wager"}, + {Command: "bet max", Desc: "Set the maximum wager"}, + {Command: "spin", Desc: "Spin using your current wager"}, + {Command: "autospin", Desc: "Start automatic betting"}, + {Command: "stop", Desc: "Leave the slot machine"}, +} func (m *Machine) privateMenu(prefix string) []Event { events := m.private(prefix) events[0].Color = "casino_menu" if m.phase == machineReady { - events[0].Menu = &MenuView{Kind: MenuSlotCommands, Body: slotMenuText} + events[0].Menu = &MenuView{Kind: MenuSlotCommands, Commands: slotCommands} } return events } diff --git a/internal/casino/machine_test.go b/internal/casino/machine_test.go index fdfa42c..386e4f9 100644 --- a/internal/casino/machine_test.go +++ b/internal/casino/machine_test.go @@ -27,7 +27,7 @@ func TestMachineMenuEventCarriesStructuredMenu(t *testing.T) { m := newMachine(1, MachineConfig{ID: "slots", Game: GameSlots}, rand.New(rand.NewSource(7))) m.player = &Participant{Name: "alice", Connected: true} events := m.privateMenu("You sit down at the slot machine.") - if len(events) != 1 || events[0].Menu == nil || events[0].Menu.Kind != MenuSlotCommands || events[0].Menu.Body == "" { + if len(events) != 1 || events[0].Menu == nil || events[0].Menu.Kind != MenuSlotCommands || len(events[0].Menu.Commands) == 0 { t.Fatalf("slot menu event=%+v", events) } } diff --git a/internal/casino/types.go b/internal/casino/types.go index c28b471..52c4bfa 100644 --- a/internal/casino/types.go +++ b/internal/casino/types.go @@ -181,10 +181,18 @@ const ( // presentation layer can enrich it (balances, command colors) without parsing // message prefixes. type MenuView struct { - Kind MenuKind - Body string // command list (MenuSlotCommands) - Title string // prompt heading (MenuBlackjackTurn) - Actions []string // available commands (MenuBlackjackTurn) + Kind MenuKind + Body string // command list (MenuSlotCommands) + Title string // prompt heading (MenuBlackjackTurn) + Actions []string // available commands (MenuBlackjackTurn) + Commands []MenuCommand // unified command list for all casino menus +} + +// MenuCommand is one selectable command shown in a casino menu: the typed +// command text plus a short human-readable description. +type MenuCommand struct { + Command string + Desc string } type Event struct { |
