diff options
| author | historia <[not public]> | 2026-09-29 19:48:32 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-29 19:48:32 -0400 |
| commit | ffc7b582e9c14fdc2e579b9239b5ff7ad9a0d90a (patch) | |
| tree | e286210755094b41025f2947e412e05e59ec2ac9 /internal/game/render_roulette_tracker_test.go | |
| parent | 624626b9a10ae47cf7d5f6e91765570ed759bd74 (diff) | |
| download | thehouseoficarus-ffc7b582e9c14fdc2e579b9239b5ff7ad9a0d90a.tar.gz | |
feat: roulette history tracker
Diffstat (limited to 'internal/game/render_roulette_tracker_test.go')
| -rw-r--r-- | internal/game/render_roulette_tracker_test.go | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/internal/game/render_roulette_tracker_test.go b/internal/game/render_roulette_tracker_test.go new file mode 100644 index 0000000..3bae930 --- /dev/null +++ b/internal/game/render_roulette_tracker_test.go @@ -0,0 +1,120 @@ +package game + +import ( + "strings" + "testing" + + "thehouseoficarus/internal/casino" + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +func TestRouletteTrackerFormats(t *testing.T) { + for _, mode := range []string{"none", "ansi", "xterm256"} { + for _, unicode := range []bool{false, true} { + for _, history := range [][]string{nil, {"0"}, {"1", "2", "00", "1", "36", "00", "36", "36", "2", "0"}} { + s := &casino.RouletteSnapshot{DoubleZero: true, History: history} + art := renderRouletteTracker(s, mode, unicode) + rows := strings.Split(art, "\n") + if len(rows) != 5 { + t.Fatalf("tracker has %d rows", len(rows)) + } + for _, row := range rows { + if color.VisibleLen(row) != 74 { + t.Errorf("tracker row width %d: %q", color.VisibleLen(row), plainRoulette(row)) + } + if !unicode { + for _, r := range plainRoulette(row) { + if r > 127 { + t.Errorf("non-ASCII rune %q", r) + } + } + } + } + if mode == "none" && strings.Contains(art, "\x1b") { + t.Fatal("color disabled but ANSI present") + } + if mode != "none" { + for _, bg := range []int{0, 1, 2} { + if !strings.Contains(art, color.BgCode(mode, bg)) { + t.Errorf("missing color %d", bg) + } + } + } + plain := plainRoulette(art) + if strings.Contains(plain, "History, not odds") { + t.Fatal("tracker should not carry the removed caption") + } + if len(history) == 0 { + for _, want := range []string{"last 0 spins", "awaiting the first spin", "Hot: --", "Cold: --", "Red --", "Black --"} { + if !strings.Contains(plain, want) { + t.Errorf("missing %q", want) + } + } + } + } + } + } +} + +func TestRouletteTrackerStatistics(t *testing.T) { + s := &casino.RouletteSnapshot{DoubleZero: true, History: []string{"1", "2", "00", "1", "36", "00", "36", "36", "2", "0"}} + plain := plainRoulette(renderRouletteTracker(s, "none", true)) + for _, want := range []string{"last 10 spins", "Red 50.0%", "Black 20.0%", "Zero 30.0%", "Hot: 36R (3) 00G (2) 1R (2)", "Cold: 3R (0) 4B (0) 5R (0)"} { + if !strings.Contains(strings.Join(strings.Fields(plain), " "), want) { + t.Errorf("missing %q:\n%s", want, plain) + } + } + // Only the newest eight, newest on the left, and 00 stays distinct from 0. + recent := strings.Split(plain, "\n")[1] + hits := racetrackNumbers(recent) + if got := strings.Join(hits, ","); got != "0,2,36,36,00,36,1,00" { + t.Errorf("recent hits: %s", got) + } + // Unhit pockets aren't called hot. Cold tie order includes 00 only on + // American wheels; it is not silently merged with zero. + s.History = []string{"0"} + if line := strings.Split(renderRouletteTracker(s, "none", false), "\n")[2]; !strings.Contains(strings.Join(strings.Fields(line), " "), "Hot: 0G (1) | Cold: 00G (0)") { + t.Errorf("zero ranking: %s", line) + } + s.DoubleZero = false + if strings.Contains(renderRouletteTracker(s, "none", false), "00G") { + t.Fatal("European tracker contains 00") + } +} + +func TestRouletteTrackerAboveBettingBoardsOnly(t *testing.T) { + for _, racetrack := range []bool{false, true} { + s := &casino.RouletteSnapshot{Racetrack: racetrack, Phase: "betting", History: []string{"17"}, Wheel: casino.WheelOrder(false)} + art := renderRouletteSnapshot(s, "none", true, 7) + tracker := renderRouletteTracker(s, "none", true) + if !strings.HasPrefix(art, tracker+"\n") { + t.Fatal("tracker not above board") + } + for _, phase := range []string{"playing", "result"} { + s.Phase = phase + s.Pockets = [5]string{"25", "17", "34", "6", "27"} + if strings.Contains(renderRouletteSnapshot(s, "none", true, 7), "History:") { + t.Fatalf("tracker appeared during %s", phase) + } + } + } +} + +func TestRouletteRulesUseLiveHistory(t *testing.T) { + g := &Game{Casino: casino.NewManager(7)} + p := player.New("alice") + p.RoomID, p.Credits = 1, 100 + p.Options = map[string]any{"color": "none", "unicode": true} + sess := &net.Session{Player: p} + cfg := casino.TableConfig{ID: "r", Game: casino.GameRoulette, BettingTicks: 1, Roulette: casino.RouletteConfig{SpinTicks: 1}} + g.Casino.Join(1, cfg, p.Name) + g.Casino.Bet(1, "r", p.Name, "red", 1, casinoWallet{g: g, p: p, saveCharacter: func(*player.Player) error { return nil }}) + g.Casino.Tick() + g.Casino.Tick() + art := g.renderCasinoRules(sess, cfg) + if !strings.Contains(art, "History: last 1 spin") || strings.Contains(art, "awaiting the first spin") { + t.Fatalf("rules did not show live history:\n%s", art) + } +} |
