aboutsummaryrefslogtreecommitdiff
path: root/internal/casino
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino')
-rw-r--r--internal/casino/baccarat.go6
-rw-r--r--internal/casino/blackjack.go2
-rw-r--r--internal/casino/blackjack_wagers.go2
-rw-r--r--internal/casino/craps.go2
-rw-r--r--internal/casino/craps_rules.go10
-rw-r--r--internal/casino/craps_ux.go8
-rw-r--r--internal/casino/craps_variants.go6
-rw-r--r--internal/casino/craps_wagers.go8
-rw-r--r--internal/casino/house_edge_craps.go6
-rw-r--r--internal/casino/house_edge_test.go16
-rw-r--r--internal/casino/machine.go13
-rw-r--r--internal/casino/manager.go18
-rw-r--r--internal/casino/pai_gow.go6
-rw-r--r--internal/casino/pai_gow_rules.go2
-rw-r--r--internal/casino/roulette.go2
-rw-r--r--internal/casino/roulette_wagers.go14
-rw-r--r--internal/casino/table.go7
-rw-r--r--internal/casino/table_hints.go4
-rw-r--r--internal/casino/table_ux.go4
-rw-r--r--internal/casino/table_ux_test.go2
-rw-r--r--internal/casino/types.go3
-rw-r--r--internal/casino/video_poker.go14
22 files changed, 90 insertions, 65 deletions
diff --git a/internal/casino/baccarat.go b/internal/casino/baccarat.go
index 3140fa8..f1ba951 100644
--- a/internal/casino/baccarat.go
+++ b/internal/casino/baccarat.go
@@ -120,7 +120,7 @@ func (t *baccaratTable) menuEvent(name string) Event {
}
commands = append(commands,
MenuCommand{Command: fmt.Sprintf("bet %d banker", t.Config.MinBet)},
- MenuCommand{Command: "bet <n> on <bet>"},
+ MenuCommand{Command: "bet N on BET"},
MenuCommand{Command: "stop", Desc: "Leave; during a round, leave afterward"},
MenuCommand{Command: "table", Desc: "Check the table"})
commands = withTableHints(commands, t.wagerRows())
@@ -182,7 +182,7 @@ func (t *baccaratTable) bet(name, spot string, amount int, wallet Wallet) []Even
spot = p.mainSpot
}
if spot == "" {
- return []Event{t.private(name, "Bet on what? Try bet <n> on "+t.Rules.betChoices()+".")}
+ return []Event{t.private(name, "Bet on what? Try bet N on "+t.Rules.betChoices()+".")}
}
if !t.Rules.allowsSpot(spot) {
return []Event{t.private(name, "Baccarat bets must be on player, banker, or tie, or an enabled optional bet. Available: "+t.Rules.betChoices()+".")}
@@ -262,7 +262,7 @@ func (t *baccaratTable) rebetAll(name string, wallet Wallet) []Event {
return []Event{t.private(name, "You have already bet a main wager this round.")}
}
if !p.hasLastBet() {
- return []Event{t.private(name, "Bet on what? Try bet <n> on "+t.Rules.betChoices()+".")}
+ return []Event{t.private(name, "Bet on what? Try bet N on "+t.Rules.betChoices()+".")}
}
var events []Event
for _, spot := range t.Rules.spots() {
diff --git a/internal/casino/blackjack.go b/internal/casino/blackjack.go
index eee5023..5656129 100644
--- a/internal/casino/blackjack.go
+++ b/internal/casino/blackjack.go
@@ -118,7 +118,7 @@ func (t *blackjackTable) menuEvent(name string) Event {
}
e := t.betMenuEvent(name, []MenuCommand{
{Command: "bet", Desc: fmt.Sprintf("%s: %d credits (also: rebet)", desc, amount)},
- {Command: "bet <amount>", Desc: "Change wager; min/max accepted"},
+ {Command: "bet N", Desc: "Change wager; min/max accepted"},
{Command: "stop", Desc: "Leave; during a round, leave afterward"},
{Command: "table", Desc: "Check the table"},
})
diff --git a/internal/casino/blackjack_wagers.go b/internal/casino/blackjack_wagers.go
index 23afac8..211962f 100644
--- a/internal/casino/blackjack_wagers.go
+++ b/internal/casino/blackjack_wagers.go
@@ -2,7 +2,7 @@ package casino
import "fmt"
-// Blackjack's only standing wager is bet <amount>, so outside the hand's
+// Blackjack's only standing wager is bet N, so outside the hand's
// decision points its wager list is empty (which also suppresses the "list"
// hint). During the insurance window and the player's own turn, the extra
// wagers and wagers-that-return-money get listed: insurance, decline,
diff --git a/internal/casino/craps.go b/internal/casino/craps.go
index 6fa6c79..73c67c4 100644
--- a/internal/casino/craps.go
+++ b/internal/casino/craps.go
@@ -209,7 +209,7 @@ func (t *crapsTable) bet(name, spot string, amount int, w Wallet) []Event {
spot = p.lastSpot
}
if spot == "" && t.Config.Craps.variant() == "simplified" {
- // Pass is the only target, so "bet <amount>" needs no target at all.
+ // Pass is the only target, so "bet N" needs no target at all.
spot = "pass"
}
spot = strings.ToLower(strings.TrimSpace(spot))
diff --git a/internal/casino/craps_rules.go b/internal/casino/craps_rules.go
index 60a7c0f..1c5e998 100644
--- a/internal/casino/craps_rules.go
+++ b/internal/casino/craps_rules.go
@@ -119,11 +119,11 @@ func (c TableConfig) CrapsRulesText() string {
odds = fmt.Sprintf("%dx", r.Odds)
}
return prefix + fmt.Sprintf("Bank Craps | Free odds %s | Field 12 pays %d:1\n"+
- "bet <n> on pass / dontpass / come / dontcome / field\n"+
- "bet <n> on place 6 / buy 4 / lay 10 / hard 8 / hop 2 3\n"+
- "bet <n> on seven / craps / eleven / ace-deuce / aces / boxcars / horn\n"+
- "bet <n> on odds pass / odds come 6 (also dontpass/dontcome)\n"+
- "bet <n> on fire / small / tall / all (before shooter's first roll only)\n"+
+ "bet N on pass / dontpass / come / dontcome / field\n"+
+ "bet N on place 6 / buy 4 / lay 10 / hard 8 / hop 2 3\n"+
+ "bet N on seven / craps / eleven / ace-deuce / aces / boxcars / horn\n"+
+ "bet N on odds pass / odds come 6 (also dontpass/dontcome)\n"+
+ "bet N on fire / small / tall / all (before shooter's first roll only)\n"+
"Pass/come 1:1; don't bars 12. Place 4/10 9:5, 5/9 7:5, 6/8 7:6.\n"+
"Buy/lay true odds; 5%% commission on buy STAKE / lay WIN, rounded down, wins only.\n"+
"Field 2 pays 2:1; 3/4/9/10/11 pay 1:1. Hard 4/10 7:1; 6/8 9:1.\n"+
diff --git a/internal/casino/craps_ux.go b/internal/casino/craps_ux.go
index 0a7932d..e64feb3 100644
--- a/internal/casino/craps_ux.go
+++ b/internal/casino/craps_ux.go
@@ -17,16 +17,16 @@ func (t *crapsTable) commands(name string) []MenuCommand {
if p.lastBet > 0 {
commands = append(commands,
MenuCommand{Command: "bet", Desc: fmt.Sprintf("Rebet %d on pass (also: rebet)", p.lastBet)},
- MenuCommand{Command: "bet <amount>", Desc: "New amount on pass; the dice roll 3 ticks after a bet"})
+ MenuCommand{Command: "bet N", Desc: "New amount on pass; the dice roll 3 ticks after a bet"})
} else {
- commands = append(commands, MenuCommand{Command: "bet <amount>", Desc: "Wager on pass; the dice roll 3 ticks after a bet"})
+ commands = append(commands, MenuCommand{Command: "bet N", Desc: "Wager on pass; the dice roll 3 ticks after a bet"})
}
} else {
- target := "<bet>"
+ target := "BET"
if !t.Config.Craps.allows("buy") {
target = "pass"
}
- commands = append(commands, MenuCommand{Command: "bet <n> on " + target, Desc: "Place a wager"})
+ commands = append(commands, MenuCommand{Command: "bet N on " + target, Desc: "Place a wager"})
}
}
if p.HasBet {
diff --git a/internal/casino/craps_variants.go b/internal/casino/craps_variants.go
index bcb06d0..634af36 100644
--- a/internal/casino/craps_variants.go
+++ b/internal/casino/craps_variants.go
@@ -213,17 +213,17 @@ func (c CrapsConfig) variantRulesText() string {
rules += "\nTrue odds on 4/10: 2:1; 5/9: 3:2; 6/8: 6:5. Lay and don't odds invert these ratios. Buy/lay commission: 5% buy STAKE / lay PROFIT, rounded down, on wins only; removals have no commission."
}
if c.variant() == "crapless" {
- rules += fmt.Sprintf("\nPass/come pay 1:1 on their point before 7; 7 loses. Line bets only on come-out; come requires a table point.\nTargets: pass / come / odds pass / odds come <number> / place <number> / buy <number> / lay <number>.\nPlace 4/10 pays 9:5; 5/9 7:5; 6/8 7:6.\nfield: 2 pays 2:1, 12 pays %d:1, 3/4/9/10/11 pay 1:1; others lose.\nhard 4/10 pays 7:1; hard 6/8 pays 9:1; easy number or 7 loses.\nseven 4:1; craps 7:1; eleven / ace-deuce 15:1; aces / boxcars 30:1.\nhorn splits equally among 2/3/11/12; hop <a> <b> pays 15:1 easy / 30:1 double, unordered.\nPlace/buy/hardways and come odds are off on come-out; established come contracts and lay always work.\nNo Fire or ATS. Making a point retains the shooter; seven-out rotates.", c.Field12)
+ rules += fmt.Sprintf("\nPass/come pay 1:1 on their point before 7; 7 loses. Line bets only on come-out; come requires a table point.\nTargets: pass / come / odds pass / odds come NUMBER / place NUMBER / buy NUMBER / lay NUMBER.\nPlace 4/10 pays 9:5; 5/9 7:5; 6/8 7:6.\nfield: 2 pays 2:1, 12 pays %d:1, 3/4/9/10/11 pay 1:1; others lose.\nhard 4/10 pays 7:1; hard 6/8 pays 9:1; easy number or 7 loses.\nseven 4:1; craps 7:1; eleven / ace-deuce 15:1; aces / boxcars 30:1.\nhorn splits equally among 2/3/11/12; hop A B pays 15:1 easy / 30:1 double, unordered.\nPlace/buy/hardways and come odds are off on come-out; established come contracts and lay always work.\nNo Fire or ATS. Making a point retains the shooter; seven-out rotates.", c.Field12)
}
if c.variant() == "new_york" {
rules += "\nPass come-out 7/11 wins, 2/3/12 loses; 4/5/6/8/9/10 establish a point. Make it before 7 to win, all 1:1. Don't pass is opposite, except come-out 12 pushes. Line bets only on come-out. Making a point retains the shooter; seven-out rotates."
}
cadence := "Solo roll anytime; multiplayer waits 10 ticks, then dealer rolls after 60 ticks."
autoroll := "Bets stay open until the roll. autoroll repeats every 3 ticks, respecting the betting window."
- betLine := "Bet: bet <n> on pass; the felt is authoritative for this edition."
+ betLine := "Bet: bet N on pass; the felt is authoritative for this edition."
stopLine := "stop cancels autoroll; manual stop waits for live bets."
if c.variant() == "simplified" {
- betLine = "Bet: bet <amount> — always on pass; bare bet rebets your last wager."
+ betLine = "Bet: bet N — always on pass; bare bet rebets your last wager."
cadence = "The dice roll automatically 3 ticks after the first bet."
autoroll = ""
stopLine = "stop waits for live bets."
diff --git a/internal/casino/craps_wagers.go b/internal/casino/craps_wagers.go
index a36f43d..beb8722 100644
--- a/internal/casino/craps_wagers.go
+++ b/internal/casino/craps_wagers.go
@@ -80,8 +80,8 @@ func (t *crapsTable) wagerRows(name string) []WagerRow {
// win condition and the 5% vig applies only to the winning wager.
func crapsBuyLayRows() []WagerRow {
return []WagerRow{
- {"buy <n>", "true", "This number rolled before 7; 5% commission on wins"},
- {"lay <n>", "true", "7 rolled before this number; 5% commission on wins"},
+ {"buy N", "true", "This number rolled before 7; 5% commission on wins"},
+ {"lay N", "true", "7 rolled before this number; 5% commission on wins"},
}
}
@@ -95,7 +95,7 @@ func crapsPropRows(c CrapsConfig, bonuses bool) []WagerRow {
{"eleven / ace-deuce", "15:1", "Next roll totals 11 / 3"},
{"aces / boxcars", "30:1", "Next roll is 2 / 12"},
{"horn", "27:4", "Splits the stake over 2/3/11/12"},
- {"hop <a> <b>", "15:1/30:1", "Next roll is a/b"},
+ {"hop A B", "15:1/30:1", "Next roll is a/b"},
}
if bonuses {
rows = append(rows,
@@ -149,6 +149,6 @@ func crapsPlaceRows(c CrapsConfig) []WagerRow {
func crapsOddsRows() []WagerRow {
return []WagerRow{
{"odds pass / come", "true", "Free odds behind the point"},
- {"odds dont <n>", "true", "Laid against the point at inverse odds"},
+ {"odds dont N", "true", "Laid against the point at inverse odds"},
}
}
diff --git a/internal/casino/house_edge_craps.go b/internal/casino/house_edge_craps.go
index 6b86571..d634aa6 100644
--- a/internal/casino/house_edge_craps.go
+++ b/internal/casino/house_edge_craps.go
@@ -10,9 +10,9 @@ func crapsHouseEdge(cfg TableConfig, bet string) HouseEdge {
switch bet {
case "pass", "come", "dontpass", "dontcome":
return edge(-crapsContractEV(c, bet, 0))
- case "odds pass", "odds pass / come", "odds come <n>", "odds dontpass", "odds dont <n>", "odds dontcome <n>":
+ case "odds pass", "odds pass / come", "odds come N", "odds dontpass", "odds dont N", "odds dontcome N":
return edge(0)
- case "buy <n>", "lay <n>":
+ case "buy N", "lay N":
e := HouseEdge{Note: "5% commission on wins only; bet multiples keep the vig whole"}
for _, n := range []int{2, 3, 4, 5, 6, 8, 9, 10, 11, 12} {
if !c.number(n) {
@@ -33,7 +33,7 @@ func crapsHouseEdge(cfg TableConfig, bet string) HouseEdge {
return edge(-crapsResolvedEV(c, "eleven", 0))
case "aces / boxcars":
return edge(-crapsResolvedEV(c, "aces", 0))
- case "hop <a> <b>":
+ case "hop A B":
return HouseEdge{Parts: []EdgePart{{"", -crapsResolvedEV(c, "hop", 12)}, {"(doubles)", -crapsResolvedEV(c, "hop", 11)}}}
case "small / tall":
return edge(1 - 35*crapsCollectProbability([]int{2, 3, 4, 5, 6}))
diff --git a/internal/casino/house_edge_test.go b/internal/casino/house_edge_test.go
index 0704edc..bb9c924 100644
--- a/internal/casino/house_edge_test.go
+++ b/internal/casino/house_edge_test.go
@@ -17,9 +17,9 @@ func TestKnownHouseEdges(t *testing.T) {
bet string
want float64 // expected loss per unit stake (unrounded fraction)
}{
- {"roulette straight", TableConfig{Game: GameRoulette}, "<number>", 1.0 / 37},
+ {"roulette straight", TableConfig{Game: GameRoulette}, "NUMBER", 1.0 / 37},
{"roulette even money", TableConfig{Game: GameRoulette}, "red / black", 1.0 / 37},
- {"roulette DZ straight", TableConfig{Game: GameRoulette, Roulette: RouletteConfig{DoubleZero: true}}, "<number>", 2.0 / 38},
+ {"roulette DZ straight", TableConfig{Game: GameRoulette, Roulette: RouletteConfig{DoubleZero: true}}, "NUMBER", 2.0 / 38},
{"roulette DZ basket", TableConfig{Game: GameRoulette, Roulette: RouletteConfig{DoubleZero: true}}, "basket", 3.0 / 38},
{"craps pass", TableConfig{Game: GameCraps}, "pass", 0.014141414141414},
{"craps dontpass", TableConfig{Game: GameCraps}, "dontpass", 0.013636363636364},
@@ -108,14 +108,14 @@ func TestUnrecognizedTargetNeverInherits(t *testing.T) {
}
}
-// TestCrapsGroupedTargetsHaveRTP: the list names grouped rows (4/10, buy <n>,
+// TestCrapsGroupedTargetsHaveRTP: the list names grouped rows (4/10, buy N,
// odds pass / come). Each must resolve to a real edge, not the unknown dash.
func TestCrapsGroupedTargetsHaveRTP(t *testing.T) {
for _, bet := range []string{
"place 4/10", "place 5/9", "place 6/8",
"hard 4/10", "hard 6/8",
- "odds pass / come", "odds dont <n>",
- "buy <n>", "lay <n>",
+ "odds pass / come", "odds dont N",
+ "buy N", "lay N",
} {
got := (TableConfig{Game: GameCraps}).HouseEdge(bet)
if len(got.Parts) == 0 {
@@ -132,20 +132,20 @@ func TestCrapsGroupedTargetsHaveRTP(t *testing.T) {
// The <n> rows expand to every legal number, including the crapless extras.
for _, want := range []string{"2", "3", "11", "12"} {
found := false
- for _, part := range crapless.HouseEdge("lay <n>").Parts {
+ for _, part := range crapless.HouseEdge("lay N").Parts {
if part.Label == want {
found = true
}
}
if !found {
- t.Fatalf("crapless lay <n> missing number %s", want)
+ t.Fatalf("crapless lay N missing number %s", want)
}
}
}
// TestHopBetsTwoParts pins the split hop analysis (easy vs doubles).
func TestHopBetsTwoParts(t *testing.T) {
- got := (TableConfig{Game: GameCraps}).HouseEdge("hop <a> <b>")
+ got := (TableConfig{Game: GameCraps}).HouseEdge("hop A B")
if len(got.Parts) != 2 {
t.Fatalf("hop analysis: %s", got.Text(2))
}
diff --git a/internal/casino/machine.go b/internal/casino/machine.go
index 610e745..0733384 100644
--- a/internal/casino/machine.go
+++ b/internal/casino/machine.go
@@ -103,7 +103,7 @@ func (m *Machine) start(name string, wallet Wallet) []Event {
return m.private("You're in the middle of a spin!")
}
if m.bet <= 0 {
- return m.private("Set a wager first with 'bet <amount>'.")
+ return m.private("Set a wager first with 'bet N'.")
}
return m.beginSpinLocked(m.bet, wallet,
fmt.Sprintf("%s pulls the lever on the slots machine.", name),
@@ -139,7 +139,7 @@ func (m *Machine) enableAutoBet(name string, wallet Wallet) []Event {
return m.private("You're in the middle of a spin!")
}
if m.bet <= 0 {
- return m.private("Set a wager first with 'bet <amount>'.")
+ return m.private("Set a wager first with 'bet N'.")
}
m.player.Wallet = wallet
m.autoBet = true
@@ -522,12 +522,19 @@ func formatWins(wins []SlotWin, ways int) string {
return message[:len(message)-1]
}
+// Hints is the machine's live command list for the "what" command.
+func (m *Machine) Hints() []MenuCommand {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ return m.commands()
+}
+
func (m *Machine) commands() []MenuCommand {
if m.autoBet {
return []MenuCommand{{Command: "stop", Desc: "Stop automatic betting"}}
}
commands := []MenuCommand{
- {Command: "bet <amount>", Desc: "Set your wager and spin"},
+ {Command: "bet N", Desc: "Set your wager and spin"},
{Command: "bet max", Desc: "Bet the maximum and spin"},
}
if m.bet > 0 {
diff --git a/internal/casino/manager.go b/internal/casino/manager.go
index 8a33df6..bdd9249 100644
--- a/internal/casino/manager.go
+++ b/internal/casino/manager.go
@@ -205,7 +205,7 @@ func (m *Manager) JoinMachine(roomID int, cfg MachineConfig, name string) []Even
if machine == nil {
return []Event{{RoomID: roomID, TableID: cfg.ID, Type: EventResult, PrivateTo: name, Message: "That game is not available here."}}
}
- return machine.join(name)
+ return markHints(machine.join(name))
}
func (m *Manager) SetMachineBet(roomID int, machineID, name string, amount int, wallet Wallet) []Event {
@@ -247,7 +247,21 @@ func (m *Manager) Join(roomID int, cfg TableConfig, name string) []Event {
if table == nil {
return []Event{{RoomID: roomID, TableID: cfg.ID, Type: EventResult, PrivateTo: name, Message: "That game is not available here."}}
}
- return table.join(name)
+ return markHints(table.join(name))
+}
+
+// markHints tags the first menu in a join/status result so the presentation
+// layer always shows its command hints under the "casino_hints once" default.
+// Recurring menus (after a bet or action) leave the flag clear, and other
+// events (seat notices, rule felts) are untouched.
+func markHints(events []Event) []Event {
+ for i := range events {
+ if events[i].Menu != nil {
+ events[i].Menu.ShowHints = true
+ break
+ }
+ }
+ return events
}
func (m *Manager) Leave(roomID int, tableID, name string) []Event {
diff --git a/internal/casino/pai_gow.go b/internal/casino/pai_gow.go
index 0de79e3..a2dafcf 100644
--- a/internal/casino/pai_gow.go
+++ b/internal/casino/pai_gow.go
@@ -148,12 +148,12 @@ func (t *paiGowTable) hintsLocked(name string) []MenuCommand {
// Side bets are placed before the main wager: the main bet is what
// deals, so there is no later chance to add them.
if p.fortune.Total == 0 {
- commands = append(commands, MenuCommand{Command: "bet <amount> fortune", Desc: "Optional Fortune side bet; place before your main bet"})
+ commands = append(commands, MenuCommand{Command: "bet N fortune", Desc: "Optional Fortune side bet; place before your main bet"})
}
if faceUp && p.pushace.Total == 0 {
- commands = append(commands, MenuCommand{Command: "bet <amount> pushace", Desc: "Optional Push Ace High side bet; place before your main bet"})
+ commands = append(commands, MenuCommand{Command: "bet N pushace", Desc: "Optional Push Ace High side bet; place before your main bet"})
}
- commands = append(commands, MenuCommand{Command: "bet <amount>", Desc: "Main wager; deals once every bettor is in"})
+ commands = append(commands, MenuCommand{Command: "bet N", Desc: "Main wager; deals once every bettor is in"})
}
if p.main.Total > 0 || p.fortune.Total > 0 || p.pushace.Total > 0 {
commands = append(commands, MenuCommand{Command: "unbet", Desc: "Take back all your wagers before the deal"})
diff --git a/internal/casino/pai_gow_rules.go b/internal/casino/pai_gow_rules.go
index 0539c9f..494cde7 100644
--- a/internal/casino/pai_gow_rules.go
+++ b/internal/casino/pai_gow_rules.go
@@ -110,7 +110,7 @@ func paiGowRules(c TableConfig) string {
case PaiGowNoPush:
s += " No commission; split results use two extra cards: player must beat dealer, ties lose; Joker is ace."
}
- s += "\nHouse way: preserve the best high-hand category, maximize low within it; split two pair, full houses and trips-pairs when this improves low to a pair. Never foul. house arranges the house way and still needs set; autoset on locks any legal arrangement immediately (autoset off restores the confirmation). A multiplayer timeout or disconnect uses the house way; a lone player is never timed out.\nBet main first, optionally bet <amount> fortune before dealing; unbet takes every undealt wager back. Fortune uses all seven cards regardless of split; highest listed prize only, profit odds plus stake returned."
+ s += "\nHouse way: preserve the best high-hand category, maximize low within it; split two pair, full houses and trips-pairs when this improves low to a pair. Never foul. house arranges the house way and still needs set; autoset on locks any legal arrangement immediately (autoset off restores the confirmation). A multiplayer timeout or disconnect uses the house way; a lone player is never timed out.\nBet main first, optionally bet N fortune before dealing; unbet takes every undealt wager back. Fortune uses all seven cards regardless of split; highest listed prize only, profit odds plus stake returned."
if r.Envy {
s += "\nEnvy: Fortune stake of at least 5 qualifies for fixed bonuses on OTHER active players' hands, never your own or dealer; multiple bonuses add."
} else {
diff --git a/internal/casino/roulette.go b/internal/casino/roulette.go
index 8f45d49..ad0a706 100644
--- a/internal/casino/roulette.go
+++ b/internal/casino/roulette.go
@@ -518,7 +518,7 @@ func (t *rouletteTable) menuEvent(name string, board bool) Event {
commands := []MenuCommand{}
p := t.players.get(name)
if t.phase != phasePlaying && p != nil && !p.HasBet {
- commands = append(commands, MenuCommand{Command: "bet <n> on <bet>", Desc: "Place one wager"})
+ commands = append(commands, MenuCommand{Command: "bet N on BET", Desc: "Place one wager"})
if p.lastBet > 0 {
commands = append(commands, MenuCommand{Command: "bet / rebet", Desc: "Repeat your previous wager"})
}
diff --git a/internal/casino/roulette_wagers.go b/internal/casino/roulette_wagers.go
index 7562582..5879f03 100644
--- a/internal/casino/roulette_wagers.go
+++ b/internal/casino/roulette_wagers.go
@@ -13,17 +13,17 @@ func (t *rouletteTable) wagerRows() []WagerRow {
}
chance := func(n int) string { return fmt.Sprintf("%d/%d", n, pockets) }
rows := []WagerRow{
- {"<number>", "35:1", "One pocket (" + chance(1) + ")"},
+ {"NUMBER", "35:1", "One pocket (" + chance(1) + ")"},
{"red / black", "1:1", "The color (" + chance(18) + ")"},
{"odd / even", "1:1", "The parity (" + chance(18) + ")"},
{"low / high", "1:1", "Low 1-18 / high 19-36 (" + chance(18) + ")"},
{"1to12 / 13to24 / 25to36", "2:1", "A dozen (" + chance(12) + ")"},
{"row1 / row2 / row3", "2:1", "A numbered row (" + chance(12) + ")"},
- {"split <a> <b>", "17:1", "Two touching pockets (" + chance(2) + ")"},
- {"street <a> <b> <c>", "11:1", "Three numbers in a street (" + chance(3) + ")"},
- {"trio 0 <a> <b>", "11:1", "A zero-edge trio (" + chance(3) + ")"},
- {"corner <a> <b> <c> <d>", "8:1", "A 2x2 square (" + chance(4) + ")"},
- {"line <a> … <f>", "5:1", "Two adjoining streets (" + chance(6) + ")"},
+ {"split A B", "17:1", "Two touching pockets (" + chance(2) + ")"},
+ {"street A B C", "11:1", "Three numbers in a street (" + chance(3) + ")"},
+ {"trio 0 A B", "11:1", "A zero-edge trio (" + chance(3) + ")"},
+ {"corner A B C D", "8:1", "A 2x2 square (" + chance(4) + ")"},
+ {"line A … F", "5:1", "Two adjoining streets (" + chance(6) + ")"},
{"basket", basketOdds, "0, 1, 2, 3 (+ 00) (" + chance(basketPockets) + ")"},
}
if t.Config.Roulette.Racetrack && !t.Config.Roulette.DoubleZero {
@@ -32,7 +32,7 @@ func (t *rouletteTable) wagerRows() []WagerRow {
WagerRow{"tiers", "2:1", "The 12 pockets opposite zero, 6 chips"},
WagerRow{"orphelins", "31:5 or 13:5", "The two orphan arcs, 5 chips"},
WagerRow{"zero / jeu zero", "8:1 or 7:2", "The 7 pockets nearest zero, 4 chips"},
- WagerRow{"neighbors <n>", "3:1", "Pocket n plus four numbers each side, 9 chips"},
+ WagerRow{"neighbors N", "3:1", "Pocket n plus four numbers each side, 9 chips"},
)
}
return rows
diff --git a/internal/casino/table.go b/internal/casino/table.go
index d93d91e..d9d3525 100644
--- a/internal/casino/table.go
+++ b/internal/casino/table.go
@@ -127,8 +127,9 @@ func (t *Table) refundAll() []Event { return t.game.refundAll() }
// idle on the Table wrapper delegates to the game.
func (t *Table) idle() bool { return t.game.idle() }
-// Status is a read-only view: it never restarts a deadline or changes a wager.
-func (t *Table) Status(name string) []Event { return t.game.status(name) }
+// Status is a read-only view: it never restarts a deadline or changes a
+// wager. It is an explicit look/watch, so its menu always carries hints.
+func (t *Table) Status(name string) []Event { return markHints(t.game.status(name)) }
// Board returns the game's table-only board for the table command: the live
// wheel, dice layout or paytable, with no balance, command menu, or state
@@ -165,7 +166,7 @@ func (t *Table) RulesText() string { return rulesTextOnTable(t.Config) }
func (t *Table) HelpTopic() string { return t.Config.HelpTopic() }
// Wagers is the game's live list of bets the player could make right now.
-// Empty when nothing is worth naming (a lone "bet <amount>" wager, a
+// Empty when nothing is worth naming (a lone "bet N" wager, a
// committed round) — the same signal that suppresses the "list" hint.
func (t *Table) Wagers(name string) []WagerRow { return t.game.wagers(name) }
diff --git a/internal/casino/table_hints.go b/internal/casino/table_hints.go
index 98eaced..eafb05e 100644
--- a/internal/casino/table_hints.go
+++ b/internal/casino/table_hints.go
@@ -19,7 +19,7 @@ func (t *rouletteTable) hints(name string) []MenuCommand {
}
commands := []MenuCommand{}
if t.phase != phasePlaying && !p.HasBet {
- commands = append(commands, MenuCommand{Command: "bet <n> on <bet>", Desc: "Place one wager"})
+ commands = append(commands, MenuCommand{Command: "bet N on BET", Desc: "Place one wager"})
if p.lastBet > 0 {
commands = append(commands, MenuCommand{Command: "bet / rebet", Desc: "Repeat your previous wager"})
}
@@ -82,7 +82,7 @@ func baccaratHintCommands(t *baccaratTable, name string) []MenuCommand {
}
return append(commands,
MenuCommand{Command: fmt.Sprintf("bet %d banker", t.Config.MinBet)},
- MenuCommand{Command: "bet <n> on <bet>"},
+ MenuCommand{Command: "bet N on BET"},
MenuCommand{Command: "stop", Desc: "Leave; during a round, leave afterward"},
MenuCommand{Command: "table", Desc: "Check the table"},
)
diff --git a/internal/casino/table_ux.go b/internal/casino/table_ux.go
index b570b36..5d70aaf 100644
--- a/internal/casino/table_ux.go
+++ b/internal/casino/table_ux.go
@@ -6,7 +6,7 @@ import (
)
// WagerRow is one line of a table's "list" of available bets, in the game's
-// current state: the bet wording (what follows "bet <n> on"), its
+// current state: the bet wording (what follows "bet N on"), its
// payout, and a brief description of the win condition.
type WagerRow struct {
Bet string
@@ -21,7 +21,7 @@ const (
// withTableHints finishes any game's hint list: "rules" is always present
// while seated at a table, and "list" follows when the game named more than
-// one wager a player might want to make (a lone bet is just "bet <amount>").
+// one wager a player might want to make (a lone bet is just "bet N").
// Games whose own menus already append these hints are not double-hinted.
func withTableHints(commands []MenuCommand, wagerRows []WagerRow) []MenuCommand {
for _, c := range commands {
diff --git a/internal/casino/table_ux_test.go b/internal/casino/table_ux_test.go
index 4d58a5f..f81d537 100644
--- a/internal/casino/table_ux_test.go
+++ b/internal/casino/table_ux_test.go
@@ -218,7 +218,7 @@ func TestRebetMenusAndMidRoundJoin(t *testing.T) {
bac := newBaccaratTestTable(TableConfig{ID: "bac", Game: GameBaccarat})
bac.join("alice")
first := bac.menuEvent("alice")
- if first.Menu.Commands[0].Command == "bet <amount>" {
+ if first.Menu.Commands[0].Command == "bet N" {
t.Fatal("first baccarat bet omitted target")
}
bac.bet("alice", "banker", 25, &testWallet{credits: 100})
diff --git a/internal/casino/types.go b/internal/casino/types.go
index 5f9de1d..570d0ac 100644
--- a/internal/casino/types.go
+++ b/internal/casino/types.go
@@ -256,6 +256,9 @@ type MenuView struct {
ShowBalance bool // prepend the player's chips/credits balance
Commands []MenuCommand
Repeat bool // returning bettor: compact clients can show a short reminder
+ // ShowHints marks a menu that always carries its command list under the
+ // "casino_hints once" option: the join menu and an explicit look/status.
+ ShowHints bool
}
// MenuCommand is one selectable command shown in a casino menu: the typed
diff --git a/internal/casino/video_poker.go b/internal/casino/video_poker.go
index d2578b8..4cb9883 100644
--- a/internal/casino/video_poker.go
+++ b/internal/casino/video_poker.go
@@ -147,7 +147,7 @@ func (t *videoPokerTable) hintsLocked(name string) []MenuCommand {
if t.players[name] == nil {
return nil
}
- commands := []MenuCommand{{Command: "bet [1-4|max]", Desc: "Load coins (up to five per hand)"}, {Command: "deal", Desc: "Pay and deal the loaded wager"}}
+ commands := []MenuCommand{{Command: "bet [1-4|MAX]", Desc: "Load coins (up to five per hand)"}, {Command: "deal", Desc: "Pay and deal the loaded wager"}}
if p := t.players[name]; p != nil && p.active {
commands = []MenuCommand{{Command: "1 3 5", Desc: "Toggle held cards (531 also works)"}, {Command: "draw", Desc: "Replace unheld cards on every line"}}
if p.drawn {
@@ -155,9 +155,9 @@ func (t *videoPokerTable) hintsLocked(name string) []MenuCommand {
}
} else {
commands = append(commands,
- MenuCommand{Command: "game <name>", Desc: t.settings.GameChoices()},
- MenuCommand{Command: "denom <1|10|50>", Desc: "Credits per coin"},
- MenuCommand{Command: "hands <count>", Desc: "Supported hand counts are announced by hands"},
+ MenuCommand{Command: "game NAME", Desc: t.settings.GameChoices()},
+ MenuCommand{Command: "denom 1|10|50", Desc: "Credits per coin"},
+ MenuCommand{Command: "hands COUNT", Desc: "Supported hand counts are announced by hands"},
)
if cabinetFeature(t.settings.Cabinet) != "" {
commands = append(commands, MenuCommand{Command: "feature on|off", Desc: "Toggle the extra-cost bonus"})
@@ -298,7 +298,7 @@ func (t *videoPokerTable) bet(name, spot string, amount int, w Wallet) []Event {
return []Event{t.private(name, "Finish this hand with draw first.")}
}
if p.loaded == 0 {
- return []Event{t.private(name, "Load coins first: bet [1-4|max].")}
+ return []Event{t.private(name, "Load coins first: bet [1-4|MAX].")}
}
if p.featureOn && p.loaded != 5 {
return []Event{t.private(name, "The feature requires five wager coins per hand. Use bet max or feature off.")}
@@ -406,7 +406,7 @@ func (t *videoPokerTable) action(name, action string) []Event {
default:
add, err := strconv.Atoi(fields[1])
if err != nil || add < 1 || add > 4 {
- return []Event{t.private(name, "Use bet [1-4|max].")}
+ return []Event{t.private(name, "Use bet [1-4|MAX].")}
}
n = min(5, p.loaded+add)
}
@@ -462,7 +462,7 @@ func (t *videoPokerTable) action(name, action string) []Event {
p.clearResult()
return t.statusLocked(name, false)
}
- return []Event{t.private(name, "Use bet [1-4|max], unbet, deal, game <name>, denom 1|10|50, hands <count>, paytable, or feature on|off on a bonus cabinet.")}
+ return []Event{t.private(name, "Use bet [1-4|MAX], unbet, deal, game NAME, denom 1|10|50, hands COUNT, paytable, or feature on|off on a bonus cabinet.")}
}
func (t *videoPokerTable) drawLocked(name string, p *pokerPlayer) []Event {