From a5b9c1052c4640ba519e608e0c97e603ce6aa5ef Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Wed, 30 Sep 2026 18:57:30 -0400 Subject: feat: blackjack and baccarat rules felt added, casino layout simplified --- internal/game/render_blackjack_felt_test.go | 84 +++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 internal/game/render_blackjack_felt_test.go (limited to 'internal/game/render_blackjack_felt_test.go') diff --git a/internal/game/render_blackjack_felt_test.go b/internal/game/render_blackjack_felt_test.go new file mode 100644 index 0000000..a67251a --- /dev/null +++ b/internal/game/render_blackjack_felt_test.go @@ -0,0 +1,84 @@ +package game + +import ( + "strings" + "testing" + + "thehouseoficarus/internal/casino" + "thehouseoficarus/internal/color" +) + +func TestBlackjackRulesFeltGeometryAndPhrases(t *testing.T) { + cfg := casino.TableConfig{ID: "midnight", Game: casino.GameBlackjack, MinBet: 100, MaxBet: 10000, MaxPlayers: 6, BettingTicks: 18, + Blackjack: casino.BlackjackConfig{ActionTicks: 15, DoubleSplitBlackjack: &[]bool{false}[0]}} + for _, unicode := range []bool{false, true} { + art := renderBlackjackRulesFelt(cfg, "none", unicode) + rows := strings.Split(strings.TrimSuffix(art, "\n"), "\n") + if len(rows) != 21 { + t.Fatalf("felt height %d", len(rows)) + } + for _, row := range rows { + if color.VisibleLen(row) != 77 { + t.Fatalf("misaligned (%d): %s", color.VisibleLen(row), row) + } + } + for _, want := range []string{ + "BLACKJACK PAYS 3 TO 2", "DEALER MUST STAND ON 17 AND DRAW TO 16", "INSURANCE PAYS 2 TO 1", + "THE SHOE", "PAYS", "DOUBLE & SPLIT", "HOUSE", + "DECKS", "SHUFFLE AT", "25% REMAINING", "RESHUFFLE MID-ROUND", "HOLE CARD", + "ORDINARY WIN", "SPLIT+DOUBLE 21", "DOUBLE", "ANY TWO, ONE CARD", "MAX HANDS", "ONE CARD, NO HITS", + "SURRENDER", "NOT OFFERED", "100-10000 CREDITS", "18 TICKS", "15 TICKS", + } { + if !strings.Contains(art, want) { + t.Fatalf("missing %q in unicode=%v felt", want, unicode) + } + } + // The raw dump's phrases and fixed engine rules must not leak onto the felt. + for _, stale := range []string{"Number of decks", "Dealer hole card / peek:", "FRACTIONS", "BJ CHECK", "HOUSE BLACKJACK"} { + if strings.Contains(art, stale) { + t.Fatalf("stale prose %q on felt", stale) + } + } + } +} + +func TestBlackjackRulesFeltFollowsConfig(t *testing.T) { + yes, no := true, false + cfg := casino.TableConfig{ID: "high", Game: casino.GameBlackjack, MinBet: 5, MaxBet: 500, + Blackjack: casino.BlackjackConfig{Decks: 1, BlackjackPayout: 1.2, DealerHitSoft17: &yes, Surrender: &yes, + ShuffleAt: 0.5, MaxSplitHands: 2, DoubleAfterSplit: &no, ResplitAces: &yes, SplitUnlikeTenValues: &no, + DoubleSplitBlackjack: &no, InsurancePayout: 2}} + art := renderBlackjackRulesFelt(cfg, "none", true) + for _, want := range []string{"BLACKJACK PAYS 6 TO 5", "DEALER HITS SOFT 17", "LATE, TWO CARDS", "50% REMAINING", + "AFTER SPLIT NO", "RESPLIT ACES YES", "PAIRS IDENTICAL RANKS", + "SPLIT+DOUBLE 21 1:1", "5-500 CREDITS"} { + if !strings.Contains(art, want) { + t.Fatalf("missing %q: %s", want, art) + } + } +} + +func TestBlackjackJoinPrintsFeltNotProse(t *testing.T) { + g := &Game{Casino: casino.NewManager(1)} + g.Casino.Join(1, casino.TableConfig{ID: "bj", Game: casino.GameBlackjack, BettingTicks: 2}, "alice") + events := g.Casino.Join(1, casino.TableConfig{ID: "bj", Game: casino.GameBlackjack, BettingTicks: 2}, "bob") + found := false + for _, e := range events { + if view, ok := e.Snapshot.(*casino.BlackjackRulesView); ok { + found = true + art := renderBlackjackRulesFelt(view.Config, "none", true) + if !strings.Contains(art, "BLACKJACK PAYS 3 TO 2") || !strings.Contains(art, "DECKS") { + t.Fatal("rules felt missing phrases", art) + } + if e.Message != "" { + t.Fatal("rules event carries prose", e.Message) + } + } + if strings.Contains(e.Message+e.PrivateMessage, "Number of decks") { + t.Fatal("raw rules prose leaked onto join", e) + } + } + if !found { + t.Fatal("join did not attach the rules felt") + } +} -- cgit v1.2.3