diff options
| author | historia <[not public]> | 2026-09-29 13:28:06 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-29 13:28:06 -0400 |
| commit | 9803facd4368420a5dce3af59c835e13cb015cf5 (patch) | |
| tree | 4ecb75f9c88b16ae72d3573bef026d760eb8d7a8 /internal/casino/roulette_test.go | |
| parent | 13db3e50cb6e29eedbf25416e5284bf08b0adefb (diff) | |
| download | thehouseoficarus-9803facd4368420a5dce3af59c835e13cb015cf5.tar.gz | |
feat: roulette in domita casino
Diffstat (limited to 'internal/casino/roulette_test.go')
| -rw-r--r-- | internal/casino/roulette_test.go | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/internal/casino/roulette_test.go b/internal/casino/roulette_test.go new file mode 100644 index 0000000..8defe1e --- /dev/null +++ b/internal/casino/roulette_test.go @@ -0,0 +1,171 @@ +package casino + +import ( + "math/rand" + "strconv" + "strings" + "testing" +) + +func TestRouletteRulesAndWheels(t *testing.T) { + for _, double := range []bool{false, true} { + wheel := rouletteWheel(double) + seen := map[string]bool{} + want := 37 + if double { + want = 38 + } + if len(wheel) != want { + t.Fatal("wheel size", len(wheel)) + } + for _, pocket := range wheel { + if seen[pocket] { + t.Fatal("duplicate", pocket) + } + seen[pocket] = true + } + for n := 0; n <= 36; n++ { + if !seen[strconv.Itoa(n)] { + t.Fatal("missing", n) + } + } + if seen["00"] != double { + t.Fatal("wrong double zero") + } + for _, spot := range strings.Fields("red black even odd low high dozen1 dozen2 dozen3 column1 column2 column3") { + pockets, mult := rouletteBet(spot, double) + if len(pockets)*mult != 36 { + t.Fatalf("%s coverage/payout %d/%d", spot, len(pockets), mult) + } + for _, p := range pockets { + if p == "0" || p == "00" { + t.Fatal("zero in outside bet") + } + } + } + pockets, mult := rouletteBet("00", double) + if double && (len(pockets) != 1 || mult != 36) || !double && mult != 0 { + t.Fatal("00 rules") + } + } + for _, spot := range []string{"-1", "37", "000", "01", "banana", ""} { + if _, m := rouletteBet(spot, true); m != 0 { + t.Fatal("accepted", spot) + } + } +} +func rouletteFixture() *rouletteTable { + return newRouletteTable(1, TableConfig{ID: "r", Game: GameRoulette, BettingTicks: 2, Roulette: RouletteConfig{SpinTicks: 2}}, rand.New(rand.NewSource(7))) +} +func TestRouletteTimedPhasesAndSettlement(t *testing.T) { + for _, double := range []bool{false, true} { + table := rouletteFixture() + table.Config.Roulette.DoubleZero = double + wheel := rouletteWheel(double) + index := rand.New(rand.NewSource(7)).Intn(len(wheel)) + table.join("a") + w := &testWallet{chips: 100} + table.bet("a", wheel[index], 10, w) + table.bet("a", "red", 10, w) + if w.chips != 90 || table.phase != phaseBetting { + t.Fatal("duplicate or early spin") + } + table.tick() + if table.phase != phaseBetting { + t.Fatal("betting ended early") + } + events := table.tick() + if table.phase != phasePlaying || len(events) == 0 { + t.Fatal("no spinning phase") + } + table.join("b") + other := &testWallet{chips: 100} + table.bet("b", "red", 10, other) + if other.chips != 100 { + t.Fatal("bet while spinning") + } + table.tick() + if w.paid != 0 { + t.Fatal("settled early") + } + table.action("a", "leave_after") + events = table.tick() + if w.paid != 360 || len(w.open) != 0 || table.hasParticipant("a") { + t.Fatalf("bad settlement: %+v", w) + } + s := events[0].Snapshot.(*RouletteSnapshot) + if s.Pockets != [3]string{wheel[(index+len(wheel)-1)%len(wheel)], wheel[index], wheel[(index+1)%len(wheel)]} { + t.Fatal("incorrect wheel neighbors") + } + table.tick() + table.refundAll() + if w.paid != 360 { + t.Fatal("double paid") + } + } +} +func TestRouletteRefundAndPersistenceFailure(t *testing.T) { + for _, spinning := range []bool{false, true} { + table := rouletteFixture() + table.join("a") + w := &testWallet{chips: 100} + table.bet("a", "red", 10, w) + if spinning { + table.tick() + table.tick() + } + table.markDisconnected("a") + table.refundAll() + table.tick() + if w.chips != 100 || len(w.open) != 0 || w.paid != 0 { + t.Fatalf("disconnect refund: %+v", w) + } + } + table := rouletteFixture() + table.join("a") + w := &failingPayoutWallet{testWallet: testWallet{chips: 100}} + table.bet("a", "red", 10, w) + for i := 0; i < 4; i++ { + table.tick() + } + if len(w.open) != 1 { + t.Fatal("failed settlement consumed stake") + } + table.refundAll() + table.refundAll() + if w.chips != 100 || len(w.open) != 0 { + t.Fatal("failed settlement not refunded once") + } +} +func TestRouletteViewsAndConfig(t *testing.T) { + for _, double := range []bool{false, true} { + s := &RouletteSnapshot{DoubleZero: double, Phase: "playing"} + wheel := s.ASCII() + if strings.Contains(wheel, "00") != double || strings.Count(wheel, "[ 0 ]") != 1 { + t.Fatal("wrong wheel zero pockets", wheel) + } + s.Phase = "betting" + layout := s.ASCII() + if !strings.Contains(layout, "column3") || !strings.Contains(layout, "36R") || strings.Contains(layout, "[00 G]") != double { + t.Fatal("bad layout", layout) + } + s.Phase = "result" + s.Pockets = [3]string{"26", "0", "32"} + if !strings.Contains(s.ASCII(), "| 26 | 0 | 32 |") { + t.Fatal(s.ASCII()) + } + } + cfg := TableConfig{ID: "r", Game: GameRoulette} + if err := cfg.Validate(); err != nil { + t.Fatal(err) + } + cfg.Roulette.SpinTicks = -1 + if cfg.Validate() == nil { + t.Fatal("invalid ticks accepted") + } + cfg.Roulette.SpinTicks = 2 + cfg.Game = GameBlackjack + if cfg.Validate() == nil { + t.Fatal("foreign rule block accepted") + } +} |
