aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/command_hints_test.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-30 16:16:17 -0400
committerhistoria <[not public]>2026-09-30 16:16:17 -0400
commita4c09c2d38d1f3bc1d3ad4bdb0234bdea7c37e73 (patch)
tree00c9567e620f331a59dd44f1f1fa779ecd94a845 /internal/casino/command_hints_test.go
parent724079f8de1ae69965c6267dca3d7fc3cc86afa2 (diff)
downloadthehouseoficarus-a4c09c2d38d1f3bc1d3ad4bdb0234bdea7c37e73.tar.gz
feat: additional craps variants added, standardizard casino command hints
Diffstat (limited to 'internal/casino/command_hints_test.go')
-rw-r--r--internal/casino/command_hints_test.go155
1 files changed, 155 insertions, 0 deletions
diff --git a/internal/casino/command_hints_test.go b/internal/casino/command_hints_test.go
new file mode 100644
index 0000000..c570e15
--- /dev/null
+++ b/internal/casino/command_hints_test.go
@@ -0,0 +1,155 @@
+package casino
+
+import (
+ "strings"
+ "testing"
+ "time"
+)
+
+func hintHas(commands []MenuCommand, command string) bool {
+ for _, c := range commands {
+ if c.Command == command {
+ return true
+ }
+ }
+ return false
+}
+func hintBet(commands []MenuCommand) bool {
+ for _, c := range commands {
+ if strings.HasPrefix(c.Command, "bet") {
+ return true
+ }
+ }
+ return false
+}
+func TestCrapsHintsFollowPlayerState(t *testing.T) {
+ for _, v := range crapsVariants {
+ tb := variantFixture(v)
+ tb.join("a")
+ w := &testWallet{chips: 1000}
+ commands := tb.commands("a")
+ if !hintBet(commands) || !hintHas(commands, "stop") {
+ t.Fatal(v, "missing idle actions", commands)
+ }
+ for _, c := range []string{"roll", "autoroll", "unbet <target> / unbet all"} {
+ if hintHas(commands, c) {
+ t.Fatal(v, "idle", c)
+ }
+ }
+ tb.bet("a", "pass", 10, w)
+ commands = tb.commands("a")
+ if !hintHas(commands, "roll") || !hintHas(commands, "autoroll") || hintHas(commands, "unbet <target> / unbet all") {
+ t.Fatal(v, "contract actions", commands)
+ }
+ if !tb.Config.Craps.allows("buy") && hintBet(commands) {
+ t.Fatal(v, "duplicate pass advertised")
+ }
+ tb.action("a", "autoroll")
+ if hintHas(tb.commands("a"), "autoroll") {
+ t.Fatal(v, "already enabled")
+ }
+ tb.join("b")
+ if hintHas(tb.commands("a"), "roll") {
+ t.Fatal(v, "roll before betting window ends")
+ }
+ tb.players.get("a").autoRoll = false
+ tb.bet("b", "pass", 10, &testWallet{chips: 1000})
+ tb.tickElapsed(10 * time.Second)
+ if !hintHas(tb.commands("a"), "roll") || hintHas(tb.commands("b"), "roll") {
+ t.Fatal(v, "wrong shooter hints")
+ }
+ watch := tb.commands("watcher")
+ if len(watch) != 1 || watch[0].Command != "table" {
+ t.Fatal(v, "spectator actions", watch)
+ }
+ }
+}
+func TestCrapsHintsOnlyOfferRemovableWagers(t *testing.T) {
+ tb := crapsFixture()
+ tb.join("a")
+ w := &testWallet{chips: 1000}
+ tb.bet("a", "pass", 10, w)
+ tb.resolveRoll(2, 2)
+ if hintHas(tb.commands("a"), "unbet <target> / unbet all") {
+ t.Fatal("contract removable")
+ }
+ tb.bet("a", "odds pass", 10, w)
+ if !hintHas(tb.commands("a"), "unbet <target> / unbet all") {
+ t.Fatal("odds not removable")
+ }
+ tb.action("a", "unbet odds pass")
+ if hintHas(tb.commands("a"), "unbet <target> / unbet all") {
+ t.Fatal("removed odds still advertised")
+ }
+ tb.bet("a", "place 6", 12, w)
+ if !hintHas(tb.commands("a"), "unbet <target> / unbet all") {
+ t.Fatal("place removal missing")
+ }
+}
+func TestOtherCasinoHintsRespectCommitment(t *testing.T) {
+ bj := newBlackjackTable(1, TableConfig{ID: "bj", Game: GameBlackjack, BettingTicks: 10}, newTestRand())
+ bj.join("a")
+ bj.join("b")
+ bj.bet("a", "", 10, &testWallet{chips: 1000})
+ if hintBet(bj.menuEvent("a").Menu.Commands) {
+ t.Fatal("blackjack duplicate bet advertised")
+ }
+ if !hintBet(bj.menuEvent("b").Menu.Commands) {
+ t.Fatal("blackjack missing eligible bet")
+ }
+ bac := newBaccaratTable(1, TableConfig{ID: "bac", Game: GameBaccarat, BettingTicks: 10}, newTestRand())
+ bac.join("a")
+ bac.join("b")
+ bac.bet("a", "banker", 10, &testWallet{chips: 1000})
+ if hintBet(bac.menuEvent("a").Menu.Commands) {
+ t.Fatal("baccarat duplicate bet advertised")
+ }
+ if !hintBet(bac.menuEvent("b").Menu.Commands) {
+ t.Fatal("baccarat missing eligible bet")
+ }
+ r := newRouletteTable(1, TableConfig{ID: "r", Game: GameRoulette, BettingTicks: 10}, newTestRand())
+ r.join("a")
+ r.join("b")
+ if hintHas(r.menuEvent("a").Menu.Commands, "bet / rebet") {
+ t.Fatal("roulette no wager to repeat")
+ }
+ r.bet("a", "red", 10, &testWallet{chips: 1000})
+ if hintBet(r.menuEvent("a").Menu.Commands) {
+ t.Fatal("roulette duplicate bet advertised")
+ }
+ if !hintBet(r.menuEvent("b").Menu.Commands) {
+ t.Fatal("roulette missing eligible bet")
+ }
+ r.phase = phasePlaying
+ if hintBet(r.menuEvent("b").Menu.Commands) {
+ t.Fatal("roulette spinning bets advertised")
+ }
+ r.phase = phaseWaiting
+ r.players.get("a").HasBet = false
+ if !hintHas(r.menuEvent("a").Menu.Commands, "bet / rebet") {
+ t.Fatal("roulette repeat missing")
+ }
+}
+func TestSlotHintsRequirePriorWager(t *testing.T) {
+ m := newMachine(1, MachineConfig{ID: "slots", Game: GameSlots}, newTestRand())
+ m.join("a")
+ for _, c := range []string{"spin", "autospin"} {
+ if hintHas(m.commands(), c) {
+ t.Fatal("fresh slots", c)
+ }
+ }
+ m.bet = 10
+ for _, c := range []string{"spin", "autospin"} {
+ if !hintHas(m.commands(), c) {
+ t.Fatal("configured slots", c)
+ }
+ }
+ m.autoBet = true
+ if commands := m.commands(); len(commands) != 1 || commands[0].Command != "stop" {
+ t.Fatal("autospin hints", commands)
+ }
+ m.phase = machineSpinning
+ if events := m.privateMenu(""); events[0].Menu != nil {
+ t.Fatal("manual commands during spin")
+ }
+}