aboutsummaryrefslogtreecommitdiff
path: root/internal/casino
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino')
-rw-r--r--internal/casino/baccarat.go8
-rw-r--r--internal/casino/baccarat_test.go3
-rw-r--r--internal/casino/blackjack.go25
-rw-r--r--internal/casino/blackjack_test.go15
-rw-r--r--internal/casino/machine.go10
-rw-r--r--internal/casino/machine_test.go2
-rw-r--r--internal/casino/types.go16
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 {