package game import ( "strings" "testing" "thehouseoficarus/internal/casino" "thehouseoficarus/internal/color" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" ) func TestRoulettePayoutTables(t *testing.T) { for _, mode := range []string{"none", "ansi", "xterm256"} { for _, unicode := range []bool{false, true} { for _, double := range []bool{false, true} { for _, width := range []int{60, 78} { g := &Game{} p := player.New("alice") p.Options = map[string]any{"color": mode, "unicode": unicode, "wrap_width": width} sess := &net.Session{Player: p} cfg := casino.TableConfig{Game: casino.GameRoulette, Roulette: casino.RouletteConfig{DoubleZero: double, Racetrack: !double}} art := g.rouletteReminder(sess, cfg) plain := stripANSI(art) for _, want := range []string{"Roulette payouts", "Target / example", "Chance", "Profit", "35:1", "17:1", "11:1", "8:1", "5:1", "2:1", "1:1", "help roulette"} { if !strings.Contains(plain, want) { t.Fatalf("missing %q:\n%s", want, plain) } } if double { for _, want := range []string{"1/38", "5/38", "6:1", "0, 00"} { if !strings.Contains(plain, want) { t.Fatalf("missing %q", want) } } if strings.Contains(plain, "Racetrack payouts") { t.Fatal("American wheel has call bets") } } else { for _, want := range []string{"1/37", "4/37", "17/37", "12/37", "8/37", "7/37", "9/37", "Racetrack payouts", "5:3", "13:5", "31:5", "7:2", "zero / jeu zero"} { if !strings.Contains(plain, want) { t.Fatalf("missing %q:\n%s", want, plain) } } } if (mode == "none") == strings.Contains(art, "\x1b[") { t.Fatalf("wrong color mode %s", mode) } tableWidth := 0 for _, line := range strings.Split(art, "\n") { plainLine := stripANSI(line) if plainLine != "" && strings.ContainsRune("╔╟╚+", []rune(plainLine)[0]) { tableWidth = color.VisibleLen(line) } if plainLine != "" && strings.ContainsRune("╔║╟╚+|", []rune(plainLine)[0]) { if got := color.VisibleLen(line); got != tableWidth || got > width { t.Errorf("uneven table width %d (want %d, max %d): %q", got, tableWidth, width, plainLine) } } if !unicode { for _, r := range plainLine { if r > 127 { t.Errorf("non-ASCII %q", r) } } } } } } } } } func TestCasinoHelpTopics(t *testing.T) { g := &Game{Deps: Deps{DataDir: "../../data"}} conn := &casinoTestConn{} sess := &net.Session{Player: player.New("alice"), Conn: conn} for topic, want := range map[string]string{ "casino": "Profit odds", "play": "Casino quick guide", "blackjack": "3:2", "baccarat": "0.95:1", "slots": "243", "roulette": "35:1", "racetrack": "17/37", "zero": "Jeu Zero", } { conn.data = nil g.doHelp(sess, topic) if !strings.Contains(string(conn.data), want) { t.Errorf("help %s: %s", topic, conn.data) } } } func TestTableHelpTeachesCurrentGame(t *testing.T) { for _, game := range []casino.GameName{casino.GameBlackjack, casino.GameBaccarat, casino.GameRoulette} { g, sess, conn := uxCasinoGame(t) g.DataDir = "../../data" g.Casino.Join(1, casino.TableConfig{ID: "test", Game: game}, "alice") conn.data = nil g.executeTableStatus(sess, []string{"help"}, "table help") text := stripANSI(string(conn.data)) if strings.Contains(text, "No help") || !strings.Contains(text, "Table controls") || !strings.Contains(text, "pays") && !strings.Contains(text, "35:1") { t.Fatalf("help %s: %s", game, text) } if game != casino.GameBlackjack && strings.Contains(text, "insurance / decline") { t.Fatalf("blackjack-only controls shown for %s", game) } } g, sess, conn := uxCasinoGame(t) g.DataDir = "../../data" g.executeTableStatus(sess, []string{"help"}, "table help") if !strings.Contains(string(conn.data), "Casino quick guide") { t.Fatal("unseated players cannot learn the games") } } func TestTableHelpAtSlotMachine(t *testing.T) { g, sess, conn := uxCasinoGame(t) g.DataDir = "../../data" g.Casino.JoinMachine(1, casino.MachineConfig{ID: "slots", Game: casino.GameSlots}, "alice") g.executeTableStatus(sess, []string{"help"}, "table help") if !strings.Contains(string(conn.data), "Slots quick guide") { t.Fatal("machine player did not get the slots guide") } }